From 2b183a913eb1bc3c4639face6eaa1e0b2a5be781 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 01/53] OTWO-7546 Added layout for home page --- app/assets/javascripts/dropdown_handler.js | 71 + app/assets/javascripts/mobile_menu.js | 86 + app/assets/javascripts/project_swimlanes.js | 30 + app/assets/javascripts/rotating_stats.js | 29 + app/assets/javascripts/theme_toggle.js | 75 + app/assets/stylesheets/base.sass | 11 +- app/assets/stylesheets/buttons.sass | 45 +- app/assets/stylesheets/dark_theme.sass | 150 ++ app/assets/stylesheets/footer.sass | 213 ++- app/assets/stylesheets/home.sass | 1655 +++++++++++++++-- app/assets/stylesheets/home_search.sass | 145 ++ app/assets/stylesheets/oh-styles.sass | 37 +- app/assets/stylesheets/page.sass | 893 +++++++-- app/assets/stylesheets/search-dingus.sass | 83 +- .../home/_compact_project_card.html.haml | 37 + app/views/home/_join_now_home.html.haml | 44 +- app/views/home/_top_contributors.html.haml | 40 + app/views/home/_top_lists.html.haml | 141 +- app/views/home/_user_journeys.html.haml | 272 +++ app/views/home/_whats_new.html.haml | 11 +- app/views/home/index.html.haml | 112 +- app/views/layouts/partials/_footer.html.haml | 81 +- app/views/layouts/partials/_mast.html.haml | 131 +- .../search_dingus/_search_bar.html.haml | 10 +- config/initializers/assets.rb | 3 +- config/locales/home.en.yml | 79 +- test/controllers/home_controller_test.rb | 8 +- test/controllers/licenses_controller_test.rb | 2 +- .../organizations_controller_test.rb | 2 +- test/controllers/projects_controller_test.rb | 2 +- 30 files changed, 3913 insertions(+), 585 deletions(-) create mode 100644 app/assets/javascripts/dropdown_handler.js create mode 100644 app/assets/javascripts/mobile_menu.js create mode 100644 app/assets/javascripts/project_swimlanes.js create mode 100644 app/assets/javascripts/rotating_stats.js create mode 100644 app/assets/javascripts/theme_toggle.js create mode 100644 app/assets/stylesheets/dark_theme.sass create mode 100644 app/assets/stylesheets/home_search.sass create mode 100644 app/views/home/_compact_project_card.html.haml create mode 100644 app/views/home/_top_contributors.html.haml create mode 100644 app/views/home/_user_journeys.html.haml diff --git a/app/assets/javascripts/dropdown_handler.js b/app/assets/javascripts/dropdown_handler.js new file mode 100644 index 000000000..f9aea4967 --- /dev/null +++ b/app/assets/javascripts/dropdown_handler.js @@ -0,0 +1,71 @@ +// Dropdown menu handler for logged user menu +(function($) { + 'use strict'; + + function initDropdown() { + var $dropdown = $('#logged_user_menu'); + + if ($dropdown.length === 0) { + return; // Dropdown not found (user not logged in) + } + + var $toggle = $dropdown.find('.dropdown-toggle'); + var $menu = $dropdown.find('.dropdown-menu'); + + // Remove any existing event handlers + $toggle.off('click.userDropdown'); + $(document).off('click.userDropdown keydown.userDropdown'); + $menu.find('a').off('click.userDropdown'); + + // Toggle dropdown on click + $toggle.on('click.userDropdown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var wasOpen = $dropdown.hasClass('open'); + + // Close all dropdowns first + $('.dropdown').removeClass('open'); + + // Toggle this dropdown + if (!wasOpen) { + $dropdown.addClass('open'); + } + + return false; + }); + + // Close dropdown when clicking outside + $(document).on('click.userDropdown', function(e) { + var $target = $(e.target); + if (!$dropdown.is($target) && $dropdown.has($target).length === 0) { + $dropdown.removeClass('open'); + } + }); + + // Close dropdown when clicking a menu item + $menu.find('a').on('click.userDropdown', function() { + setTimeout(function() { + $dropdown.removeClass('open'); + }, 150); + }); + + // Close dropdown on ESC key + $(document).on('keydown.userDropdown', function(e) { + if (e.keyCode === 27) { + $dropdown.removeClass('open'); + } + }); + } + + // Initialize when DOM is ready + $(document).ready(function() { + initDropdown(); + }); + + // Re-initialize on Turbolinks page change + $(document).on('page:change', function() { + initDropdown(); + }); + +})(jQuery); diff --git a/app/assets/javascripts/mobile_menu.js b/app/assets/javascripts/mobile_menu.js new file mode 100644 index 000000000..d57090864 --- /dev/null +++ b/app/assets/javascripts/mobile_menu.js @@ -0,0 +1,86 @@ +// Mobile Menu Toggle Functionality +var MobileMenu = { + init: function() { + console.log('MobileMenu: Initializing...'); + this.bindEvents(); + }, + + toggleMenu: function() { + console.log('MobileMenu: Toggle clicked!'); + var mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenu) { + if (mobileMenu.classList.contains('show')) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } else { + mobileMenu.classList.add('show'); + console.log('MobileMenu: Menu opened'); + } + } else { + console.log('MobileMenu: WARNING - Mobile menu element not found!'); + } + }, + + closeMenu: function() { + var mobileMenu = document.getElementById('mobile-menu'); + if (mobileMenu) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } + }, + + bindEvents: function() { + var self = this; + var toggleBtn = document.getElementById('mobile-menu-toggle'); + + console.log('MobileMenu: Toggle button found?', !!toggleBtn); + + if (toggleBtn) { + toggleBtn.onclick = function(e) { + e.preventDefault(); + self.toggleMenu(); + return false; + }; + console.log('MobileMenu: Click handler attached'); + } else { + console.log('MobileMenu: WARNING - Mobile menu toggle button not found!'); + } + + // Close menu when clicking on a link + var mobileMenuLinks = document.querySelectorAll('.mobile-menu-items a, .mobile-menu-user a, .mobile-menu-signin a'); + console.log('MobileMenu: Found', mobileMenuLinks.length, 'menu links'); + + for (var i = 0; i < mobileMenuLinks.length; i++) { + mobileMenuLinks[i].onclick = function() { + self.closeMenu(); + }; + } + + // Close menu when window is resized to desktop size + window.addEventListener('resize', function() { + if (window.innerWidth >= 1024) { + self.closeMenu(); + } + }); + } +}; + +// Initialize on DOM ready +if (typeof $ !== 'undefined') { + $(document).on('page:change', function() { + console.log('MobileMenu: page:change event fired'); + MobileMenu.init(); + }); + + $(document).ready(function() { + console.log('MobileMenu: document.ready event fired'); + MobileMenu.init(); + }); +} else { + // Fallback if jQuery is not available + document.addEventListener('DOMContentLoaded', function() { + console.log('MobileMenu: DOMContentLoaded event fired'); + MobileMenu.init(); + }); +} diff --git a/app/assets/javascripts/project_swimlanes.js b/app/assets/javascripts/project_swimlanes.js new file mode 100644 index 000000000..270f4e96f --- /dev/null +++ b/app/assets/javascripts/project_swimlanes.js @@ -0,0 +1,30 @@ +// Project Swimlanes - Show More/Less functionality +document.addEventListener('DOMContentLoaded', function() { + var buttons = document.querySelectorAll('.show_more_btn'); + + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener('click', function() { + var swimlane = this.getAttribute('data-swimlane'); + var swimlaneContent = document.querySelector('.swimlane_content[data-swimlane="' + swimlane + '"]'); + var hiddenCards = swimlaneContent.querySelectorAll('.hidden_card'); + var isExpanded = this.textContent.trim() === 'Show Less'; + + // Toggle visibility of hidden cards + for (var j = 0; j < hiddenCards.length; j++) { + if (isExpanded) { + hiddenCards[j].style.display = 'none'; + } else { + hiddenCards[j].style.display = 'block'; + } + console.log('Card', j, 'classes:', hiddenCards[j].className, 'display:', window.getComputedStyle(hiddenCards[j]).display); + } + + // Toggle button text + if (isExpanded) { + this.textContent = 'Show More'; + } else { + this.textContent = 'Show Less'; + } + }); + } +}); diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js new file mode 100644 index 000000000..39a368b7b --- /dev/null +++ b/app/assets/javascripts/rotating_stats.js @@ -0,0 +1,29 @@ +// Rotating Stats Animation for Homepage +document.addEventListener('DOMContentLoaded', function() { + var globalStats = document.getElementById('global_statistics'); + + if (!globalStats) return; + + var statElements = globalStats.querySelectorAll('p'); + + if (statElements.length === 0) return; + + var currentIndex = 0; + + // Show first stat initially + statElements[0].classList.remove('hide'); + + // Rotate stats every 2 seconds + setInterval(function() { + // Hide current stat + statElements[currentIndex].classList.add('hide'); + + // Move to next stat + currentIndex = (currentIndex + 1) % statElements.length; + + // Show next stat after a brief delay + setTimeout(function() { + statElements[currentIndex].classList.remove('hide'); + }, 300); + }, 2000); +}); diff --git a/app/assets/javascripts/theme_toggle.js b/app/assets/javascripts/theme_toggle.js new file mode 100644 index 000000000..44c98daaf --- /dev/null +++ b/app/assets/javascripts/theme_toggle.js @@ -0,0 +1,75 @@ +// Theme Toggle Functionality +var ThemeToggle = { + init: function() { + console.log('ThemeToggle: Initializing...'); + var savedTheme = this.getSavedTheme(); + console.log('ThemeToggle: Saved theme is', savedTheme); + this.applyTheme(savedTheme); + this.bindEvents(); + }, + + getSavedTheme: function() { + try { + return localStorage.getItem('theme') || 'light'; + } catch (e) { + return 'light'; + } + }, + + applyTheme: function(theme) { + var html = document.documentElement; + var moonIcon = document.getElementById('theme-icon-moon'); + var sunIcon = document.getElementById('theme-icon-sun'); + + if (theme === 'dark') { + html.classList.add('dark'); + if (moonIcon) moonIcon.classList.add('hidden'); + if (sunIcon) sunIcon.classList.remove('hidden'); + } else { + html.classList.remove('dark'); + if (moonIcon) moonIcon.classList.remove('hidden'); + if (sunIcon) sunIcon.classList.add('hidden'); + } + + try { + localStorage.setItem('theme', theme); + } catch (e) { + console.log('Could not save theme preference'); + } + }, + + toggleTheme: function() { + var currentTheme = this.getSavedTheme(); + var newTheme = currentTheme === 'light' ? 'dark' : 'light'; + this.applyTheme(newTheme); + }, + + bindEvents: function() { + var self = this; + var themeToggleBtn = document.getElementById('theme-toggle'); + + console.log('ThemeToggle: Theme toggle button found?', !!themeToggleBtn); + + if (themeToggleBtn) { + themeToggleBtn.onclick = function(e) { + e.preventDefault(); + console.log('ThemeToggle: Toggle clicked!'); + self.toggleTheme(); + return false; + }; + console.log('ThemeToggle: Click handler attached'); + } else { + console.log('ThemeToggle: WARNING - Theme toggle button not found!'); + } + } +}; + +// Initialize on DOM ready +$(document).on('page:change', function() { + ThemeToggle.init(); +}); + +// Also initialize immediately if DOM is already loaded +$(document).ready(function() { + ThemeToggle.init(); +}); diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 2e50269cc..43c4cbed2 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -2,13 +2,13 @@ // base mixins =sans_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =serif_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =arial_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =lucida_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =small_font font-size: .91667em =tiny_font @@ -21,10 +21,11 @@ #commits_summary_page, #mini_account_row [class^="icon-"], [class*=" icon-"] - font-family: Roboto !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important body text-rendering: optimizeLegibility + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" a @include site-link-color diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 276577616..8baec979b 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -90,14 +90,49 @@ button.btn:active border-color: #005481 .btn-refresh - @include secondary-button-colors + color: white !important + background-color: #5A2A82 !important + border-color: #5A2A82 !important + border-radius: 0 12px 12px 0 + margin-left: -1px + padding: 10px 18px + min-width: 48px + transition: all 0.15s ease-in-out + &:hover + background-color: rgba(90, 42, 130, 0.9) !important + border-color: #5A2A82 !important + transform: none + &:active + background-color: #4A1A72 !important + border-color: #4A1A72 !important &.no-hover:hover - background-color: #0082C6 !important + background-color: #5A2A82 !important &.active, &.no-border.active - background-color: #66B7E8 !important - border-color: #66B7E8 + background-color: #4A1A72 !important + border-color: #4A1A72 &.disabled, &[disabled] - background-color: #66CEF6 !important + background-color: #8A5AAA !important + border-color: #8A5AAA !important + opacity: 0.6 + cursor: not-allowed + i + font-size: 16px + line-height: 1 + + // Dark mode support + .dark & + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: #ffb91a !important + &:active + background-color: #e6a617 !important + border-color: #e6a617 !important + &.active, &.no-border.active + background-color: #e6a617 !important + border-color: #e6a617 .btn-success @include join-now-button-colors diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass new file mode 100644 index 000000000..9a7f3d844 --- /dev/null +++ b/app/assets/stylesheets/dark_theme.sass @@ -0,0 +1,150 @@ +// Dark Theme Styles +// Applied when .dark class is added to html element + +html.dark + // Body and background colors - matches home page sections + body + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Page container + #page, .container#page + background-color: #1D0631 !important + + // Header stays with purple gradient (no change needed) + // Footer stays with purple gradient (no change needed) + + // Content areas + #page-contents, #page_contents + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Cards and wells + .well + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Tables + table + background-color: #1D0631 + color: #e2e8f0 + td, th + border-color: #4b5563 !important + color: #e2e8f0 !important + + // Links + a + color: #60a5fa !important + &:hover + color: #93c5fd !important + + // Buttons (keep primary yellow button, update others) + .btn + &:not(.btn-primary):not(.btn-success) + background-color: #334155 !important + color: #e2e8f0 !important + &:hover + background-color: #475569 !important + + // Forms + input, textarea, select + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + &::placeholder + color: #94a3b8 !important + + // Billboard/Showcase + .billboard + background-color: #1D0631 !important + color: #e2e8f0 !important + + .showcase + background-color: #1D0631 !important + + // Project container + #project_container + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Headings + h1, h2, h3, h4, h5, h6 + color: #f1f5f9 !important + + // Alerts + .alert + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Dropdown menus + .dropdown-menu + background-color: #1D0631 !important + border-color: #4b5563 !important + li a + color: #e2e8f0 !important + &:hover + background-color: rgba(90, 42, 130, 0.5) !important + + // Home page sections + .top_ten, .top_ten.middle, .top_ten.last + background-color: #1D0631 !important + color: #e2e8f0 !important + + .home_page_row .col-md-4 + background-color: #1D0631 !important + + // Navigation menu - keep white text on purple gradient + .navbar, #navbar-inner + .new_main_menu li a + color: white !important + + // Search inputs + .for_search_all_code + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Badges + .badge + background-color: rgba(90, 42, 130, 0.5) !important + color: #e2e8f0 !important + + // Panels + .panel + background-color: #1D0631 !important + border-color: #4b5563 !important + .panel-heading + background-color: rgba(90, 42, 130, 0.5) !important + color: #e2e8f0 !important + .panel-body + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Code blocks + pre, code + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Borders + .mezzo + border-top-color: #4b5563 !important + + .right_border + border-right-color: #4b5563 !important + + // Text colors + .signature_color + color: #60a5fa !important + + // Keep error colors visible + .error + color: #ef4444 !important + + .bad + color: #f87171 !important + + .good + color: #4ade80 !important diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index dccca4602..61f45a71a 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -1,81 +1,154 @@ footer - @include footer-colors + // Figma design: purple gradient background + position: relative + overflow: hidden + background: linear-gradient(to right, #1D0631, #5A2A82) !important + color: white !important + margin: 0 + padding: 0 + width: 100% + max-width: 100% + // Matches Figma: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 + .footer-container + position: relative + max-width: 1280px + margin: 0 auto + padding: 24px 16px + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 48px 32px - margin: 20px 0 0 0 - min-height: 250px - padding-top: 10px - width: 960px - - @media (min-width: 320px) and (max-width: 480px) - width: auto - min-height: 194px !important - padding-top: 0px !important - margin: 0px 0 0 0 !important - .footer-left - margin-left: 5px !important - margin-top: 20px !important - min-height: 150px !important - .footer-mid - line-height: 15px - margin-left: 30px !important - font-size: 8px !important - width: 32% !important - .footer-right - font-size: 8px !important - padding-right: 2px !important - width: 31% !important - .footer-bottom - margin-left: 0px !important - margin-top: 0px !important - @media (min-width: 540px) and (max-width: 1024px) - width: auto + // Matches Figma: grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8 md:gap-12 + .footer-grid + display: grid + grid-template-columns: 1fr + gap: 24px + margin-bottom: 24px + @media (min-width: 640px) + gap: 32px + margin-bottom: 32px + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 48px + margin-bottom: 48px + // Matches Figma: h-8 sm:h-10 .footer-left - float: left - margin-top: 20px - margin-left: 30px - width: 15% + display: flex + align-items: flex-start + justify-content: center + @media (min-width: 768px) + justify-content: flex-start a .logo_img - width: 100% - height: 100% + width: auto + height: 32px + object-fit: contain + @media (min-width: 640px) + height: 40px - .footer-mid - float: left - margin-top: 20px - margin-left: 120px - margin-right: 40px - width: 20% - font-size: 12px - a - @include footer-colors - padding: 4px 4px - - .footer-right - float: left - margin-top: 20px - margin-bottom: 20px - width: 30% - font-size: 12px - padding-right: 60px - text-align: left + // Matches Figma: text-xs sm:text-sm for headers and links + .footer-mid, .footer-right + h3 + font-size: 12px + font-weight: 600 + color: white !important + margin-bottom: 8px + text-transform: uppercase + letter-spacing: 0.05em + @media (min-width: 640px) + font-size: 14px + margin-bottom: 12px + @media (min-width: 768px) + margin-bottom: 16px + ul + list-style: none + margin: 0 + padding: 0 + li + margin-bottom: 6px + @media (min-width: 640px) + margin-bottom: 8px + @media (min-width: 768px) + margin-bottom: 10px + p + margin: 0 0 6px 0 + @media (min-width: 640px) + margin: 0 0 8px 0 + @media (min-width: 768px) + margin: 0 0 10px 0 a - @include footer-colors - padding: 4px 6px + color: #d1d5db !important + text-decoration: none + font-size: 12px + font-weight: 400 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px + &:hover + color: white !important + // Matches Figma: py-3 sm:py-4 md:py-6 .footer-bottom - @include footer-copyright-colors - - float: left - margin-top: 10px - margin-left: 30px - margin-bottom: 2em - bottom: 0 - a - @include footer-copyright-colors + border-top: 1px solid rgba(255, 255, 255, 0.1) + padding: 12px 0 + @media (min-width: 640px) + padding: 16px 0 + @media (min-width: 768px) + padding: 24px 0 + .footer-bottom-content + display: flex + flex-direction: column + align-items: center + justify-content: space-between + gap: 12px + @media (min-width: 640px) + flex-direction: row + // Matches Figma: text-[10px] sm:text-xs + .copyright + color: #9ca3af !important + font-size: 10px + order: 2 + @media (min-width: 640px) + font-size: 12px + order: 1 + .follow-us + display: flex + align-items: center + gap: 8px + order: 1 + @media (min-width: 640px) + order: 2 + // Matches Figma: text-xs sm:text-sm + span + color: #d1d5db !important + font-size: 12px + font-weight: 500 + @media (min-width: 640px) + font-size: 14px + // Matches Figma: w-8 h-8 + a + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + transition: background-color 0.3s ease + &:hover + background-color: rgba(255, 255, 255, 0.2) + i + color: #d1d5db + font-size: 16px + transition: color 0.3s ease + &:hover i + color: white footer.fluid_footer width: auto + background: linear-gradient(to right, #1D0631, #5A2A82) !important .footer-left width: 12% ul @@ -83,16 +156,22 @@ footer.fluid_footer a padding: 1rem 2rem font-weight: bold + color: rgba(209, 213, 219, 1) !important + &:hover + color: white !important .footer-mid width: 25% text-align: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-right width: 40% float: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-bottom float: left margin-botton: 2em !important + color: rgba(156, 163, 175, 1) !important diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4c18ddd8c..4978e69e3 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,159 +1,138 @@ -.showcase - padding-top: 20px - padding-bottom: 30px - width: 980px - margin-left: -15px - padding-left: 5px - padding-right: 5px -@media only all and (min-width: 320px) and (max-width: 480px) - .showcase - width: auto - margin-left: 0px - .billboard - height: 185px !important - -.billboard - @include billboard-background-color - color: white - height: 160px - padding-top: 30px - padding-bottom: 30px - text-align: center +// ======================================== +// Hero Section - Figma Design System +// ======================================== +// Main container with consistent 1280px max-width throughout +// Isolated from global body font-size: 1.3rem override + +.hero_section + padding: 0 16px 40px + background: #fff !important + transition: background 0.3s ease + box-sizing: border-box + @media (min-width: 640px) + padding: 0 24px 40px + @media (min-width: 1024px) + padding: 0 32px 40px + // Dark mode support + html.dark & + background: #1D0631 !important + // Ensure all child elements use border-box + *, *::before, *::after + box-sizing: border-box + +// Hero Container - Controls all child widths consistently (1280px) +.hero_container + max-width: 1280px + margin: 0 auto width: 100% - z-index: 2 - .billboard_search - // margin-top: 80px - .discover - padding: 0 - .discover_msg - font-size: 2.1em !important - font-weight: 400!important - margin-top: -10px + box-sizing: border-box + +// Hero Header - Figma Specs +.hero_header + text-align: center + margin-bottom: 24px + padding: 0 + @media (min-width: 768px) margin-bottom: 24px - h1 - line-height: 1.2em !important -@media only all and (min-width: 320px) and (max-width: 480px) - .billboard - .discover_msg - font-size: 13px !important - #global_statistics - p - font-size: 10px !important -@media (min-width: 540px) and (max-width: 1024px) - .showcase - width: auto - margin-left: 0px -.global_stats - margin-top: 25px -#icon_text - font-size: 1.6em - color: #646E81 - margin-left: -27px - cursor: pointer + +.hero_title + font-size: 1.875rem + font-weight: 700 + color: #5A2A82 + margin-bottom: 8px + line-height: 2.25rem + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 2.25rem + line-height: 2.5rem + @media (min-width: 768px) + font-size: 48px + line-height: 1 + // Dark mode support + html.dark & + color: #ffffff + +.hero_subtitle + font-size: 0.875rem + color: #6b7280 + margin: 0 auto + max-width: 672px + line-height: 1.25rem + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 16px + line-height: 1.5rem + // Dark mode support + html.dark & + color: #9ca3af + +// Search Container - Narrower for focused search UX (768px) +.search_container + max-width: 768px + margin: 0 auto 20px + width: 100% + box-sizing: border-box + +// Rotating Stats - Figma Animation +.rotating_stats + text-align: center + margin-top: 12px + margin-bottom: 24px + height: 20px #global_statistics - padding-top: 1px - p - margin-top: -20px - text-align: center - font-size: 14px -.join_now - @include join-now-header-colors - padding-bottom: 5px - padding-left: 12px -.join_now_home - padding-top: 10px - font-size: 1.4em p - font-size: 14px !important - .join_now_color - @include join-now-section-colors - li - font-size: 0.5em !important - height: 30px - width: 30px -.home_page_row - .whats_new - margin-right: -27px !important - .col-md-8 - h2 - font-weight: 500 !important - margin-right: -40px !important - margin-left: 30px !important - text-indent: -10px !important - .col-md-4 - @include site-well-color - padding-bottom: 20px - h2 - font-weight: 500 !important - margin-left: 5px !important - text-indent: -10px !important - -@media only all and (min-width: 320px) and (max-width: 480px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important - .for_search_all_code - width: auto !important - .join_now_home - padding-top: 5px font-size: 12px - li - height: 10px - width: 10px -@media (min-width: 540px) and (max-width: 1024px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important -.btn.btn_join_now - @include join-now-button-colors - font-family: 'Roboto' !important - font-size: 22px !important - font-weight: bold !important - margin-left: 60px !important - width: 161px !important -::-webkit-input-placeholder - @include site-placeholder-color -\:-moz-placeholder - @include site-placeholder-color - opacity: 1 -::-moz-placeholder - @include site-placeholder-color - opacity: 1 -\:-ms-input-placeholder - @include site-placeholder-color - -input:focus::-webkit-input-placeholder - color: transparent -input:focus::-moz-placeholder - color: transparent -input:focus::-ms-input-placeholder - color: transparent -@media only all and (min-width: 320px) and (max-width: 480px) - .btn.btn_join_now - margin-left: 0px !important - width: 135px !important - font-size: 15px !important + font-weight: 500 + background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) + -webkit-background-clip: text + -webkit-text-fill-color: transparent + background-clip: text + margin: 0 + line-height: 1.5 + transition: opacity 0.3s ease + &.hide + opacity: 0 + display: block + &:not(.hide) + opacity: 1 + display: block + // Dark mode support + html.dark & + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) !important + -webkit-background-clip: text !important + -webkit-text-fill-color: transparent !important + background-clip: text !important +// Old join_now and home_page_row styles removed - using new card-based design +// Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px - margin-left: -14px + margin-left: 0 + padding: 32px 16px + width: 100% + background: #f9fafb !important + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + // Dark mode support + html.dark & + background: #1D0631 !important .top_ten_main padding-left: 40px !important padding-bottom: 6px !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" .top_ten_row padding-left: 9px !important padding-bottom: 15px + transition: all 0.3s ease + border-radius: 8px + margin-bottom: 12px + &:hover + background: rgba(90, 42, 130, 0.05) + transform: translateX(4px) .top_ten_bar line-height: 0.6em width: 30% @@ -162,11 +141,31 @@ input:focus::-ms-input-placeholder .top_ten_label line-height: 1.5em padding-left: 8px - color: #555 + color: #6b7280 + font-size: 12px + font-weight: 500 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + @media (min-width: 768px) + font-size: 14px + // Dark mode support + html.dark & + color: #9ca3af .top_ten h2 - font-weight: 500 !important + font-weight: 600 !important font-size: 16px !important + color: #111827 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode support + html.dark & + color: #ffffff .top_ten.middle @include site-well-color .top_ten.last @@ -174,8 +173,8 @@ input:focus::-ms-input-placeholder margin-right: -25px !important .top_ten_icon img - background-color: #EEE - color: #000 + background-color: #f3f4f6 + color: #111827 font-size: 4px line-height: 32px width: 32px @@ -184,37 +183,46 @@ input:focus::-ms-input-placeholder float: left margin-bottom: 0 margin-top: 0 - margin-right: 2px + margin-right: 8px + border-radius: 8px + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) -webkit-filter:grayscale(1) filter: grayscale(1) - opacity: .75 + opacity: .85 + transition: all 0.3s ease + // Dark mode support + html.dark & + background-color: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + &:hover img + opacity: 1 + transform: scale(1.05) a.top_ten_icon p opacity: .75 background-color: #999 -.for_search_all_code - @include site-placeholder-color - height: 23px !important - // margin-top: -108px - width: 413px +// Old .for_search_all_code removed - using new .search_input instead .top_ten_link a border: 0 -.icon_search - font-size: 14px - font-weight: 300 - margin-top: -37px - margin-left: -71px - text-align: center -.wh_new - @include whats-new-header-colors - width: 100% - padding-bottom: 5px -.common - padding-left: 6px - padding-bottom: 3px - font-size: 1.4em - font-weight: normal - color: #000000 + color: #5A2A82 + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 15px + @media (min-width: 768px) + font-size: 16px + &:hover + color: #1D0631 + text-decoration: none + // Dark mode support + html.dark & + color: #ffffff + &:hover + color: #ffb91a +// Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors .most_active_projects @@ -239,4 +247,1315 @@ button #no-background background-color: white !important +// Feature Cards Section - Figma: 1024px width +.features_section + max-width: 1024px + margin: 0 auto 24px + box-sizing: border-box + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 20px + +.feature_card + text-align: center + margin-bottom: 16px + @media (min-width: 768px) + margin-bottom: 0 + +.feature_icon + width: 40px + height: 40px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin: 0 auto 12px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.feature_icon_yellow + background: linear-gradient(135deg, #facc15 0%, #f97316 100%) + +.feature_icon_blue + background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) + +.feature_icon_green + background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%) + +.feature_title + font-size: 12px !important + font-weight: 600 + color: #111827 + margin-bottom: 4px + line-height: 1.3 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #ffffff + +.feature_description + font-size: 10px !important + color: #6b7280 + line-height: 1.625 + padding: 0 8px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af + +// Quick Stats Bar - Compact and centered (768px) +.quick_stats_bar + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + max-width: 768px + margin: 0 auto + padding: 16px + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) + backdrop-filter: blur(4px) + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.6) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + box-sizing: border-box + @media (min-width: 768px) + display: flex + align-items: center + justify-content: center + gap: 24px + padding: 12px 24px + // Dark mode support - Yellow background with purple text + html.dark & + background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) + border-color: #ffb91a + +.stat_item + text-align: center + +.stat_value + font-size: 16px + font-weight: 700 + color: #5A2A82 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px + @media (min-width: 768px) + font-size: 20px + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_label + font-size: 8px + color: #6b7280 + text-transform: uppercase + font-weight: 500 + letter-spacing: 0.05em + margin-top: 2px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 9px + @media (min-width: 768px) + font-size: 10px + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_divider + display: none + transition: background 0.3s ease + @media (min-width: 768px) + display: block + width: 1px + height: 32px + background: #9ca3af + opacity: 1 + // Dark mode - Purple divider on yellow background + html.dark & + @media (min-width: 768px) + background: #5A2A82 + opacity: 0.3 + +// Content Section +.content_section + padding: 32px 0 + background: white !important + transition: background 0.3s ease + // Dark mode support + html.dark & + background: #1D0631 !important + + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + +// Content Grid - matches Figma design: grid md:grid-cols-[35%_65%] gap-6 +.content_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: 35% 65% + +.contributors_column + width: 100% + +.whats_new_column + width: 100% + display: block + +.whats_new_join_stack + display: flex + flex-direction: column + gap: 24px + +// Top Contributors Card - Matches ContributorJourney.tsx +.top_contributors_card + position: relative + padding: 3px + border-radius: 16px + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 24px + overflow: hidden + transition: box-shadow 0.3s + &:hover + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + html.dark & + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + + > * + background: white + border-radius: 16px + overflow: hidden + html.dark & + background: #1D0631 + +// Card Header +.top_contributors_card .card_header + display: flex + align-items: flex-start + gap: 16px + padding: 24px 24px 16px 24px + background: white + html.dark & + background: #1D0631 + +.top_contributors_card .header_icon + display: flex + align-items: center + justify-content: center + width: 40px + height: 40px + border-radius: 12px + background: rgba(90, 42, 130, 0.1) + flex-shrink: 0 + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + @media (min-width: 640px) + width: 48px + height: 48px + html.dark & + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + + i + color: #5A2A82 + font-size: 20px + @media (min-width: 640px) + font-size: 24px + html.dark & + color: white + +.top_contributors_card .header_content + flex: 1 + +.top_contributors_card .card_title + font-size: 16px + font-weight: 700 + color: #5A2A82 + margin: 0 0 6px 0 + line-height: 1.4 + @media (min-width: 640px) + font-size: 18px + html.dark & + color: white + +.top_contributors_card .card_subtitle + font-size: 10px + color: rgba(90, 42, 130, 0.7) + margin: 0 + line-height: 1.5 + @media (min-width: 640px) + font-size: 12px + html.dark & + color: #9CA3AF + +// Contributors List +.top_contributors_card .contributors_list + padding: 16px + display: flex + flex-direction: column + gap: 8px + +.top_contributors_card .contributor_item + display: flex + align-items: center + gap: 12px + padding: 12px + background: linear-gradient(to right, #f9fafb 0%, #f1f5f9 100%) + border: 1px solid #e5e7eb + border-radius: 12px + transition: all 0.2s + text-decoration: none + cursor: pointer + + &:hover + background: linear-gradient(to right, #f5f3ff 0%, #ede9fe 100%) + border-color: #5A2A82 + + html.dark & + background: linear-gradient(to right, rgba(55, 65, 81, 0.5) 0%, rgba(71, 85, 105, 0.5) 100%) + border-color: #4B5563 + &:hover + background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) + border-color: #5A2A82 + +// Show only top 4 contributors on mobile +.top_contributors_card .contributor_item:nth-child(n+5) + @media (max-width: 767px) + display: none + +// Avatar +.top_contributors_card .contributor_avatar + flex-shrink: 0 + width: 40px + height: 40px + +.top_contributors_card .avatar_img, +.top_contributors_card .avatar_placeholder + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + display: block + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + border: 2px solid #9CA3AF + +// Contributor Info +.top_contributors_card .contributor_info + flex: 1 + min-width: 0 + display: flex + flex-direction: column + gap: 2px + +.top_contributors_card .contributor_name_row + display: flex + align-items: center + gap: 8px + margin-bottom: 2px + +.top_contributors_card .contributor_name + font-size: 14px + font-weight: 600 + color: #5A2A82 + line-height: 1.3 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + html.dark & + color: white + +.top_contributors_card .contributor_rank + font-size: 9px + font-weight: 400 + color: rgba(90, 42, 130, 0.6) + text-transform: uppercase + letter-spacing: 0.05em + white-space: nowrap + flex-shrink: 0 + html.dark & + color: #9CA3AF + +.top_contributors_card .contributor_stats + display: flex + align-items: center + gap: 12px + font-size: 10px + color: rgba(90, 42, 130, 0.7) + + html.dark & + color: #9CA3AF + + span + display: flex + align-items: center + gap: 4px + + i + flex-shrink: 0 + display: inline-flex + align-items: center + justify-content: center + font-size: 9px + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_commits, +.top_contributors_card .stat_projects + font-weight: 400 + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_separator + display: none + +// Arrow +.top_contributors_card .contributor_arrow + flex-shrink: 0 + color: rgba(90, 42, 130, 0.4) + display: flex + align-items: center + transition: color 0.2s + + i + font-size: 16px + + html.dark & + color: rgba(255, 255, 255, 0.3) + +.top_contributors_card .contributor_item:hover .contributor_arrow + color: #5A2A82 + html.dark & + color: white + +// View All Footer +.top_contributors_card .view_all_footer + padding: 16px + +.top_contributors_card .view_all_link + display: flex + width: 100% + align-items: center + justify-content: center + gap: 8px + padding: 12px + font-size: 14px + font-weight: 600 + color: #5A2A82 + text-decoration: none + border-top: 1px solid rgba(90, 42, 130, 0.2) + transition: color 0.2s + cursor: pointer + + span + border-bottom: 1px solid #5A2A82 + + &:hover + color: #1D0631 + span + border-bottom-color: #1D0631 + + i + font-size: 16px + + html.dark & + color: white !important + border-top-color: #ffb91a + border-top-width: 2px + border-radius: 12px + margin-top: 8px + span + border-bottom-color: white + &:hover + color: white !important + +// What's New Card - Figma Design +.whats_new_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + // Dark mode support + html.dark & + background: #1D0631 + +.card_header_section + padding: 16px 16px 12px 16px !important + background: white + @media (min-width: 640px) + padding: 24px 24px 16px 24px !important + // Dark mode support + html.dark & + background: #1D0631 + +.whats_new_title + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important + +.whats_new_subtitle + font-size: 12px !important + color: rgba(90, 42, 130, 0.7) !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.featured_image_section + padding: 0 16px 16px !important + display: block + @media (min-width: 640px) + padding: 0 24px 24px !important + +.featured_image_link + display: block + position: relative + border-radius: 12px + overflow: hidden + transition: transform 0.3s + &:hover + transform: scale(1.02) + .featured_image + transform: scale(1.05) + +.featured_image + width: 100% + height: auto + display: block + max-width: 100% + transition: transform 0.3s + +// Join Now Card - Figma Design (p-6 wrapper) +.join_now_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + padding: 24px !important + // Dark mode support + html.dark & + background: #1D0631 + +.join_header + display: flex + align-items: center + gap: 12px !important + margin-bottom: 16px !important + padding: 0 !important + +.join_icon + width: 40px + height: 40px + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.join_header_text + flex: 1 + +.join_title + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important + +.join_subtitle + font-size: 12px !important + color: #6b7280 !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.benefits_list + padding: 0 !important + margin-bottom: 24px !important + // Figma: space-y-3 (12px gap) + > * + * + margin-top: 12px !important + +.benefit_item + display: flex + align-items: flex-start + gap: 12px +.benefit_icon + width: 28px + height: 28px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + margin-top: 2px + @media (min-width: 640px) + width: 32px + height: 32px + +.benefit_icon_award + background: rgba(90, 42, 130, 0.1) + i + font-size: 14px + color: #5A2A82 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #5A2A82 + +.benefit_icon_trending + background: rgba(29, 6, 49, 0.1) + i + font-size: 14px + color: #1D0631 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #1D0631 + +.benefit_content + flex: 1 + +.benefit_title + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: white !important + +.benefit_description + font-size: 10px !important + color: #6b7280 !important + line-height: 1.625 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.join_cta + padding: 0 !important + +.btn_join_cta + width: 100% + padding: 12px 24px !important + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) !important + color: white !important + border-radius: 8px !important + font-weight: 600 !important + transition: all 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + display: flex + align-items: center + justify-content: center + gap: 8px + font-size: 14px !important + text-decoration: none !important + border: none + cursor: pointer + @media (min-width: 640px) + font-size: 16px !important + &:hover + background: linear-gradient(90deg, rgba(29, 6, 49, 0.9) 0%, rgba(90, 42, 130, 0.9) 100%) !important + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important + color: white !important + text-decoration: none !important + i + font-size: 14px + @media (min-width: 640px) + font-size: 16px + +// User Journeys Section +.user_journeys_section + background: #1D0631 + padding: 64px 0 + @media (max-width: 768px) + padding: 48px 0 + + // Matches Figma: max-w-7xl mx-auto + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + +.journeys_header + text-align: center + margin-bottom: 48px + +// Matches Figma: text-2xl sm:text-3xl +.journeys_title + font-size: 24px + font-weight: 700 !important + color: white + margin-bottom: 12px + @media (min-width: 640px) + font-size: 30px + +// Matches Figma: text-base sm:text-lg max-w-2xl +.journeys_subtitle + font-size: 16px + color: #d1d5db + max-width: 672px + margin: 0 auto + @media (min-width: 640px) + font-size: 18px + +// Mobile View - Collapsible +// Matches Figma: lg:hidden (shows below 768px, then desktop grid takes over) +.journeys_mobile + display: block + @media (min-width: 768px) + display: none + +.journey_card + position: relative + padding: 3px + border-radius: 12px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 16px + transition: all 0.3s + +// Matches Figma: rounded-xl p-4 +.journey_card_header + width: 100% + display: flex + align-items: center + gap: 16px + padding: 16px + text-align: left + transition: background 0.3s + background: #1D0631 + border: none + cursor: pointer + border-radius: 12px + &:hover + background: rgba(90, 42, 130, 0.2) + +.journey_header_icon + width: 40px + height: 40px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 20px + color: white + +.journey_header_title + flex: 1 + font-size: 14px + font-weight: 600 !important + color: white + overflow-wrap: break-word + +.journey_toggle_icon + flex-shrink: 0 + i + font-size: 20px + color: white + +// Matches Figma: px-4 pb-4 +.journey_card_content + display: none + padding: 0 16px 16px + background: #1D0631 + border-radius: 0 0 12px 12px + +.journey_card.expanded .journey_card_content + display: block + animation: slideDown 0.3s ease-out + +@keyframes slideDown + from + opacity: 0 + transform: translateY(-10px) + to + opacity: 1 + transform: translateY(0) + +.journey_description + font-size: 14px + color: #d1d5db + line-height: 1.5 + margin-bottom: 16px + overflow-wrap: break-word + +.journey_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +.steps_title + font-size: 12px + font-weight: 600 + color: #e5e7eb + text-transform: uppercase + letter-spacing: 0.05em + margin-bottom: 8px + +.steps_list + list-style: none + padding: 0 + margin: 0 + li + display: flex + align-items: flex-start + gap: 8px + font-size: 14px + color: #d1d5db + margin-bottom: 6px + &:last-child + margin-bottom: 0 + +.step_bullet + color: #ffb91a + font-weight: 700 + +// Desktop View - Grid +// Matches Figma: hidden lg:grid md:grid-cols-2 lg:grid-cols-3 gap-6 +.journeys_desktop + display: none + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 24px + min-width: 0 + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + +// Matches Figma: rounded-xl p-6 +.journey_grid_card + border-radius: 12px + padding: 24px + border: 1px solid rgba(90, 42, 130, 0.4) + background: #1D0631 + transition: all 0.3s + min-width: 0 + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) + border-color: #ffb91a + +.grid_card_icon + width: 56px + height: 56px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin-bottom: 16px + transition: transform 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 28px + color: white + +.journey_grid_card:hover .grid_card_icon + transform: scale(1.1) + +.grid_card_title + font-size: 17px + font-weight: 700 !important + color: white + margin-bottom: 8px + line-height: 1.2 + overflow-wrap: break-word + +.grid_card_description + font-size: 14px + color: #d1d5db + margin-bottom: 16px + line-height: 1.5 + overflow-wrap: break-word + +.grid_card_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +// Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 +.project_swimlanes_section + padding: 32px 16px + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + html.dark & + background: #1D0631 + +// Matches Figma: max-w-7xl mx-auto +.swimlanes_container + max-width: 1280px + margin: 0 auto + +// Matches Figma: grid lg:grid-cols-3 md:grid-cols-2 gap-6 +.swimlanes_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + +.swimlane_column + display: flex + flex-direction: column + +// Matches Figma: mb-4 +.swimlane_header + margin-bottom: 16px + +// Matches Figma: text-base sm:text-lg md:text-xl font-bold +.swimlane_title + font-size: 16px + font-weight: 700 !important + color: #111827 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px + @media (min-width: 768px) + font-size: 20px + html.dark & + color: #ffffff + +// Matches Figma: space-y-3 +.swimlane_content + display: flex + flex-direction: column + gap: 12px + +// Compact Project Card Styles +.compact_project_card + position: relative + padding: 3px + border-radius: 8px + overflow: hidden + transition: all 0.3s ease + background: transparent + cursor: pointer + &:hover + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + .compact_card_inner + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + +.compact_project_card .compact_card_inner + background: white + border-radius: 8px + padding: 12px + transition: all 0.3s ease + height: 100% + border: 1px solid #e5e7eb + @media (min-width: 640px) + padding: 16px + html.dark & + background: #1D0631 + border-color: #4b5563 + +.compact_project_card .compact_card_header + display: flex + align-items: flex-start + gap: 8px + margin-bottom: 8px + @media (min-width: 640px) + gap: 12px + +.compact_project_card .project_logo + flex-shrink: 0 + width: 32px + height: 32px + @media (min-width: 640px) + width: 40px + height: 40px + img, p + width: 100% !important + height: 100% !important + border-radius: 8px !important + object-fit: cover + border: 1px solid #e5e7eb !important + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important + html.dark & + border-color: #4b5563 !important + p + display: flex !important + align-items: center !important + justify-content: center !important + margin: 0 !important + font-size: 16px !important + font-weight: 600 !important + +.compact_project_card .project-icon-placeholder + width: 100% + height: 100% + display: flex + align-items: center + justify-content: center + background: #f3f4f6 + border-radius: 8px + font-size: 16px + font-weight: 600 + color: #6b7280 + border: 1px solid #e5e7eb + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: #9ca3af + +.compact_project_card .account-avatar + width: 100% + height: 100% + border-radius: 8px + object-fit: cover + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + html.dark & + border-color: #4b5563 + +.compact_project_card .project_info + flex: 1 + min-width: 0 + +.compact_project_card .project_name_row + display: flex + align-items: center + gap: 8px + +.compact_project_card .project_name + color: #111827 + font-size: 14px + font-weight: 600 + text-decoration: none + transition: color 0.3s ease + display: inline-block + line-height: 1.4 + @media (min-width: 640px) + font-size: 16px + &:hover + color: #5A2A82 + html.dark & + color: #ffffff !important + &:hover + color: #ffb91a !important + span + display: inline + +.compact_project_card .trending_icon + flex-shrink: 0 + i + color: #ff6b35 + font-size: 12px + @media (min-width: 640px) + font-size: 14px + +.compact_project_card .compact_card_description + margin-bottom: 12px + p + font-size: 12px + color: #6b7280 + line-height: 1.5 + margin: 0 + overflow: hidden + text-overflow: ellipsis + display: -webkit-box + -webkit-line-clamp: 2 + -webkit-box-orient: vertical + @media (min-width: 640px) + font-size: 14px + html.dark & + color: #9ca3af + +.compact_project_card .compact_card_footer + display: flex + align-items: center + justify-content: space-between + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_left + display: flex + align-items: center + gap: 12px + +.compact_project_card .contributor_info + display: flex + align-items: center + gap: 4px + color: #6b7280 + html.dark & + color: #9ca3af + i + font-size: 10px + @media (min-width: 640px) + font-size: 12px + span + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_right + display: flex + align-items: center + +.compact_project_card .language_badge + display: inline-flex + align-items: center + justify-content: center + padding: 2px 8px + border-radius: 9999px + font-size: 10px + font-weight: 500 + line-height: 1.5 + white-space: nowrap + text-align: center + min-width: 40px + background: rgba(255, 185, 26, 0.3) + color: #1D0631 + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 12px + html.dark & + background: #ffb91a + color: #000000 + +// Responsive visibility helpers +.desktop_only + display: none !important + @media (min-width: 768px) + display: block !important + +.mobile_only + display: block + @media (min-width: 768px) + display: none !important + +.mobile_card + width: 100% + +.hidden_card + width: 100% + margin-bottom: 16px + +// Show More Button +.show_more_btn + width: 100% + font-size: 14px + background: white + color: #5A2A82 + border: 2px solid #5A2A82 + padding: 12px 16px + border-radius: 8px + font-weight: 600 + cursor: pointer + transition: all 0.3s ease + margin-top: 8px + display: block + &:hover + background: #5A2A82 + color: white + html.dark & + background: transparent + color: white + border-color: white + &:hover + background: white + color: #5A2A82 + +// Pagination Controls +.pagination_controls + display: flex + align-items: center + justify-content: center + gap: 16px + margin-top: 16px + padding: 12px + +.pagination_btn + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background: white + border: 1px solid #e5e7eb + border-radius: 8px + cursor: pointer + transition: all 0.3s ease + color: #5A2A82 + &:hover:not([disabled]) + background: #5A2A82 + color: white + border-color: #5A2A82 + &[disabled] + opacity: 0.4 + cursor: not-allowed + i + font-size: 14px + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: white + &:hover:not([disabled]) + background: #5A2A82 + border-color: #5A2A82 + +.pagination_info + font-size: 14px + color: #6b7280 + font-weight: 500 + html.dark & + color: #9ca3af + .current_page + color: #5A2A82 + font-weight: 700 + html.dark & + color: #ffb91a + +.swimlane_project_item + display: none + &[data-page="1"] + display: block + +// View All Projects Button - Matches Figma: pt-4 pb-8 +.view_all_container + padding-top: 16px + padding-bottom: 32px + padding-left: 16px + padding-right: 16px + display: flex + justify-content: center + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-left: 32px + padding-right: 32px + html.dark & + background: #1D0631 + +// Matches Figma: flex items-center gap-2 px-6 py-3 bg-[#ffb91a] rounded-lg +.btn_view_all_projects + display: inline-flex + align-items: center + gap: 8px + padding: 12px 24px + background: #ffb91a + color: #000000 + border-radius: 8px + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + font-size: 16px + &:hover + background: rgba(255, 185, 26, 0.9) + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) + color: #000000 + html.dark & + color: #ffffff !important + &:hover + color: #ffffff !important + i + font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass new file mode 100644 index 000000000..b1c0d45e8 --- /dev/null +++ b/app/assets/stylesheets/home_search.sass @@ -0,0 +1,145 @@ +// ======================================== +// Home Page Search Bar - Figma Design +// ======================================== +// Dedicated styles for search input with higher specificity +// to override Bootstrap and other base styles + +// Hero Section Search Container - Nested under .hero_section +.hero_section + .search_form#search_form + margin-bottom: 0 + position: relative + width: 100% + + .search_input_wrapper + position: relative + width: 100% + display: block + + // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) + // Positioned to align with input text baseline + .search_icon + position: absolute + top: 50% + left: 8px + transform: translateY(-60%) + display: block + width: 14px + height: 14px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 10 + transition: color 0.3s ease + text-align: center + font-style: normal + @media (min-width: 640px) + left: 24px + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Ensure Font Awesome icon centers properly + &::before + display: block + + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 + input.search_input + width: 100% + padding: 10px 12px 10px 32px + font-size: 11px + font-weight: 400 + line-height: 1.5 + height: auto + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #ffffff + background-clip: padding-box + color: #111827 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + appearance: none + -webkit-appearance: none + -moz-appearance: none + vertical-align: middle + box-sizing: border-box + + // Placeholder styling - Figma specs + &::placeholder + color: #9ca3af + opacity: 1 + + // Hover state - Figma specs + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) + border-color: #d1d5db + + // Focus state - Figma specs + &:focus + border-color: #0E4B7A + outline: none + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + background: #ffffff + &::placeholder + color: #d1d5db + + // Desktop - Figma: sm:pl-14 sm:pr-4 sm:py-4 + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px + border-radius: 16px + + // Dark mode support + html.dark & + background: #1D0631 + color: #ffffff + border-color: #4b5563 + &::placeholder + color: #6b7280 + opacity: 1 + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1) + border-color: #6b7280 + &:focus + border-color: #2E8B9E + background: #1D0631 + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// ======================================== +// Bootstrap Override - Global Search Form +// ======================================== +// High specificity overrides for Bootstrap and other frameworks +// Ensures Figma padding is applied: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 +form#search_form + input[type="text"].search_input, + input.search_input + width: 100% !important + padding: 10px 12px 10px 32px !important + font-size: 11px !important + height: auto !important + border: 2px solid #e5e7eb !important + border-radius: 16px !important + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important + background: #ffffff !important + color: #111827 !important + box-sizing: border-box !important + @media (min-width: 640px) + padding: 16px 16px 16px 56px !important + font-size: 16px !important + &:focus + border-color: #0E4B7A !important + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important + outline: none !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff !important + border-color: #4b5563 !important + &:focus + border-color: #2E8B9E !important + background: #1D0631 !important + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index bda95eea6..db0833582 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -55,13 +55,16 @@ @mixin top-navbar-colors color: white - background-color: #211e1e !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important @mixin top-navbar-menu-colors - color: white + color: white !important + font-weight: 500 + transition: color 0.3s ease &:hover padding-bottom: 0px - color: color-gradient($SECONDARY_YELLOW, 60) + color: rgba(255, 255, 255, 0.7) !important + text-decoration: none @mixin twitter-background-color background-color: #5A2A82 @@ -119,15 +122,15 @@ border-left: 3px solid color-gradient($PRIMARY_DARK_GRAY, 100) @mixin footer-colors - color: color-gradient($PRIMARY_PURPLE, 120) !important - background-color: color-gradient($FOOTER_BACKGROUND, 0) !important + color: white !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important a &:hover - color: color-gradient($FOOTER_BACKGROUND, 0) + color: white !important border-radius: 0 @mixin footer-copyright-colors - color: color-gradient($LIGHT_GRAY, 100) !important + color: rgba(156, 163, 175, 1) !important //////////////////////////////////////////////////////////////////// // @@ -264,6 +267,26 @@ &.no-border:hover border-color: color-gradient($LIGHT_GRAY, 80) !important +@mixin purple-theme-button-colors + color: white + background-color: #5A2A82 !important + border-color: #5A2A82 !important + &:hover + background-color: #4A1A72 !important + border-color: #4A1A72 !important + &.no-border:hover + border-color: #4A1A72 !important + +@mixin search-input-colors + background-color: #ffffff + color: #202020 + border-color: #d1d5db + &:focus + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + &::placeholder + color: #9ca3af + @mixin join-now-button-colors color: white background-color: color-gradient($SECONDARY_FOREST_GREEN, 100) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cc813f4f1..cb5fd0334 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -1,21 +1,42 @@ @import "base" +// Mobile menu slide down animation +@keyframes slideDown + 0% + opacity: 0 + transform: translateY(-10px) + 100% + opacity: 1 + transform: translateY(0) + body - background-color: #211e1e !important - font-family: Roboto + background-color: white !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" font-size: 1.3rem !important margin: 0 + padding: 0 color: #000 + width: 100% + overflow-x: hidden -#page - width: 980px +.container#page, #page + width: 100% !important + max-width: 100% !important background-color: white - margin: 0 auto - padding: 10px 10px 0 10px + margin: 0 !important + padding: 0 !important header - background-color: white - margin: -10px -10px 0px -10px + // Figma design: gradient background for entire header + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-size: 16px + margin: 0 + padding: 0 + width: 100% + position: sticky + top: 0 + z-index: 1000 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -27,60 +48,94 @@ header overflow-x: hidden !important @media (min-width: 540px) and (max-width: 1024px) - #page - margin: 0 auto - padding: 10px 10px 0 10px - width: auto + #page, .container#page + margin: 0 !important + padding: 0 !important + width: 100% !important + max-width: 100% !important #nav-top-bar - margin: 11px 11px 0 11px + width: 100% + max-width: 1280px + margin: 0 auto + padding: 0 32px + display: flex + align-items: center .new_main_menu - list-style: none - margin: 0 - padding: 0 + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + gap: 18px !important li - display: inline-block - margin-right: 32px + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important a - @include top-navbar-menu-colors - font-size: 11pt - text-decoration: none + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: color 0.3s ease !important + white-space: nowrap !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + padding: 6px 0 !important + margin: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important #navbar-inner - @include top-navbar-colors - padding-top: 1px - margin-left: 4px - margin-right: 5px - height: 42px + // Navigation moved to main navbar - hide this section + display: none -@media (min-width: 320px) and (max-width: 340px) +@media (min-width: 320px) and (max-width: 480px) #nav-top-bar - margin: 11px 4px 0 0px !important + padding: 0 16px .new_main_menu + gap: 16px + .mobile-menu + .mobile-menu-content + padding: 0 8px + .mobile-menu-items li - margin-right: 2px !important a - font-size: 10px !important - -@media (min-width: 341px) and (max-width: 480px) + padding: 10px 12px + font-size: 13px + +@media (min-width: 481px) and (max-width: 768px) #nav-top-bar - margin: 11px 10px 0 11px !important - .new_main_menu - li - margin-right: 6px !important - a - font-size: 10px !important -#nav-top-bar .new_main_menu li a:hover - color: #fff - text-decoration: underline + padding: 0 24px + .new_main_menu + gap: 24px #page_contents + max-width: 100% + padding: 0 20px .separator-div @include site-separator-color #project_container background-color: white margin: 0 auto - padding: 10px 10px 0 10px + padding: 10px 20px 0 20px + width: 100% + max-width: 100% #page-contents width: auto #project_masthead @@ -99,132 +154,667 @@ header .quick_reference_heading, .nutshell_heading padding-left: 2rem -@media (min-width: 320px) and (max-width: 480px) +// sm: 640px+ +@media (min-width: 640px) .navbar - margin-right: 0px !important - .btn - font-size: 10px !important - .company-div - float: none !important - .navbar_large_text - font-size: 18px !important + .navbar-container + padding: 0 16px + gap: 12px + .logo-and-brand + gap: 6px + .logo-div + .logo_img + height: 28px + .company-div + .navbar_large_text + font-size: 16px + .separator-div + display: block + height: 24px + .actions-div + gap: 8px + .theme-toggle-btn + padding: 8px + .theme-icon + font-size: 20px + .mobile-menu-toggle + padding: 8px + i + font-size: 20px + .mobile-menu + .mobile-menu-content + padding: 0 12px + .mobile-menu-items + li + a + padding: 12px 16px + +// md: 768px+ +@media (min-width: 768px) + .navbar + .navbar-container + padding: 0 24px + gap: 16px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 32px + .separator-div + display: block + height: 26px + .logo-and-brand + gap: 10px + .mobile-menu + .mobile-menu-content + padding: 0 24px + .mobile-menu-items + li + a + padding: 14px 20px + font-size: 15px + +// lg: 1024px+ (Desktop) +@media (min-width: 1024px) + .navbar + min-height: 72px + .navbar-container + padding: 0 32px + min-height: 72px + .logo-and-brand + gap: 12px + .logo-div + .logo_img + height: 32px !important + .company-div + .navbar_large_text + font-size: 20px + .separator-div + height: 28px + .actions-div + gap: 16px + .nav-menu + display: flex !important + .new_main_menu + gap: 18px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 14px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + .desktop-actions + display: flex !important + .mobile-menu-toggle + display: none !important + .mobile-menu + display: none + +// xl: 1280px+ +@media (min-width: 1280px) + .navbar + min-height: 80px + .navbar-container + min-height: 80px + .logo-div + .logo_img + height: 48px + .company-div + .navbar_large_text + font-size: 24px + .nav-menu + .new_main_menu + gap: 28px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// 2xl: 1536px+ +@media (min-width: 1536px) + .navbar + min-height: 88px + .navbar-container + min-height: 88px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 30px + .nav-menu + .new_main_menu + gap: 36px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// Mobile/Tablet: < 1024px (show hamburger menu) +@media (max-width: 1023px) + .navbar + .nav-menu + display: none !important + .desktop-actions + display: none !important + .mobile-menu-toggle + display: flex !important .navbar + // Figma design: gradient background with purple theme + position: relative + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) border-width: 0 - margin-top: 15px - margin-right: 15px - margin-bottom: 5px !important - margin-left: 15px - padding: 0 0 5px 0 - min-height: 30px + border-bottom: 1px solid rgba(31, 41, 55, 0.8) + margin: 0 + padding: 0 + min-height: 64px + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2) + width: 100% + display: flex + align-items: flex-start + flex-wrap: wrap + overflow: visible + z-index: 1001 + // Override global button styles for navbar + .btn + border: none !important + border-width: 0 !important + box-shadow: none !important + &:active + top: 0 !important + left: 0 !important + .navbar-container + max-width: 100% + margin: 0 auto + padding: 0 16px + width: 100% + min-height: 64px + display: flex !important + flex-direction: row !important + flex-wrap: nowrap !important + align-items: center + justify-content: space-between + gap: 12px + position: relative + .hidden + display: none !important + position: absolute !important + width: 0 !important + height: 0 !important + overflow: hidden !important + .logo-and-brand + display: flex + align-items: center + gap: 8px + flex-shrink: 0 + min-width: 0 .logo-div - height: 35px - float: left + display: flex + align-items: center + flex-shrink: 0 .logo_link text-decoration: none - margin-top: 3px + display: flex + align-items: center .logo_img - height: 100% + height: 20px + width: auto + display: block + object-fit: contain .company-div - @include top-navigation-colors - float: left + color: white !important + display: flex + align-items: center + min-width: 0 .navbar_small_text font-size: 10px !important .navbar_large_text - font-size: 24px + color: white !important + font-weight: 700 + line-height: 1.25 + letter-spacing: 0 + white-space: nowrap a - @include top-navigation-colors + color: white !important text-decoration: none - .spacing-div - float: left - width: 2px - height: 30px - background-color: white - margin: 0 auto + &:hover + color: #d1d5db !important + transition: color 0.3s ease .separator-div - @include site-separator-color - @include site-separator-background-color - float: left + background-color: rgba(255, 255, 255, 0.2) width: 1px - height: 30px - margin: 0 2px 0 2px + height: 20px + margin: 0 + display: none + .nav-menu + display: none + position: absolute + left: 50% + transform: translateX(-50%) + .new_main_menu + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + li + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important + a + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: all 0.3s ease !important + white-space: nowrap !important + position: relative !important + padding: 6px 0 !important + margin: 0 !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + &::after + content: '' !important + position: absolute !important + bottom: 0 !important + left: 0 !important + right: 0 !important + height: 2px !important + background-color: #FFB91A !important + border-radius: 2px !important .actions-div - float: right - .twitter-text - @include twitter-background-color - color: white - font-weight: bold - font-size: 0.80em + display: flex + align-items: center + gap: 12px + flex-shrink: 0 + flex-grow: 0 + .theme-toggle-btn + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + .theme-icon + font-size: 18px + color: white + transition: all 0.3s ease + .hidden + display: none + .mobile-menu-toggle + display: none + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + i + font-size: 18px + color: white .btn border: 0 - display: inline-block - font-size: 15px - line-height: 25px + display: inline-flex + align-items: center + justify-content: center + font-size: 14px + line-height: 1.5 margin: 0 - padding: 1px 6px 0 7px - .follow_btn - @include follow-button-colors - display: inline-block - padding: 0 0 0 4px - height: 24px - i, p, img - color: white - display: inline-block - vertical-align: top - i - color: #555 - vertical-align: middle - &:before - padding-right: 0 - p - font-weight: normal - line-height: 24px - text-transform: none - margin: 0 - border-radius: 0 4px 4px 0 - img - margin-right: -1px + padding: 8px 16px + border-radius: 8px + font-weight: 600 + transition: all 0.3s ease + cursor: pointer + text-decoration: none + white-space: nowrap ul list-style: none - margin: 3px 0 0 0 + margin: 0 + padding: 0 + display: flex + align-items: center + gap: 12px li - float: left - padding: 0 0 0 8px + padding: 0 + list-style: none #top_nav_actions - float: right - margin-right: 5px + display: flex + align-items: center + gap: 12px + margin: 0 + padding: 0 li - padding-top: 5px + padding: 0 + list-style: none + .btn-primary, .btn-success + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-width: 0 !important + font-weight: 600 !important + padding: 10px 20px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: transparent !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) !important + i + font-size: 14px + margin: 0 + .btn-outline + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + font-weight: 600 !important + padding: 8px 18px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + border-color: #FFB91A !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important @media (min-width: 320px) and (max-width: 340px) #top_nav_actions margin-right: 0px !important -#logged_user_menu - a - color: black !important +// Mobile Menu +.mobile-menu + display: none + width: 100% + flex-basis: 100% + background: linear-gradient(to right, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + border-top: 1px solid rgba(255, 255, 255, 0.15) + padding: 20px 0 + position: relative + z-index: 1002 + overflow: visible + order: 2 + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) + &.show + display: block !important + visibility: visible !important + opacity: 1 !important + animation: slideDown 0.3s ease-out + .mobile-menu-content + max-width: 1280px + margin: 0 auto + padding: 0 16px + width: 100% + display: block + visibility: visible + .mobile-menu-items + list-style: none + margin: 0 + padding: 0 + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-weight: 500 + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + &.select + color: #FFB91A !important + font-weight: 600 + background-color: rgba(255, 185, 26, 0.15) + .mobile-menu-user + list-style: none + margin: 16px 0 0 0 + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.15) + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + .mobile-menu-signin + margin-top: 16px + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.1) + display: block + visibility: visible + .btn-mobile-signin + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + margin-bottom: 12px + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) + &:active + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) + .btn-mobile-join-now + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) + &:active + transform: translateY(0) + box-shadow: none + +.dropdown#logged_user_menu + position: relative + z-index: 1000 + .dropdown-toggle + color: white !important text-transform: none - text-decoration: none - margin: 0 0px 15px - padding-right: 10px - padding-top: 3px - ul - margin-top: 40px - border-radius: 0 + text-decoration: none !important + margin: 0 + padding: 10px 16px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + display: inline-flex !important + align-items: center + gap: 8px + font-size: 14px + font-weight: 500 + transition: all 0.3s ease + border: 1px solid transparent + cursor: pointer !important + position: relative + z-index: 1001 + pointer-events: auto !important + &:hover + background-color: rgba(255, 255, 255, 0.15) + border-color: rgba(255, 255, 255, 0.2) + text-decoration: none !important + transform: translateY(-1px) + &:active + transform: translateY(0) + i + font-size: 14px + pointer-events: none + ul.dropdown-menu + display: none + position: absolute + right: 0 + top: calc(100% + 24px) + margin-top: 8px + border-radius: 12px + border: 1px solid rgba(255, 255, 255, 0.15) + background-color: rgba(29, 6, 49, 0.97) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 4px 10px rgba(0, 0, 0, 0.2) + z-index: 1050 + min-width: 200px + padding: 8px 0 li - width: 150px + width: 100% a - color: black + color: white !important margin: 0 + padding: 12px 16px + transition: all 0.3s ease + border-radius: 8px + display: block + cursor: pointer &:hover - background-color: #0082C6 !important - color: #fff !important + background-color: rgba(255, 185, 26, 0.2) !important + color: #FFB91A !important + transform: translateX(4px) .divider width: 100% height: 1px - margin: 13px 0 + margin: 8px 0 overflow: hidden - border-bottom: 1px solid #e5e5e5 + border-bottom: 1px solid rgba(255, 255, 255, 0.15) + &.open + ul.dropdown-menu + display: block + +// Override Bootstrap's default dropdown styling +.navbar .dropdown.open .dropdown-menu + display: block !important + +#top_nav_actions + position: relative + z-index: 1000 #flash-msg .alert @@ -277,7 +867,6 @@ header @media (min-width: 320px) and (max-width: 480px) h2 - font-size: 12px !important margin-top: 5px !important .navbar .follow_btn @@ -533,3 +1122,47 @@ fieldset width: 14rem .language_percentage_indicator width: 3rem + +// Full-width layout styles +.page-content-wrapper + width: 100% + max-width: 100% + margin: 0 auto + padding: 20px + +// Ensure home page content uses full width +.home-content, .explore-content, .billboard + width: 100% + max-width: 100% + margin: 0 auto + padding: 0 20px + +// Override Bootstrap navigation styles +.navbar .nav-menu .new_main_menu li.menu_item a, +.navbar ul.new_main_menu li.menu_item a, +ul.new_main_menu li.menu_item a + color: white !important + font-size: 14px !important + font-weight: 500 !important + text-decoration: none !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + padding: 6px 0 !important + margin: 0 !important + background: none !important + border: none !important + box-shadow: none !important + letter-spacing: 0 !important + &:hover, &:focus, &:active, &:visited + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + +@media (min-width: 1280px) + .navbar .nav-menu .new_main_menu li.menu_item a, + .navbar ul.new_main_menu li.menu_item a, + ul.new_main_menu li.menu_item a + font-size: 16px !important diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index f75db9c1d..6164b1c78 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -1,15 +1,19 @@ #search-dingus @include site-well-color - padding: 7px 0px + padding: 10px 15px line-height: 29px - min-height: 33px + min-height: 50px margin-bottom: 1em + border-radius: 8px #value_select display: inline !important select width: 400px .col-md-5 - width: 43% + width: 50% + display: flex + align-items: stretch + padding: 0 .col-md-4 width: 32% @@ -19,12 +23,75 @@ label.paginate font-size: 13px + + label.sr-only + position: absolute + width: 1px + height: 1px + padding: 0 + margin: -1px + overflow: hidden + clip: rect(0, 0, 0, 0) + white-space: nowrap + border-width: 0 + + .search-input-group + display: flex + align-items: stretch + width: 100% + max-width: 500px + button.btn - height: 34px - border-top: 10px - input - width: 180px - color: #000 + height: auto + border: 1px solid #5A2A82 + line-height: 1 + padding: 8px 16px + display: flex + align-items: center + justify-content: center + + input[type="text"] + flex: 1 + min-width: 250px + max-width: 100% + font-size: 14px + color: #1f2937 + background-color: #ffffff + border: 1px solid #d1d5db + padding: 10px 14px + transition: all 0.15s ease-in-out + border-radius: 12px 0 0 12px + margin: 0 + height: auto + line-height: 1.5 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + &:hover + border-color: #9ca3af + &:focus + outline: none + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + background-color: #ffffff + &::placeholder + color: #9ca3af + font-size: 14px + + // Dark mode support + .dark & + background-color: rgba(29, 6, 49, 0.5) + input[type="text"] + background-color: #1D0631 + color: #ffffff + border-color: #4b5563 + &:hover + border-color: #6b7280 + &:focus + border-color: #ffb91a + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) + background-color: #1D0631 + &::placeholder + color: #6b7280 + select width: 150px label.checkbox diff --git a/app/views/home/_compact_project_card.html.haml b/app/views/home/_compact_project_card.html.haml new file mode 100644 index 000000000..d67b36c4e --- /dev/null +++ b/app/views/home/_compact_project_card.html.haml @@ -0,0 +1,37 @@ +- is_account = project.is_a?(Account) +- project_url = is_account ? account_path(project) : project_path(project) + +.compact_project_card{ data: { project_id: project.id } } + .compact_card_inner + .compact_card_header + .project_logo + = link_to project_url do + - if is_account + = image_tag(avatar_img_path(project), class: 'account-avatar') + - elsif project.logo.present? + = image_tag(project.logo.attachment.url(:small), alt: project.name) + - else + .project-icon-placeholder + = project.name[0].upcase + .project_info + .project_name_row + = link_to project_url, class: 'project_name' do + %span= h(truncate(project.name, length: 30)) + - if !is_account && project.updated_at > 7.days.ago + %span.trending_icon + %i.fa.fa-arrow-trend-up + .compact_card_description + - description = is_account ? (project.try(:about) || '') : (project.description || '') + %p= h(truncate(description, length: 80)) + .compact_card_footer + .footer_left + .contributor_info + %i.fa.fa-users + - count = is_account ? (project.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0) : (project.best_analysis&.headcount || 0) + %span= count + .footer_right + - if is_account + - language_name = project.best_account_analysis&.account_analysis_fact&.primary_language&.nice_name || 'N/A' + - else + - language_name = project.best_analysis&.main_language&.nice_name || 'NA' + %span.language_badge= language_name diff --git a/app/views/home/_join_now_home.html.haml b/app/views/home/_join_now_home.html.haml index 2fe5d5a3c..b2a24f233 100644 --- a/app/views/home/_join_now_home.html.haml +++ b/app/views/home/_join_now_home.html.haml @@ -1,16 +1,28 @@ -.join_now_home - %ul - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.claim') - %span.join_now_color= t('.contributions') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.manage') - %span.join_now_color= t('.projects_data') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.highlight') - %span.join_now_color= t('.use') - %br - %p - = link_to t('.join_now'), h(new_account_path(invite: params[:invite], ref: 'homepage')), class: 'btn btn_join_now' +.join_now_card + .card_inner + .join_header + .join_icon + %i.fa.fa-user-plus + .join_header_text + %h3.join_title= t('.join_now') + %p.join_subtitle= t('.become_part_community') + + .benefits_list + .benefit_item + .benefit_icon.benefit_icon_award + %i.fa.fa-trophy + .benefit_content + %h4.benefit_title= t('.track_contributions_title') + %p.benefit_description= t('.track_contributions_desc') + + .benefit_item + .benefit_icon.benefit_icon_trending + %i.fa.fa-line-chart + .benefit_content + %h4.benefit_title= t('.gain_recognition_title') + %p.benefit_description= t('.gain_recognition_desc') + + .join_cta + = link_to new_account_path(invite: params[:invite], ref: 'homepage'), class: 'btn btn_join_cta' do + %i.fa.fa-user-plus + = t('.create_account') diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml new file mode 100644 index 000000000..d87f62121 --- /dev/null +++ b/app/views/home/_top_contributors.html.haml @@ -0,0 +1,40 @@ +.top_contributors_card + .card_header + .header_icon + %i.fa.fa-users + .header_content + %h3.card_title= t('.top_contributors') + %p.card_subtitle= t('.most_active_developers') + + .contributors_list + - @home.most_active_contributors&.each_with_index do |contributor, index| + = link_to account_path(contributor), class: 'contributor_item' do + .contributor_avatar + - begin + = image_tag avatar_img_path(contributor, 40), class: 'avatar_img', alt: contributor.name, onerror: "this.onerror=null; this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2240%22 height=%2240%22 viewBox=%220 0 40 40%22%3E%3Crect width=%2240%22 height=%2240%22 rx=%226%22 fill=%22%239CA3AF%22/%3E%3Ctext x=%2250%25%22 y=%2250%25%22 dominant-baseline=%22middle%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-size=%2218%22 font-weight=%22600%22 fill=%22white%22%3E#{h(contributor.name[0].upcase)}%3C/text%3E%3C/svg%3E';" + - rescue + %div.avatar_placeholder{style: "width: 40px; height: 40px; border-radius: 12px; background: #9CA3AF; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 18px;"} + = contributor.name[0].upcase + + .contributor_info + .contributor_name_row + %span.contributor_name= h(contributor.name) + %span.contributor_rank= "Rank ##{index + 1}" + .contributor_stats + - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 + - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project + - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 + %span.stat_commits + %i.fa.fa-dot-circle-o + = number_with_delimiter(commits) + %span.stat_projects + %i.fa.fa-code + = "#{project_count} projects" + + .contributor_arrow + %i.fa.fa-chevron-right + + .view_all_footer + = link_to accounts_path, class: 'view_all_link' do + %i.fa.fa-users + %span= t('.view_all_contributors') diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index 6630636ee..e0d18cc2b 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -1,56 +1,85 @@ -.col-md-3.top_ten - - if device? - %h2.most_popular_projects - %button.collapsed#no-background{"aria-controls" => "collapseOne", "aria-expanded" => "false", "data-target" => "#collapseOne", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase1 - =t('.popular_projects') - .collapse#collapseOne - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } - - else - %h2.most_popular_projects= t('.popular_projects') - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_projects - %button.collapsed{"aria-controls" => "collapseTwo", "aria-expanded" => "false", "data-target" => "#collapseTwo", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase2 - =t('.active_projects') - .collapse#collapseTwo - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } - - else - %h2.most_active_projects= t('.active_projects') - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } -.col-md-3.top_ten.last - - if device? - %h2.most_recent_projects - %button.collapsed#no-background{"aria-controls" => "collapseThree", "aria-expanded" => "false", "data-target" => "#collapseThree", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase3 - =t('.recent_projects') - .collapse#collapseThree - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } - - else - %h2.most_recent_projects=t('.recent_projects') - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_contributors - %button.collapsed{"aria-controls" => "collapseFour", "aria-expanded" => "false", "data-target" => "#collapseFour", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase4 - =t('.active_contributors') - .collapse#collapseFour - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } - - else - %h2.most_active_contributors=t('.active_contributors') - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } \ No newline at end of file +/ Project Swimlanes Section - New Design +.project_swimlanes_section + .swimlanes_container + .swimlanes_grid + / Most Active Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.active_projects') + .swimlane_content{ data: { swimlane: 'active' } } + / Desktop: Show all projects + - @home.most_active_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_active_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_active_projects.size > 2 + - @home.most_active_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'active', target: 'active', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Most Popular Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.popular_projects') + .swimlane_content{ data: { swimlane: 'popular' } } + / Desktop: Show all projects + - @home.most_popular_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_popular_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_popular_projects.size > 2 + - @home.most_popular_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'popular', target: 'popular', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Recently Added Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.recent_projects') + .swimlane_content{ data: { swimlane: 'recent' } } + / Desktop: Show all projects + - @home.most_recent_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_recent_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_recent_projects.size > 2 + - @home.most_recent_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'recent', target: 'recent', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + + / View All Projects Button + .view_all_container + = link_to projects_path, class: 'btn_view_all_projects' do + = t('.view_all_projects') + %i.fa.fa-arrow-right diff --git a/app/views/home/_user_journeys.html.haml b/app/views/home/_user_journeys.html.haml new file mode 100644 index 000000000..a5d6cc56e --- /dev/null +++ b/app/views/home/_user_journeys.html.haml @@ -0,0 +1,272 @@ +.user_journeys_section + .container + .journeys_header + %h2.journeys_title= t('.your_journey_with_openhub') + %p.journeys_subtitle= t('.explore_ways_description') + + / Mobile View - Collapsible Cards + .journeys_mobile + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '0'} + .journey_header_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.journey_header_title= t('.discover_compare_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.discover_compare_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '1'} + .journey_header_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.journey_header_title= t('.assess_health_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.assess_health_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '2'} + .journey_header_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.journey_header_title= t('.manage_portfolio_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.manage_portfolio_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '3'} + .journey_header_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.journey_header_title= t('.analyze_code_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.analyze_code_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '4'} + .journey_header_icon.journey_icon_security + %i.fa.fa-shield + %h3.journey_header_title= t('.check_security_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.check_security_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '5'} + .journey_header_icon.journey_icon_contribute + %i.fa.fa-search + %h3.journey_header_title= t('.identify_opportunities_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.identify_opportunities_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + + / Desktop View - Grid Cards + .journeys_desktop + .journey_grid_card + .grid_card_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.grid_card_title= t('.discover_compare_title') + %p.grid_card_description= t('.discover_compare_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.grid_card_title= t('.assess_health_title') + %p.grid_card_description= t('.assess_health_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.grid_card_title= t('.manage_portfolio_title') + %p.grid_card_description= t('.manage_portfolio_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.grid_card_title= t('.analyze_code_title') + %p.grid_card_description= t('.analyze_code_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_security + %i.fa.fa-shield + %h3.grid_card_title= t('.check_security_title') + %p.grid_card_description= t('.check_security_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_contribute + %i.fa.fa-search + %h3.grid_card_title= t('.identify_opportunities_title') + %p.grid_card_description= t('.identify_opportunities_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + +:javascript + document.addEventListener('DOMContentLoaded', function() { + const toggleButtons = document.querySelectorAll('.journey_toggle'); + + toggleButtons.forEach(function(button) { + button.addEventListener('click', function() { + const journeyCard = this.closest('.journey_card'); + const content = journeyCard.querySelector('.journey_card_content'); + const icon = this.querySelector('.journey_toggle_icon i'); + + journeyCard.classList.toggle('expanded'); + + if (journeyCard.classList.contains('expanded')) { + icon.classList.remove('fa-plus'); + icon.classList.add('fa-minus'); + } else { + icon.classList.remove('fa-minus'); + icon.classList.add('fa-plus'); + } + }); + }); + }); diff --git a/app/views/home/_whats_new.html.haml b/app/views/home/_whats_new.html.haml index 130643baf..c045b66e9 100644 --- a/app/views/home/_whats_new.html.haml +++ b/app/views/home/_whats_new.html.haml @@ -1,2 +1,9 @@ -%a{ href: 'https://www.blackduck.com/resources/analyst-reports/open-source-security-risk-analysis.html', target: '_blank' } - %img{src: image_path('home/OSSRA-OH-banner.png'), style: 'margin-left: 25px; margin-top: 15px'} +.whats_new_card + .card_inner + .card_header_section + %h3.whats_new_title= t('.whats_new') + %p.whats_new_subtitle= t('.latest_updates_reports') + + .featured_image_section + %a{ href: 'https://www.blackduck.com/resources/analyst-reports/ossra-report.html', target: '_blank', class: 'featured_image_link' } + %img.featured_image{src: image_path('home/OSSRA-OH-banner.png'), alt: t('.ossra_report_alt')} diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index c47b3002b..a9cfb02b6 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,31 +1,83 @@ - content_for(:html_title) { t('.page_title') } -.showcase - .billboard - .row - .col-md-12 - %h2.discover_msg - = t('.discover_message') - .row - .col-md-12.billboard_search - %form.search#search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'homepage' } - %input.for_search_all_code{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text' } - %i#icon_text.fa.fa-search - - .row - .col-md-12 - .global_stats - #global_statistics - %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) - %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) - %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) - %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) -.home_page_row - .col-md-4.col-xs-7.col-sm-6 - %h2.join_now= t('.join_now') - = render partial: '/home/join_now_home' - .col-md-8.col-xs-5.col-sm-6 - %h2.wh_new= t('.whats_new') - = render partial: '/home/whats_new' -.landing.col-md-12.col-sm-12.col-xs-12.last_section - = home_top_lists +- content_for(:javascript) do + = javascript_include_tag 'rotating_stats' + = javascript_include_tag 'project_swimlanes' + +/ Hero Section +.hero_section + .hero_container + / Hero Header - Centered + .hero_header + %h1.hero_title= t('.discover_message') + %p.hero_subtitle= t('.worlds_largest_directory') + + / Search Bar - Centered & Compact + .search_container + %form.search_form#search_form{ action: projects_path } + %input{ type: :hidden, name: 'ref', value: 'homepage' } + .search_input_wrapper + %i.fa.fa-search.search_icon + %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } + + / Rotating Stats + .rotating_stats + #global_statistics + %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) + %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) + %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) + %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) + + / Feature Cards - Moved to Top + .features_section + .feature_card + .feature_icon.feature_icon_yellow + %i.fa.fa-database + %h3.feature_title= t('.feature_directory_title') + %p.feature_description= t('.feature_directory_desc') + .feature_card + .feature_icon.feature_icon_blue + %i.fa.fa-heartbeat + %h3.feature_title= t('.feature_health_title') + %p.feature_description= t('.feature_health_desc') + .feature_card + .feature_icon.feature_icon_green + %i.fa.fa-user-circle + %h3.feature_title= t('.feature_portfolio_title') + %p.feature_description= t('.feature_portfolio_desc') + + / Quick Stats Bar - Ultra Compact & Elegant + .quick_stats_bar + .stat_item + .stat_value= "2.5M+" + .stat_label= t('.projects_label') + .stat_divider + .stat_item + .stat_value= number_with_delimiter(@home.person_count / 1000) + "K" + .stat_label= t('.contributors_label') + .stat_divider + .stat_item + .stat_value= "450+" + .stat_label= t('.languages_label') + .stat_divider + .stat_item + .stat_value= "12K" + .stat_label= t('.organizations_label') +/ Top Contributors and What's New Section +.content_section + .container + .content_grid + / Top Contributors - 35% + .contributors_column + = render partial: '/home/top_contributors' + + / What's New and Join Now - 65% + .whats_new_column + .whats_new_join_stack + = render partial: '/home/whats_new' + = render partial: '/home/join_now_home' + +/ User Journeys Section += render partial: '/home/user_journeys' + +/ Project Swimlanes Section += home_top_lists diff --git a/app/views/layouts/partials/_footer.html.haml b/app/views/layouts/partials/_footer.html.haml index 53b3e40d7..d16f5375d 100644 --- a/app/views/layouts/partials/_footer.html.haml +++ b/app/views/layouts/partials/_footer.html.haml @@ -1,40 +1,51 @@ - raw_email = '%69%6Efo%40O%70en%48u%62.%6E%65t' -.footer-left - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } +.footer-container + .footer-grid + .footer-left + %a.logo_link{ href: 'https://www.blackduck.com' } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck' } -.footer-mid - = "ABOUT BLACK DUCK" - %p - %p - %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' - %p - %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' - %p - %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' - %p - %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' + .footer-mid + %h3 ABOUT BLACK DUCK + %ul + %li + %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' + %li + %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' + %li + %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' + %li + %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' -.footer-right - = "ABOUT OPEN HUB" - %p - %p - %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog - %p - %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') - %p - %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us + .footer-right + %h3 ABOUT OPEN HUB + %ul + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog + %li + %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') + %li + %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us - -.footer-bottom - %sup © - = Time.current.year - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span{ itemprop: 'publisher' } - %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." - = t('copyright.rights_text') + .footer-bottom + .footer-bottom-content + .copyright + %sup © + = Time.current.year + %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span{ itemprop: 'publisher' } + %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." + = t('copyright.rights_text') + .follow-us + %span Follow us: + :ruby + twitter_url = 'https://x.com/intent/follow?original_referer=' + twitter_url += CGI.escape request.url + twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' + %a{ href: twitter_url, target: '_blank', 'aria-label' => 'Follow us on Twitter' } + %i.icon-twitter diff --git a/app/views/layouts/partials/_mast.html.haml b/app/views/layouts/partials/_mast.html.haml index f28f75c58..e67c7f71c 100644 --- a/app/views/layouts/partials/_mast.html.haml +++ b/app/views/layouts/partials/_mast.html.haml @@ -1,52 +1,103 @@ +:ruby + explore_projects_class = params[:action] == 'explore/projects' ? 'select' : '' + explore_orgs_class = params[:action] == 'explore/orgs' ? 'select' : '' + tools_select = params[:action] == 'tools' ? 'select' : '' + .navbar - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span.hidden{ itemprop: 'author' } openhub.net - %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. - %span + .navbar-container + %span.hidden{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span.hidden{ itemprop: 'author' } openhub.net + %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. + .logo-and-brand %div.logo-div - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } - %div.spacing-div + %a.logo_link{ href: root_path } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck Open Hub' } %div.separator-div - %div.spacing-div %div.company-div %a{ href: root_path } %span.navbar_large_text Open Hub - %div.actions-div - %ul#top_nav_actions - %li.twitter_follow - :ruby - twitter_url = 'https://twitter.com/intent/follow?original_referer=' - twitter_url += CGI.escape request.url - twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' - %a.btn.follow_btn{ href: twitter_url, target: '_blank' } - %i.icon-twitter - %p.follow! #{t(:follow)} @ - %p.twitter-text  OH  - - if logged_in? - %li - .dropdown#logged_user_menu - %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } - %i.icon-user - = h(current_user.name) - %i.icon-caret-down - %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer - %li - = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %nav.nav-menu + %ul.new_main_menu{ class: page_context[:select_top_menu_nav] } + %li.menu_item.projects + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li.menu_item.people + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li.menu_item.organizations + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li.menu_item.tools + = link_to t(:tools_menu), tools_path, class: tools_select + %li.menu_item.blog + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li.menu_item.bdsa + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + %div.actions-div + %button#theme-toggle.theme-toggle-btn{ 'aria-label' => 'Toggle theme' } + %i#theme-icon-moon.icon-moon.theme-icon + %i#theme-icon-sun.icon-sun.theme-icon.hidden + %ul#top_nav_actions.desktop-actions + - if logged_in? + %li + .dropdown#logged_user_menu + %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } + %i.icon-user + = h(current_user.name) + %i.icon-caret-down + %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) - - if current_user_is_admin? - %li - = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) - %li.divider - %li - = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) - - else - %li - %a.btn.btn-mini.btn-primary.btn-header{ href: new_session_path }= t :sign_in + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li.divider + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + %li + %a.btn.btn-primary.btn-header{ href: new_session_path } + %i.icon-user + = t :sign_in + %li + %a.btn.btn-outline.btn-join-now{ href: new_account_path } + Join Now + %button#mobile-menu-toggle.mobile-menu-toggle{ 'aria-label' => 'Menu' } + %i#mobile-menu-icon.icon-reorder + #mobile-menu.mobile-menu + .mobile-menu-content + %ul.mobile-menu-items + %li + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li + = link_to t(:tools_menu), tools_path, class: tools_select + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + = render 'shared/search.html.haml' + - if logged_in? + %ul.mobile-menu-user + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - %a.btn.btn-mini.btn-success.btn-header{ href: new_account_path }= t :join_now + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + .mobile-menu-signin + %a.btn.btn-primary.btn-mobile-signin{ href: new_session_path } + %i.icon-user + = t :sign_in + %a.btn.btn-outline.btn-mobile-join-now{ href: new_account_path } + Join Now - if read_only_mode? %div diff --git a/app/views/shared/search_dingus/_search_bar.html.haml b/app/views/shared/search_dingus/_search_bar.html.haml index acc25067e..ad48ad78a 100644 --- a/app/views/shared/search_dingus/_search_bar.html.haml +++ b/app/views/shared/search_dingus/_search_bar.html.haml @@ -8,8 +8,10 @@ - else .col-md-5.col-sm-5.col-xs-5.no_padding - %label #{t('.search_text')}   - = text_field_tag :query, params[:query] + %label.sr-only= t('.search_text') + .search-input-group + = text_field_tag :query, params[:query], + placeholder: t('.search_text') - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-refresh + %button.btn.btn-refresh{ type: 'Submit' } + %i.icon-search diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e3160c2b9..b35d3c3ee 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,6 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js] + api/vulnerability.sass api/vulnerability.js + rotating_stats.js project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 9e5497ce0..3607a7749 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -3,24 +3,89 @@ en: index: page_title: 'Open Hub, the open source network' discover_message: 'Discover, Track and Compare Open Source' + worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' - whats_new: "What's New" counting: 'Counting %{count} lines of code' indexing: 'Indexing %{count} open source projects' connecting: 'Connecting %{count} open source contributors' tracking: 'Tracking %{count} source control repositories' search_projects: 'Search Projects...' + feature_directory_title: 'Massive Directory' + feature_directory_desc: 'Indexing over 2.5 million open source projects.' + feature_health_title: 'Assess Health' + feature_health_desc: 'Check activity levels and risk before you adopt.' + feature_portfolio_title: 'Manage Portfolio' + feature_portfolio_desc: 'Claim your commits and track your developer rankings.' + projects_label: 'Projects' + contributors_label: 'Contributors' + languages_label: 'Languages' + organizations_label: 'Organizations' top_lists: popular_projects: 'Most Popular Projects' active_projects: 'Most Active Projects' active_contributors: 'Most Active Contributors' recent_projects: 'Recently Added Projects' + show_more: 'Show More' + show_less: 'Show Less' + view_all_projects: 'View All Projects' join_now_home: - claim: 'Claim' - contributions: 'your contributions' - manage: 'Manage' - projects_data: "your project's data" - highlight: 'Highlight' - use: 'your use of FOSS' join_now: 'Join Now' + become_part_community: 'Become part of the community' + track_contributions_title: 'Track Your Contributions' + track_contributions_desc: 'Showcase your open source portfolio and contributions' + gain_recognition_title: 'Gain Recognition' + gain_recognition_desc: 'Build your developer profile with verified achievements' + create_account: 'Create Your Account' + + whats_new: + whats_new: "What's New" + latest_updates_reports: 'Latest updates and reports' + ossra_report_alt: 'Open Source Security and Risk Analysis Report' + + top_contributors: + top_contributors: 'Top Contributors' + most_active_developers: 'Most active contributors' + view_all_contributors: 'View All Contributors' + view_all: 'View all' + + user_journeys: + your_journey_with_openhub: 'Your Journey with Open Hub' + explore_ways_description: 'Explore the many ways you can discover, track, and contribute to open source projects' + key_steps: 'Key Steps' + + discover_compare_title: 'Discover & Compare Solutions' + discover_compare_desc: 'Find the perfect open source solution by comparing features, project activity, and community engagement side-by-side.' + discover_compare_step1: 'Compare projects side-by-side' + discover_compare_step2: 'Review activity metrics' + discover_compare_step3: 'Analyze community interest' + + assess_health_title: 'Assess Project Health' + assess_health_desc: 'Determine if a project is safe to adopt by analyzing commit frequency, number of contributors, and growth trends.' + assess_health_step1: 'Analyze commit history trends' + assess_health_step2: 'Review year-over-year growth' + assess_health_step3: 'Track number of contributors' + + manage_portfolio_title: 'Manage Your Developer Portfolio' + manage_portfolio_desc: 'Build a unified portfolio of your open source work by claiming your contributions across all repositories.' + manage_portfolio_step1: 'Claim your commits' + manage_portfolio_step2: 'Earn "Kudos" and rankings' + manage_portfolio_step3: 'Track your global rank' + + analyze_code_title: 'Analyze Code Composition' + analyze_code_desc: 'Get deep insights into the codebase architecture, including language breakdown, lines of code, and comment ratios.' + analyze_code_step1: 'View language distribution' + analyze_code_step2: 'Check comment-to-code ratios' + analyze_code_step3: 'Estimate effort (COCOMO model)' + + check_security_title: 'Check Security Vulnerabilities' + check_security_desc: 'Verify the security track record of a project by checking known vulnerabilities and confidence scores before you adopt.' + check_security_step1: 'View Security Confidence Index' + check_security_step2: 'Check reported vulnerabilities (CVEs)' + check_security_step3: 'Assess risk level' + + identify_opportunities_title: 'Identify Contribution Opportunities' + identify_opportunities_desc: 'Find projects that need your skills and identify the best repositories to start your open source journey.' + identify_opportunities_step1: 'Find beginner-friendly projects' + identify_opportunities_step2: 'Locate source repositories (GitHub/GitLab)' + identify_opportunities_step3: 'Track your contribution impact' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb index 04d2523cc..3e27b7716 100644 --- a/test/controllers/home_controller_test.rb +++ b/test/controllers/home_controller_test.rb @@ -9,7 +9,13 @@ class HomeControllerTest < ActionController::TestCase best_account_analysis.account.update(best_vita_id: best_account_analysis.id, created_at: 4.days.ago) account_analysis_fact = best_account_analysis.account_analysis_fact account_analysis_fact.update(last_checkin: Time.current) - Rails.cache.stubs(:fetch).returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-recently_active_accounts-cache').returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-person_count-cache').returns(1_000_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-active_project_count-cache').returns(2_500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-repository_count-cache').returns(500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-vita_count-cache').returns([]) + Rails.cache.stubs(:fetch).with('HomeDecorator-most_active_projects-cache').returns([]) + Rails.cache.stubs(:fetch).with('homepage_top_lists').returns([]) get :index assert_response :success diff --git a/test/controllers/licenses_controller_test.rb b/test/controllers/licenses_controller_test.rb index b2ac3ce27..e04bba48b 100644 --- a/test/controllers/licenses_controller_test.rb +++ b/test/controllers/licenses_controller_test.rb @@ -52,7 +52,7 @@ class LicensesControllerTest < ActionController::TestCase get :show, params: { id: @license.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e59b18bb3..fc1e35bab 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -83,7 +83,7 @@ class OrganizationsControllerTest < ActionController::TestCase get :show, params: { id: @organization.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'should support show page via xml api' do diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index f5af4697c..59f5066ae 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -292,7 +292,7 @@ class ProjectsControllerTest < ActionController::TestCase get :show, params: { id: project.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'show accepts being called via api' do From 8bfb62785c0116c9e5d844a42bc28c62906466d7 Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Wed, 25 Feb 2026 16:18:40 +0530 Subject: [PATCH 02/53] OTWO-7574 - Figma redesign for homepage --- app/assets/javascripts/home.js.coffee | 2 +- app/assets/javascripts/rotating_stats.js | 29 --- app/assets/stylesheets/home.sass | 215 +++++++++++------------ app/assets/stylesheets/home_search.sass | 64 +++---- app/assets/stylesheets/page.sass | 28 ++- app/views/home/_top_lists.html.haml | 6 +- app/views/home/index.html.haml | 10 +- app/views/layouts/application.html.haml | 2 +- config/initializers/assets.rb | 2 +- 9 files changed, 171 insertions(+), 187 deletions(-) delete mode 100644 app/assets/javascripts/rotating_stats.js diff --git a/app/assets/javascripts/home.js.coffee b/app/assets/javascripts/home.js.coffee index 0e23b0b5b..560e1b882 100644 --- a/app/assets/javascripts/home.js.coffee +++ b/app/assets/javascripts/home.js.coffee @@ -2,7 +2,7 @@ $(document).on 'page:change', -> divs = $('p[id^="content-"]') i = 0 do -> - divs.eq(i).removeClass('hide').fadeIn(400).delay(6000).fadeOut 400, arguments.callee + divs.eq(i).removeClass('hide').fadeIn(200).delay(2500).fadeOut 200, arguments.callee i = ++i % divs.length $('#icon_text').click -> diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js deleted file mode 100644 index 39a368b7b..000000000 --- a/app/assets/javascripts/rotating_stats.js +++ /dev/null @@ -1,29 +0,0 @@ -// Rotating Stats Animation for Homepage -document.addEventListener('DOMContentLoaded', function() { - var globalStats = document.getElementById('global_statistics'); - - if (!globalStats) return; - - var statElements = globalStats.querySelectorAll('p'); - - if (statElements.length === 0) return; - - var currentIndex = 0; - - // Show first stat initially - statElements[0].classList.remove('hide'); - - // Rotate stats every 2 seconds - setInterval(function() { - // Hide current stat - statElements[currentIndex].classList.add('hide'); - - // Move to next stat - currentIndex = (currentIndex + 1) % statElements.length; - - // Show next stat after a brief delay - setTimeout(function() { - statElements[currentIndex].classList.remove('hide'); - }, 300); - }, 2000); -}); diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4978e69e3..befb1915d 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -4,15 +4,21 @@ // Main container with consistent 1280px max-width throughout // Isolated from global body font-size: 1.3rem override -.hero_section - padding: 0 16px 40px - background: #fff !important +// Common section background +.section_bg + background: #f9fafb !important transition: background 0.3s ease + html.dark & + background: #1D0631 !important + +.hero_section + @extend .section_bg + padding: 0 16px 4px box-sizing: border-box @media (min-width: 640px) - padding: 0 24px 40px + padding: 0 24px 16px @media (min-width: 1024px) - padding: 0 32px 40px + padding: 0 32px 16px // Dark mode support html.dark & background: #1D0631 !important @@ -107,12 +113,11 @@ // Old placeholder and btn_join_now styles removed - handled in new components .landing + @extend .section_bg margin-top: 20px margin-left: 0 padding: 32px 16px width: 100% - background: #f9fafb !important - transition: background 0.3s ease @media (min-width: 640px) padding: 32px 24px @media (min-width: 1024px) @@ -338,8 +343,9 @@ button padding: 12px 24px // Dark mode support - Yellow background with purple text html.dark & - background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) - border-color: #ffb91a + background: linear-gradient(90deg, #2D1548 0%, #2D1548 50%, #2D1548 100%) + border-color: rgba(255, 255, 255, 0.1) + .stat_item text-align: center @@ -354,9 +360,10 @@ button font-size: 18px @media (min-width: 768px) font-size: 20px - // Dark mode - Purple text on yellow background + // Dark mode - Yellow text on purple background html.dark & - color: #5A2A82 + color: #ffb91a + .stat_label font-size: 8px @@ -370,9 +377,10 @@ button font-size: 9px @media (min-width: 768px) font-size: 10px - // Dark mode - Purple text on yellow background + // Dark mode - Gray text on purple background html.dark & - color: #5A2A82 + color: #9ca3af + .stat_divider display: none @@ -383,17 +391,17 @@ button height: 32px background: #9ca3af opacity: 1 - // Dark mode - Purple divider on yellow background + // Dark mode - White divider with low opacity on purple background html.dark & @media (min-width: 768px) - background: #5A2A82 - opacity: 0.3 + background: #ffffff + opacity: 0.2 + // Content Section .content_section + @extend .section_bg padding: 32px 0 - background: white !important - transition: background 0.3s ease // Dark mode support html.dark & background: #1D0631 !important @@ -429,24 +437,20 @@ button // Top Contributors Card - Matches ContributorJourney.tsx .top_contributors_card - position: relative - padding: 3px + background: white border-radius: 16px - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - margin-bottom: 24px overflow: hidden - transition: box-shadow 0.3s + margin-bottom: 4px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) html.dark & - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) - > * - background: white - border-radius: 16px - overflow: hidden - html.dark & - background: #1D0631 // Card Header .top_contributors_card .card_header @@ -456,7 +460,7 @@ button padding: 24px 24px 16px 24px background: white html.dark & - background: #1D0631 + background: #2D1548 .top_contributors_card .header_icon display: flex @@ -662,51 +666,45 @@ button font-weight: 600 color: #5A2A82 text-decoration: none - border-top: 1px solid rgba(90, 42, 130, 0.2) - transition: color 0.2s + border: 2px solid rgba(90, 42, 130, 0.2) + border-radius: 12px + margin-top: 8px + transition: all 0.2s cursor: pointer - - span - border-bottom: 1px solid #5A2A82 + background: transparent &:hover color: #1D0631 - span - border-bottom-color: #1D0631 + border-color: #5A2A82 + background: rgba(90, 42, 130, 0.05) i font-size: 16px html.dark & - color: white !important - border-top-color: #ffb91a - border-top-width: 2px - border-radius: 12px - margin-top: 8px - span - border-bottom-color: white + color: #ffb91a !important + border-color: #ffb91a &:hover - color: white !important + background: #ffb91a + color: #1D0631 !important + // What's New Card - Figma Design .whats_new_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .card_header_section padding: 16px 16px 12px 16px !important @@ -715,7 +713,7 @@ button padding: 24px 24px 16px 24px !important // Dark mode support html.dark & - background: #1D0631 + background: #2D1548 .whats_new_title font-size: 18px !important @@ -768,24 +766,21 @@ button // Join Now Card - Figma Design (p-6 wrapper) .join_now_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + padding: 24px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - padding: 24px !important - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .join_header display: flex @@ -953,9 +948,12 @@ button // User Journeys Section .user_journeys_section background: #1D0631 - padding: 64px 0 + padding: 8px 0 !important + width: 100vw + margin-left: calc(-50vw + 50%) + margin-right: calc(-50vw + 50%) @media (max-width: 768px) - padding: 48px 0 + padding: 16px 0 !important // Matches Figma: max-w-7xl mx-auto .container @@ -1171,13 +1169,12 @@ button // Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 .project_swimlanes_section - padding: 32px 16px - background: #f9fafb - transition: background 0.3s ease + @extend .section_bg + padding: 8px 16px !important @media (min-width: 640px) - padding: 32px 24px + padding: 8px 24px !important @media (min-width: 1024px) - padding: 32px 32px + padding: 8px 32px !important html.dark & background: #1D0631 @@ -1226,29 +1223,32 @@ button // Compact Project Card Styles .compact_project_card position: relative - padding: 3px - border-radius: 8px + border-radius: 16px overflow: hidden transition: all 0.3s ease - background: transparent cursor: pointer + border: 1px solid rgba(229, 231, 235, 0.8) + background: white + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + &:hover - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - .compact_card_inner - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + border-color: #5A2A82 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + + html.dark & + background: #2D1548 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + border-color: #ffb91a + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) .compact_project_card .compact_card_inner - background: white - border-radius: 8px padding: 12px transition: all 0.3s ease height: 100% - border: 1px solid #e5e7eb @media (min-width: 640px) padding: 16px - html.dark & - background: #1D0631 - border-color: #4b5563 .compact_project_card .compact_card_header display: flex @@ -1432,7 +1432,6 @@ button .hidden_card width: 100% - margin-bottom: 16px // Show More Button .show_more_btn @@ -1442,7 +1441,7 @@ button color: #5A2A82 border: 2px solid #5A2A82 padding: 12px 16px - border-radius: 8px + border-radius: 12px font-weight: 600 cursor: pointer transition: all 0.3s ease @@ -1453,11 +1452,11 @@ button color: white html.dark & background: transparent - color: white - border-color: white + color: #ffb91a + border-color: #ffb91a &:hover - background: white - color: #5A2A82 + background: #ffb91a + color: #1D0631 // Pagination Controls .pagination_controls @@ -1516,7 +1515,7 @@ button // View All Projects Button - Matches Figma: pt-4 pb-8 .view_all_container - padding-top: 16px + padding-top: 32px padding-bottom: 32px padding-left: 16px padding-right: 16px @@ -1540,8 +1539,8 @@ button gap: 8px padding: 12px 24px background: #ffb91a - color: #000000 - border-radius: 8px + color: #1D0631 + border-radius: 12px font-weight: 600 font-size: 14px text-decoration: none @@ -1552,10 +1551,10 @@ button &:hover background: rgba(255, 185, 26, 0.9) box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) - color: #000000 + color: #1D0631 html.dark & - color: #ffffff !important + color: #1D0631 !important &:hover - color: #ffffff !important + color: #1D0631 !important i font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass index b1c0d45e8..b873b26f5 100644 --- a/app/assets/stylesheets/home_search.sass +++ b/app/assets/stylesheets/home_search.sass @@ -16,34 +16,34 @@ width: 100% display: block - // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) - // Positioned to align with input text baseline - .search_icon - position: absolute - top: 50% - left: 8px - transform: translateY(-60%) - display: block - width: 14px - height: 14px - font-size: 14px - color: #9ca3af - pointer-events: none - z-index: 10 - transition: color 0.3s ease - text-align: center - font-style: normal - @media (min-width: 640px) - left: 24px - width: 20px - height: 20px - font-size: 20px - // Dark mode support - html.dark & - color: #6b7280 - // Ensure Font Awesome icon centers properly - &::before - display: block + // Search Icon Wrapper - Figma Design +.search_icon_wrapper + position: absolute + top: 0 + bottom: 0 + left: 8px + display: flex + align-items: center + pointer-events: none + @media (min-width: 640px) + left: 24px + +// Search Icon - Figma specs +.search_icon + width: 14px + height: 14px + font-size: 14px + line-height: 0.5 + color: #9ca3af + transition: color 0.3s ease + @media (min-width: 640px) + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 input.search_input @@ -131,15 +131,15 @@ form#search_form padding: 16px 16px 16px 56px !important font-size: 16px !important &:focus - border-color: #0E4B7A !important + border-color: #5A2A82 !important box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important outline: none !important // Dark mode support html.dark & background: #1D0631 !important color: #ffffff !important - border-color: #4b5563 !important + border-color: #5A2A82 !important &:focus - border-color: #2E8B9E !important + border-color: #ffb91a !important background: #1D0631 !important - box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important + box-shadow: 0 0 0 4px rgba(255, 185, 26, 0.1) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cb5fd0334..994ad5244 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -18,13 +18,17 @@ body color: #000 width: 100% overflow-x: hidden + background-color: #f9fafb !important .container#page, #page width: 100% !important max-width: 100% !important - background-color: white + background-color: #f9fafb margin: 0 !important padding: 0 !important + transition: background-color 0.3s ease + html.dark & + background-color: #1D0631 header // Figma design: gradient background for entire header @@ -313,7 +317,9 @@ header .navbar // Figma design: gradient background with purple theme - position: relative + position: sticky + top: 0 + z-index: 1000 background: linear-gradient(to right, #000000, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) @@ -603,16 +609,17 @@ header .mobile-menu display: none width: 100% - flex-basis: 100% background: linear-gradient(to right, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) border-top: 1px solid rgba(255, 255, 255, 0.15) padding: 20px 0 - position: relative + position: absolute + top: 64px + left: 0 + right: 0 z-index: 1002 overflow: visible - order: 2 box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) &.show display: block !important @@ -694,7 +701,7 @@ header background-color: #FFB91A !important color: #000 !important border: none !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -720,7 +727,7 @@ header background-color: transparent !important color: #FFB91A !important border: 2px solid #FFB91A !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -736,6 +743,13 @@ header &:active transform: translateY(0) box-shadow: none + html.dark & + background-color: transparent !important + color: #FFB91A !important + border-color: #FFB91A !important + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + .dropdown#logged_user_menu position: relative diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index e0d18cc2b..02db80a5a 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -19,7 +19,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_active_projects.size > 2 - - @home.most_active_projects.drop(2).each do |project| + - @home.most_active_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -44,7 +44,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_popular_projects.size > 2 - - @home.most_popular_projects.drop(2).each do |project| + - @home.most_popular_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -69,7 +69,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_recent_projects.size > 2 - - @home.most_recent_projects.drop(2).each do |project| + - @home.most_recent_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index a9cfb02b6..82762ba3e 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,6 +1,5 @@ - content_for(:html_title) { t('.page_title') } - content_for(:javascript) do - = javascript_include_tag 'rotating_stats' = javascript_include_tag 'project_swimlanes' / Hero Section @@ -16,7 +15,8 @@ %form.search_form#search_form{ action: projects_path } %input{ type: :hidden, name: 'ref', value: 'homepage' } .search_input_wrapper - %i.fa.fa-search.search_icon + .search_icon_wrapper + %i.fa.fa-search.search_icon %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } / Rotating Stats @@ -76,8 +76,8 @@ = render partial: '/home/whats_new' = render partial: '/home/join_now_home' -/ User Journeys Section -= render partial: '/home/user_journeys' - / Project Swimlanes Section = home_top_lists + +/ User Journeys Section += render partial: '/home/user_journeys' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7c5f3dbe8..a8438e724 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,8 +18,8 @@ %body{ zoom: 1 } = yield :session_projects_banner + %header= render partial: 'layouts/partials/header' .container#page - %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear %footer= render partial: 'layouts/partials/footer' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index b35d3c3ee..052e2ce67 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -7,5 +7,5 @@ Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css api/vulnerability.sass api/vulnerability.js - rotating_stats.js project_swimlanes.js] + project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] From a44a96b38a748baaa9340460f73db94634db9d69 Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Mon, 2 Mar 2026 11:50:37 +0530 Subject: [PATCH 03/53] OTWO-7574 Fixed card layout issues in homepage --- app/assets/stylesheets/home.sass | 47 ++++++++++++++++++---- app/views/home/_top_contributors.html.haml | 7 +--- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index befb1915d..dad3498f3 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -443,6 +443,8 @@ button margin-bottom: 4px box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) transition: box-shadow 0.2s + display: flex + flex-direction: column &:hover box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) html.dark & @@ -516,6 +518,30 @@ button display: flex flex-direction: column gap: 8px + max-height: 300px + overflow-y: scroll + scrollbar-width: thin + scrollbar-color: rgba(90, 42, 130, 0.4) rgba(90, 42, 130, 0.1) + @media (min-width: 768px) + max-height: 500px + &::-webkit-scrollbar + width: 8px + &::-webkit-scrollbar-track + background: rgba(90, 42, 130, 0.1) + border-radius: 3px + &::-webkit-scrollbar-thumb + background: rgba(90, 42, 130, 0.4) + border-radius: 3px + &:hover + background: rgba(90, 42, 130, 0.6) + html.dark & + scrollbar-color: rgba(255, 185, 26, 0.4) rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-track + background: rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-thumb + background: rgba(255, 185, 26, 0.4) + &:hover + background: rgba(255, 185, 26, 0.6) .top_contributors_card .contributor_item display: flex @@ -540,10 +566,8 @@ button background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) border-color: #5A2A82 -// Show only top 4 contributors on mobile -.top_contributors_card .contributor_item:nth-child(n+5) - @media (max-width: 767px) - display: none +// All contributors visible on mobile to enable scrolling +// Mobile users can scroll to see all contributors // Avatar .top_contributors_card .contributor_avatar @@ -948,7 +972,7 @@ button // User Journeys Section .user_journeys_section background: #1D0631 - padding: 8px 0 !important + padding: 32px 0 !important width: 100vw margin-left: calc(-50vw + 50%) margin-right: calc(-50vw + 50%) @@ -996,10 +1020,10 @@ button .journey_card position: relative - padding: 3px border-radius: 12px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + border: 0.8px solid #ffb91a + background: #1D0631 margin-bottom: 16px transition: all 0.3s @@ -1230,11 +1254,14 @@ button border: 1px solid rgba(229, 231, 235, 0.8) background: white box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + height: 100% + display: flex + flex-direction: column &:hover border-color: #5A2A82 box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) - + html.dark & background: #2D1548 border-color: rgba(255, 255, 255, 0.1) @@ -1247,6 +1274,8 @@ button padding: 12px transition: all 0.3s ease height: 100% + display: flex + flex-direction: column @media (min-width: 640px) padding: 16px @@ -1347,6 +1376,8 @@ button .compact_project_card .compact_card_description margin-bottom: 12px + flex: 1 + min-height: 40px p font-size: 12px color: #6b7280 diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml index d87f62121..1544ad3b9 100644 --- a/app/views/home/_top_contributors.html.haml +++ b/app/views/home/_top_contributors.html.haml @@ -22,14 +22,9 @@ %span.contributor_rank= "Rank ##{index + 1}" .contributor_stats - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 - - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project - - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 %span.stat_commits %i.fa.fa-dot-circle-o - = number_with_delimiter(commits) - %span.stat_projects - %i.fa.fa-code - = "#{project_count} projects" + = "#{number_with_delimiter(commits)} commits" .contributor_arrow %i.fa.fa-chevron-right From 65ef0127cbf6307abeefae4b77389d924208d06a Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Mar 2026 15:51:25 +0530 Subject: [PATCH 04/53] OTWO-7557 project show page design modification --- app/assets/stylesheets/dark_theme.sass | 20 + app/assets/stylesheets/page.sass | 39 +- .../stylesheets/project_show_redesign.sass | 1283 +++++++++++++++++ .../responsive_project_layout.html.haml | 4 +- app/views/projects/show.html.haml | 503 ++++++- .../projects/show/_header_redesign.html.haml | 128 ++ .../show/_licenses_redesign.html.haml | 88 ++ config/locales/projects.en.yml | 62 + 8 files changed, 2053 insertions(+), 74 deletions(-) create mode 100644 app/assets/stylesheets/project_show_redesign.sass create mode 100644 app/views/projects/show/_header_redesign.html.haml create mode 100644 app/views/projects/show/_licenses_redesign.html.haml diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 9a7f3d844..5b8198f4f 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -148,3 +148,23 @@ html.dark .good color: #4ade80 !important + + // Project security: Did You Know block (legacy and redesign templates) + #project_container .project_row #did_you_know + background: #1D0631 !important + + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + &:hover + color: #93c5fd !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cb5fd0334..2a36dc40d 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -126,7 +126,6 @@ header gap: 24px #page_contents max-width: 100% - padding: 0 20px .separator-div @include site-separator-color @@ -1123,6 +1122,44 @@ fieldset .language_percentage_indicator width: 3rem +// Dark mode guards for legacy project security/no-analysis blocks +html.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + +body.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + // Full-width layout styles .page-content-wrapper width: 100% diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass new file mode 100644 index 000000000..d07308547 --- /dev/null +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -0,0 +1,1283 @@ +// Project Show Page Redesign - Clean Implementation Based on Figma Design +// ============================================================================ + +// Design System Variables +// ============================================================================ +$purple-dark: #1D0631 +$purple-primary: #5A2A82 +$purple-light: #9F7ABA +$yellow-accent: #FFB91A +$yellow-hover: #FFCC4D + +// Neutrals +$gray-50: #f9fafb +$gray-100: #f3f4f6 +$gray-200: #e5e7eb +$gray-600: #6b7280 +$gray-700: #374151 +$gray-900: #111827 + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05) +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) + +// System Font Stack +$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + +// ============================================================================ +// Global Overrides - Remove Bootstrap padding/margins +// ============================================================================ + +// Remove navbar margin globally with maximum specificity +.navbar, +.navbar-default, +.navbar-static-top, +nav.navbar, +header .navbar, +.navbar-fixed-top, +.navbar-fixed-bottom + margin-bottom: 0 !important + margin-top: 0 !important + +// Additional Bootstrap navbar overrides +body .navbar + margin-bottom: 0 !important + +// Target specific header navbar +header + .navbar + margin-bottom: 0 !important + +// Remove container and page padding when showing redesigned project page +body:has(.project-show-redesign) + #project_container, + .container-fluid#project_container + padding: 0 !important + margin: 0 !important + max-width: 100% !important + + #page-contents, + .row#page-contents + margin: 0 !important + padding: 0 !important + + > div + padding: 0 !important + +// Direct child approach - more specific +#project_container + > .row#page-contents + > .col-xs-12, + > .col-sm-12, + > .col-md-12 + padding: 0 !important + + > .project-show-redesign + margin: 0 !important + +// Force remove padding from all Bootstrap column classes in project container +.project-show-redesign + ~ * + .container, + .container-fluid, + .row, + [class*="col-"] + padding-left: 0 !important + padding-right: 0 !important + +// ============================================================================ +// Main Container +// ============================================================================ +.project-show-redesign + width: 100% + min-height: 100vh + background-color: $gray-50 + font-family: $font-family + -webkit-font-smoothing: antialiased + -moz-osx-font-smoothing: grayscale + margin: 0 !important + + // Hide old project header elements + #project_masthead, + #project_header, + #project_icon, + #widgets, + #project_header_activity_indicator + display: none !important + + // Reset global styles that interfere + * + box-sizing: border-box + + // ============================================================================ + // Project Header Section + // ============================================================================ + .project-header-gradient + background: linear-gradient(135deg, $purple-dark 0%, $purple-primary 100%) + padding: 50px 16px 32px + position: relative + overflow: hidden + + // Decorative gradient circle + &::before + content: '' + position: absolute + top: -192px + right: -192px + width: 384px + height: 384px + background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%) + border-radius: 50% + + .header-content + margin: 0 auto + position: relative + z-index: 10 + + .header-main + display: flex + flex-direction: column + gap: 16px + + @media (min-width: 768px) + flex-direction: row + align-items: flex-start + justify-content: space-between + gap: 24px + + a, + button + font-family: $font-family + + // Desktop: Top-right user stats and button + .header-top-right + position: absolute + top: 49px + right: 16px + display: none + align-items: center + gap: 12px + z-index: 20 + + @media (min-width: 640px) + display: flex + right: 24px + + @media (min-width: 1024px) + right: 32px + + .stats-card + background: white + border-radius: 8px + padding: 12px 16px + box-shadow: $shadow-md + min-width: 140px + + .activity-indicator-wrapper + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 4px + min-width: 80px + + [class^='thirtyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + + .thirtyfive_project_activity_text + margin: 0 + width: auto + line-height: 1.2 + font-size: 12px + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card + .stats-inner + display: flex + align-items: center + gap: 12px + margin-bottom: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .user-count + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + + .i-use-this-btn + width: 100% + padding: 10px 16px + border-radius: 6px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + transition: all 0.2s + + &.using + background: $purple-primary + color: white + + &:hover + background: darken($purple-primary, 8%) + + &.not-using + background: $yellow-accent + color: $purple-dark + + &:hover + background: $yellow-hover + + svg + width: 16px + height: 16px + + // Project title and info section + .project-title-section + flex: 1 + min-width: 0 + max-width: 900px + + @media (min-width: 768px) + max-width: calc(100% - 280px) + + h1 + font-size: 32px + font-weight: 700 + color: white + line-height: 1.2 + letter-spacing: -0.02em + margin: 0 0 12px + + @media (min-width: 768px) + font-size: 48px + + @media (min-width: 1024px) + font-size: 48px + + .action-buttons + display: flex + flex-wrap: wrap + gap: 8px + margin-bottom: 20px + + .action-btn + display: inline-flex + align-items: center + gap: 6px + padding: 6px 12px + border-radius: 8px + font-size: 12px + font-weight: 500 + line-height: 1.25 + text-decoration: none !important + appearance: none + -webkit-appearance: none + background-image: none !important + box-shadow: none !important + border: 0 + cursor: pointer + transition: all 0.2s ease + + &:link, + &:visited, + &:hover, + &:active + color: white !important + text-decoration: none !important + + @media (min-width: 768px) + font-size: 14px + + svg + width: 14px + height: 14px + flex-shrink: 0 + stroke: currentColor + + // Outlined button style (for Settings and Report Duplicate) + &.action-btn-outline + background: rgba(255, 255, 255, 0.1) + border: 1px solid rgba(255, 255, 255, 0.2) + color: white !important + box-shadow: none + + &:hover + background: rgba(255, 255, 255, 0.2) + border-color: rgba(255, 255, 255, 0.2) + color: white !important + transform: none + + &:active + transform: none + background: rgba(255, 255, 255, 0.2) + + &:focus-visible + outline: 2px solid rgba(255, 255, 255, 0.55) + outline-offset: 2px + + .project-description + font-size: 16px + color: rgba(255, 255, 255, 0.9) + line-height: 1.6 + margin-bottom: 12px + + @media (min-width: 768px) + font-size: 17px + + .timestamp + font-size: 14px + color: rgba(255, 255, 255, 0.7) + line-height: 1.5 + + // Mobile: Bottom stats card + .header-mobile-stats + display: flex + gap: 12px + margin-top: 24px + + @media (min-width: 640px) + display: none + + .activity-card-mobile + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 4px + padding: 10px 12px + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + + [class^='twentyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + + .twentyfive_project_activity_text + margin: 0 + width: auto + min-height: 0 + font-size: 10px + line-height: 1.2 + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card-mobile + flex: 1 + display: flex + flex-direction: column + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + padding: 0 + overflow: hidden + + .stats-top + display: flex + align-items: center + gap: 8px + margin-bottom: 0 + padding: 10px 12px 6px + + svg + width: 16px + height: 16px + color: $yellow-accent + + .user-count + font-size: 18px + font-weight: 700 + color: $yellow-accent + + .users-label + font-size: 12px + color: rgba(255, 255, 255, 0.8) + + .i-use-this-btn-mobile + width: calc(100% - 20px) + margin: 0 10px 10px + padding: 8px 12px + border-radius: 8px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + line-height: 1.25 + + &.using + background: $purple-primary + color: white + + &.not-using + background: $yellow-accent + color: $purple-dark + + svg + width: 16px + height: 16px + + // ============================================================================ + // Main Content Area + // ============================================================================ + .project-content + margin: 0 auto + padding: 24px + + @media (min-width: 768px) + padding: 32px + + // ============================================================================ + // Stats Grid (3 columns) + // ============================================================================ + .stats-grid + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + + .stat-card + background: white + border-radius: 12px + padding: 20px 16px + text-align: center + box-shadow: $shadow-sm + transition: box-shadow 0.2s + + &:hover + box-shadow: $shadow-md + + .stat-value + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + margin-bottom: 8px + + @media (min-width: 768px) + font-size: 36px + + .stat-label + font-size: 14px + color: $gray-600 + font-weight: 500 + line-height: 1.2 + + // ============================================================================ + // Card Components + // ============================================================================ + .gradient-card + background: white + border-radius: 12px + // Enhanced shadow to match Figma design - more prominent + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + overflow: hidden + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + .card-content + padding: 24px + + @media (min-width: 768px) + padding: 32px + + h3 + font-size: 20px + font-weight: 700 + color: $gray-900 + line-height: 1.3 + margin: 0 0 16px + padding-bottom: 12px + border-bottom: 2px solid $gray-100 + + h4 + font-size: 16px + font-weight: 600 + color: $gray-900 + line-height: 1.4 + margin: 0 0 12px + + p + font-size: 15px + line-height: 1.6 + color: $gray-700 + margin: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + // ============================================================================ + // Desktop: 3-column grid + // ============================================================================ + .three-col-grid + display: none + + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + margin-bottom: 32px + + // ============================================================================ + // Mobile: Accordion cards + // ============================================================================ + .mobile-accordion + display: flex + flex-direction: column + gap: 16px + margin-bottom: 32px + + @media (min-width: 1024px) + display: none + + .accordion-card + .accordion-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 20px + background: none + border: none + cursor: pointer + text-align: left + + .header-left + display: flex + align-items: center + gap: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + + h3 + font-size: 18px + font-weight: 600 + color: $gray-900 + margin: 0 + + .chevron + width: 20px + height: 20px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content + padding: 0 20px 20px + display: none + + &.expanded + display: block + + // ============================================================================ + // Tags + // ============================================================================ + .tags-container + display: flex + flex-wrap: wrap + gap: 8px + margin-top: 12px + + .tag + padding: 6px 12px + background: $gray-100 + color: $gray-700 + font-size: 14px + border-radius: 6px + text-decoration: none + transition: all 0.15s + + &:hover + background: $purple-primary + color: white + + // ============================================================================ + // Nutshell List + // ============================================================================ + .nutshell-list + list-style: none + padding: 0 + margin: 0 + + li + display: flex + gap: 12px + padding: 12px 0 + font-size: 15px + line-height: 1.6 + border-top: 1px solid $gray-100 + + &:first-child + border-top: none + padding-top: 0 + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + margin-top: 2px + + .content + color: $gray-700 + + a + color: $purple-primary + font-weight: 600 + text-decoration: none + + &:hover + text-decoration: underline + + // ============================================================================ + // Quick Reference List + // ============================================================================ + .quick-ref-list + list-style: none + padding: 0 + margin: 0 + + li + display: flex + justify-content: space-between + align-items: flex-start + gap: 16px + padding: 12px 0 + font-size: 15px + border-bottom: 1px solid $gray-100 + + &:last-child + border-bottom: none + + .label + color: $gray-600 + font-weight: 500 + flex-shrink: 0 + + .value + color: $purple-primary + font-weight: 600 + text-align: right + + a + color: $purple-primary + text-decoration: none + display: inline-flex + align-items: center + gap: 4px + + &:hover + text-decoration: underline + + svg + width: 14px + height: 14px + + // ============================================================================ + // Licenses Section + // ============================================================================ + .licenses-section + margin-bottom: 32px + + .section-title + font-size: 24px + font-weight: 700 + color: $gray-900 + margin-bottom: 16px + + .license-card + .license-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 20px 24px + background: none + border: none + cursor: pointer + + .header-left + display: flex + align-items: center + gap: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + + h4 + font-size: 18px + font-weight: 600 + color: $purple-primary + margin: 0 + + .chevron + width: 20px + height: 20px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .license-content + display: none + padding: 0 24px 24px + + &.expanded + display: block + + @media (min-width: 1024px) + display: block + + .license-details-grid + display: grid + gap: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 24px + + .license-detail-card + border-radius: 8px + padding: 20px + + &.permitted + background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%) + + &.forbidden + background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%) + + &.required + background: linear-gradient(135deg, #fefce8 0%, #fef9c3 100%) + + h5 + font-weight: 600 + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + + ul + list-style: none + padding: 0 + + li + font-size: 14px + margin-bottom: 8px + display: flex + align-items: center + gap: 8px + + svg + width: 12px + height: 12px + + .license-disclaimer + margin-top: 24px + padding-top: 16px + border-top: 1px solid $gray-200 + text-align: center + + p + font-size: 13px + color: $gray-600 + font-style: italic + + .view-all-link + margin-top: 16px + text-align: right + + a + font-size: 14px + font-weight: 600 + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + // ============================================================================ + // Footer Navigation + // ============================================================================ + .footer-nav-section + background: white + border-radius: 16px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + margin-bottom: 24px + + @media (min-width: 640px) + padding: 32px + margin-bottom: 32px + + a + &:link, + &:visited, + &:hover, + &:active + text-decoration: none + + // Desktop: 4-column grid + .footer-nav-grid + display: none + + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(4, 1fr) + gap: 32px + + .nav-column + .column-header + display: flex + align-items: center + gap: 8px + margin-bottom: 12px + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + svg + width: 16px + height: 16px + color: $purple-primary + + h4 + font-size: 14px + font-weight: 600 + color: $gray-900 + margin: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 8px + + &:last-child + margin-bottom: 0 + + a + font-size: 14px + color: #5A2A82 !important + text-decoration: none + transition: color 0.15s + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + + // Mobile: Accordion + .footer-nav-accordion + display: flex + flex-direction: column + + @media (min-width: 768px) + display: none + + .accordion-item + border-bottom: 1px solid $gray-200 + + &:last-child + border-bottom: none + + .accordion-header-btn + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 12px 0 + background: none + border: none + cursor: pointer + + .header-left + display: flex + align-items: center + gap: 10px + + svg + width: 16px + height: 16px + color: $purple-primary + + span + font-size: 14px + font-weight: 600 + color: $gray-900 + + .chevron + width: 16px + height: 16px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content-footer + padding-bottom: 12px + padding-left: 24px + display: none + + &.expanded + display: block + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 6px + + a + font-size: 14px + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + +// ============================================================================ +// Dark Theme Overrides (Project Show Redesign) +// ============================================================================ +html.dark + .project-show-redesign + background-color: $purple-dark + color: #e2e8f0 + + .project-content + background: transparent + + .stats-grid + .stat-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .stat-value + color: $yellow-accent + + .stat-label + color: #94a3b8 + + .gradient-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .card-content + h3 + color: #f8fafc + border-bottom-color: rgba(159, 122, 186, 0.3) + + h4 + color: #f1f5f9 + + p + color: #cbd5e1 + + a + color: $yellow-accent + + .mobile-accordion + .accordion-card + .accordion-header + .header-left + svg + color: $yellow-accent + + h3 + color: #f8fafc + + .chevron + color: #94a3b8 + + .tags-container + .tag + background: rgba(90, 42, 130, 0.35) + color: #e2e8f0 + + &:hover + background: $yellow-accent + color: $purple-dark + + .nutshell-list + li + border-top-color: rgba(159, 122, 186, 0.25) + + svg + color: $yellow-accent + + .content + color: #cbd5e1 + + a + color: $yellow-accent + + .quick-ref-list + li + border-bottom-color: rgba(159, 122, 186, 0.25) + + .label + color: #94a3b8 + + .value + color: #e2e8f0 + + a + color: $yellow-accent + + .licenses-section + .section-title + color: #f8fafc + + .license-card + .license-header + .header-left + svg + color: $yellow-accent + + h4 + color: #f8fafc + + .chevron + color: #94a3b8 + + .license-content + .license-detail-card + &.permitted + background: rgba(22, 101, 52, 0.25) + + &.forbidden + background: rgba(153, 27, 27, 0.25) + + &.required + background: rgba(133, 77, 14, 0.25) + + h5, + li + color: #e2e8f0 + + .license-disclaimer + border-top-color: rgba(159, 122, 186, 0.25) + + p + color: #94a3b8 + + .view-all-link + a + color: $yellow-accent + + .footer-nav-section + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) + + svg + color: $yellow-accent + + h4 + color: #f8fafc + + ul + li + a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) + + .accordion-header-btn + .header-left + svg + color: $yellow-accent + + span + color: #f8fafc + + .chevron + color: #94a3b8 + + .accordion-content-footer + ul + li + a + color: $yellow-accent + + &:visited, + &:active + color: $yellow-accent + + // Legacy sections rendered inside redesign page (security + analysis summary) + .project_row + color: #e2e8f0 + + h2, h3, h4, h5 + color: #f8fafc !important + + p, small, td, th, li, span + color: #cbd5e1 + + a + color: $yellow-accent + + .well, + .activity_well, + .community_well, + #vulnerability-report .well + background-color: #2D1548 !important + border-color: rgba(159, 122, 186, 0.3) !important + + .right_border + border-right-color: rgba(159, 122, 186, 0.3) !important + + .security_blog_link + color: $yellow-accent !important + + #did_you_know + background-color: transparent !important + + h4 + color: #f8fafc !important + + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + ul, + li, + .indent, + p, + span + color: #cbd5e1 !important + + a + color: $yellow-accent !important + + &:hover + color: $yellow-hover !important + text-decoration: underline + + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + color: #cbd5e1 !important + + .title + h3 + color: #f8fafc !important + + i, + [class^='icon-'], + [class*=' icon-'] + color: $yellow-accent !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + + a + color: $yellow-accent !important + + .btn.btn-primary, + .btn.btn-large.btn-primary + background-color: $yellow-accent !important + border-color: $yellow-accent !important + color: $purple-dark !important + + &:hover, + &:focus, + &:active + background-color: $yellow-hover !important + border-color: $yellow-hover !important + color: $purple-dark !important + +// Final cascade guard for legacy styles overriding Did You Know dark theme +html.dark #page #project_container .project-show-redesign .project_row #did_you_know + background: transparent !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know h4 + color: #f8fafc !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know p, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know li, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .indent, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know span + color: #cbd5e1 !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know a + color: $yellow-accent !important diff --git a/app/views/layouts/responsive_project_layout.html.haml b/app/views/layouts/responsive_project_layout.html.haml index 63f832a98..b4d49b9a6 100644 --- a/app/views/layouts/responsive_project_layout.html.haml +++ b/app/views/layouts/responsive_project_layout.html.haml @@ -20,6 +20,6 @@ %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear - %footer.fluid_footer= render partial: 'layouts/partials/fluid_footer' - = render 'cookies/consent_banner' unless cookies[:cookie_consented] + %footer= render partial: 'layouts/partials/footer' + = render 'cookies/consent_banner' unless cookies[:cookie_consented] = render partial: 'layouts/partials/js_scripts' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index f6a029249..56b6dfcf5 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,7 +1,9 @@ :ruby content_for(:html_title) { t('.page_title', name: @project.name) } page_context[:select_top_menu_nav] = 'select_projects' - page_context[:select_footer_nav] = :project_summary + page_context[:select_footer_nav] = nil # Disable old footer navigation + page_context[:footer_menu_list] = nil # Disable old footer navigation + page_context[:page_header] = nil # Disable default header, using redesigned header instead - content_for :twitter_card do %meta{ content: 'summary', name: 'twitter:card' } @@ -12,75 +14,434 @@ %meta{ content: @project.logo.attachment.url(:med), name: 'twitter:image' } %meta{ content: 'https://www.openhub.net', name: 'twitter:domain' } -%div.separator-div - = project_analysis_timestamp(@project) - -.clear -= render partial: 'projects/show/is_a_duplicate', locals: { project: @project } - -.row.row-eq-height.margin_top_two.project_row - .col-md-4.project_summary_container - .well - %h4.text-left= t('.project_summary') - - size_breach = project_description_size_breached?(@project) - %section#project_summary{ itemprop: 'description', class: ('vertical_scroll_bar' if size_breach) } - - if @project.description.present? - = simple_format @project.description.strip_tags - - else - = t('.no_description') - = link_to t('.add_description'), edit_project_path(@project) - - %section#project_tags{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %h4.title= tag_icon_link(@project) - %p.tags - %span - - tags = @project.tag_list.split(' ') - - if tags.any? - = tag_links(tags) +.project-show-redesign{ itemscope: '', itemtype: 'http://schema.org/SoftwareSourceCode' } + / Project Header with Gradient + = render partial: 'projects/show/header_redesign' + + .project-content + / Stats Grid - 3 columns (Lines of Code, Contributors, Total Commits) + .stats-grid + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.code_total, delimiter: ',') + - else + N/A + .stat-label= t('.lines_of_code') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.committers_all_time) + - else + 0 + .stat-label= t('.contributors') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.commit_count) + - else + 0 + .stat-label= t('.total_commits') + + / Desktop: 3-column grid (Project Summary, In a Nutshell, Quick Reference) + .three-col-grid + / Project Summary + .gradient-card + .card-content + %h3= t('.project_summary') + - if @project.description.present? + %p= simple_format(@project.description.strip_tags) + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + - if @project.is_a_duplicate + %p.note + = t('.note') + There are two distinct + = link_to 'Mozilla Core', '#' + and + = link_to 'Mozilla Chrome', '#' + projects for complete coverage. + + %section#project_tags + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tagged_projects_path(tag: tag), class: 'tag' + - else + %span.tag + = t('.no_tags') + - if @project.edit_authorized? + = link_to t('.add_tags'), project_tags_path(@project) + + / In a Nutshell + .gradient-card + .card-content + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + .content + representing + = link_to "#{number_with_delimiter @analysis.code_total} lines of code", languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.main_language + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + .content + is + = link_to "mostly written in #{@analysis.main_language.nice_name}", languages_summary_project_analysis_path(@project, id: 'latest') + with average comment density + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "23 6 13.5 15.5 8.5 10.5 1 18" } + %polyline{ points: "17 6 23 6 23 12" } + .content + has a + %span.font-semibold mature codebase + maintained by a + %span.font-semibold very large development team + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %polyline{ points: "12 6 12 12 16 14" } + .content + took an estimated + = link_to "#{number_with_delimiter @analysis.man_years.ceil} years of effort", estimated_cost_project_path(@project) + (COCOMO model) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" } + %line{ x1: "16", y1: "2", x2: "16", y2: "6" } + %line{ x1: "8", y1: "2", x2: "8", y2: "6" } + %line{ x1: "3", y1: "10", x2: "21", y2: "10" } + .content + - if @analysis.first_commit_time? + starting + %span.font-semibold= @analysis.first_commit_time.strftime('%B %Y') + - if @analysis.last_commit_time? + , ending + %span.font-semibold= "#{time_ago_in_words(@analysis.last_commit_time)} ago" + - else + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference + .gradient-card + .card-content + %h3= t('.quick_reference.quick_reference') + %ul.quick-ref-list + - if @project.organization + %li + %span.label= t('.organization') + %span.value + = link_to @project.organization.name, organization_path(@project.organization) + + %li + %span.label= t('.homepage') + %span.value + - homepage_link = @project.decorate.sorted_link_list['Homepage']&.first + - if homepage_link + = link_to homepage_link.url, itemprop: 'url' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + = truncate(homepage_link.url.gsub(/^https?:\/\//, ''), length: 30) + - else + N/A + + %li + %span.label= t('.documentation') + %span.value + - doc_link = @project.decorate.sorted_link_list['Documentation']&.first + - if doc_link + = link_to doc_link.url do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M4 19.5A2.5 2.5 0 0 1 6.5 17H20" } + %path{ d: "M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" } + View Docs + - else + N/A + + %li + %span.label= t('.code_location') + %span.value + - if @project.enlistments.present? + = link_to truncate(@project.enlistments.first.code_location.url, length: 40), @project.enlistments.first.code_location.url + - else + = link_to t('.add_code_location'), project_enlistments_path(@project) + + %li + %span.label= t('.similar_projects') + %span.value + %div#similar_projects{ data: { project_id: @project.to_param } } + + %li + %span.label= t('.managers') + %span.value + - if @project.active_managers.present? + = link_to "#{@project.active_managers.count} managers", project_managers_path(@project) + - else + = link_to t('.become_first', what: @project.name), new_project_manager_path(@project) + + / Mobile: Collapsible accordion cards + .mobile-accordion + / Project Summary - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h3= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @project.description.present? + %p= simple_format(@project.description.strip_tags, {}, wrapper_tag: 'span') + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + %section#project_tags_mobile + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tagged_projects_path(tag: tag), class: 'tag' + - else + %span.tag= t('.no_tags') + + / In a Nutshell - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "12", y1: "20", x2: "12", y2: "10" } + %line{ x1: "18", y1: "20", x2: "18", y2: "4" } + %line{ x1: "6", y1: "20", x2: "6", y2: "16" } + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + / Add remaining factoids as in desktop version - else - = t('.no_tags') - - if @project.edit_authorized? - = link_to t('.add_tags'), project_tags_path(@project) - - if @project.project_badges.active.present? - %section#project_badges - %h4.title= link_to t('project_badges.index.title'), project_project_badges_path(@project) - = show_badges - = more_badges_link - - .col-md-4.nutshell_container - .well - - p_name = truncate_project_name(@project.name, 40) - %h4.nutshell_heading!= t('.in_a_nutshell', name: @project.name.length > 40 ? p_name : p_name + '...') - - if @analysis.present? - = render 'projects/show/factoids' - - else - = render partial: 'projects/show/no_analysis_summary' - .col-md-4.quick_reference_container - .well - = render partial: 'projects/show/quick_reference' - -.row.mezzo -= render partial: 'projects/show/licenses' - -.row.mezzo -.project_security - = render partial: 'projects/show/security' - -.row.mezzo -- if @analysis.empty? - .analysis_blank_container - .row.project_row - .col-md-12.blank_message_container - .col-md-8.analysis_alert_container - = render partial: 'projects/show/no_analysis_summary' - .col-md-4#proj_rating= render 'projects/show/community_rating', title: t('.community_rating'), score: @score - - if @project.enlistments.empty? && @analysis.empty? - = image_tag('sample_ohloh_analysis.jpg', class: 'col-md-12') -- else - = render partial: 'projects/show/analysis_summary' - -- if @scan_analytics.present? - .row.mezzo - #scan_data_row - .project_row= render partial: 'projects/show/scan_analytics_summary', collection: @scan_analytics, as: :scan_analytics + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + %h3= t('.quick_reference.quick_reference') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + %ul.quick-ref-list + / Same content as desktop version + - if @project.organization + %li + %span.label= t('.organization') + %span.value= link_to @project.organization.name, organization_path(@project.organization) + / Add remaining items + + / Licenses Section + = render partial: 'projects/show/licenses_redesign' + + / Project Security Section + = render partial: 'projects/show/security' + + / Code, Activity, Community Grid + - if @analysis.present? + = render partial: 'projects/show/analysis_summary' + + / Project Navigation Footer + .footer-nav-section + / Desktop: 4-column grid + .footer-nav-grid + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h4= t('.project_summary') + %ul + %li= link_to t('.news'), '#' + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), '#' + %li= link_to t('.related_projects'), similar_project_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %h4= t('.code_data') + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), '#security' + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %h4= t('.scm_data') + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %h4= t('.community_data') + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), '#' + %li= link_to t('.user_contributor_locations'), '#' + + / Mobile: Accordion + .footer-nav-accordion + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %span= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.news'), '#' + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), '#' + %li= link_to t('.related_projects'), similar_project_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %span= t('.code_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), '#security' + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %span= t('.scm_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span= t('.community_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), '#' + %li= link_to t('.user_contributor_locations'), '#' + +:javascript + function toggleAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } + + function toggleFooterAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml new file mode 100644 index 000000000..6283af5ef --- /dev/null +++ b/app/views/projects/show/_header_redesign.html.haml @@ -0,0 +1,128 @@ +.project-header-gradient + / Decorative gradient circle is handled by CSS + + / Top Right Stats & Button (Desktop only, hidden on mobile) + .header-top-right + .activity-indicator-wrapper + / Activity indicator will be rendered here + - project_activity_level_class(@project, :thirtyfive) + - project_activity_level_text(@project, :thirtyfive) + + .stats-card + .stats-inner + / Users icon + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + .user-count + = number_with_delimiter(@project.user_count) + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + + .header-content + .header-main + .project-title-section + %h1{ itemprop: 'name' } + = @project.name + + .action-buttons + = link_to settings_project_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "3" } + %path{ d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" } + = t('.settings') + + - if @project.is_a_duplicate + - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) + = link_to edit_project_duplicate_path(@project, @project.is_a_duplicate), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + = t('.edit_duplicate') + - else + = link_to new_project_duplicate_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M4 15s1-1 4-1 5 2 9 2V3s-4 0-6-2-9-1-3 1-4 1-4 1z" } + %line{ x1: "4", y1: "22", x2: "4", y2: "15" } + = t('.report_duplicate') + + %p.project-description{ itemprop: 'description' } + - if @project.description.present? + = truncate(@project.description.strip_tags, length: 200) + - else + = t('.no_description') + + %p.timestamp + = t('.analyzed', time: time_ago_in_words(@project.analysis_updated_or_project_created_time)) + = t('.based_on_code', time: time_ago_in_words(@project.best_analysis.try(:logged_at))) if @project.best_analysis.try(:logged_at) + + / Mobile Stats & Button (shown only on mobile) + .header-mobile-stats + .activity-card-mobile + - project_activity_level_class(@project, :twentyfive) + - project_activity_level_text(@project, :twentyfive) + + .stats-card-mobile + .stats-top + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span.user-count= number_with_delimiter(@project.user_count) + %span.users-label= t('.users') + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn-mobile.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + +/ Login Modal for non-logged in users +- unless logged_in? + .modal.fade#LoginModal{ 'aria-hidden': 'true', 'aria-labelledby': 'LoginModalLabel', role: 'dialog' } + .modal-dialog.modal-sm + .modal-content + .modal-header + %button.close{ 'aria-label': 'Close', 'data-dismiss': 'modal', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %h4.modal-title#LoginModalLabel= t('.login_required') + .model-body + = render 'sessions/sign_in' diff --git a/app/views/projects/show/_licenses_redesign.html.haml b/app/views/projects/show/_licenses_redesign.html.haml new file mode 100644 index 000000000..39ad61977 --- /dev/null +++ b/app/views/projects/show/_licenses_redesign.html.haml @@ -0,0 +1,88 @@ +- if @project.licenses.count > 0 + .licenses-section + %h2.section-title= t('.licenses') + + - @project.licenses.each do |license| + .license-card.gradient-card + %button.license-header{ onclick: "toggleLicense(this)", class: "license-toggle-#{license.id}" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" } + %h4= license.name + + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + + .license-content{ class: "license-content-#{license.id}" } + .license-details-grid + / Permitted Column + .license-detail-card.permitted + %h5 + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.permitted') + %ul + - license.permitted_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = p.name + + / Forbidden Column + .license-detail-card.forbidden + %h5 + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = t('.forbidden') + %ul + - license.forbidden_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = p.name + + / Required Column + .license-detail-card.required + %h5 + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + = t('.required') + %ul + - license.required_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + = p.name + + .license-disclaimer + %p= t('.disclaimer') + + .view-all-link + = link_to "#{t('.view_all_licenses')} →", project_licenses_path(@project) + +:javascript + function toggleLicense(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.classList.contains('expanded'); + + if (isExpanded) { + content.classList.remove('expanded'); + content.style.display = 'none'; + chevron.classList.remove('expanded'); + } else { + content.classList.add('expanded'); + content.style.display = 'block'; + chevron.classList.add('expanded'); + } + } diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 723000732..f9dd89f02 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -92,6 +92,16 @@ en: show: page_title: "The %{name} Open Source Project on Open Hub" project_summary: "Project Summary" + overview: "Overview" + news: "News" + settings: "Settings" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + languages: "Languages" + commits: "Commits" + contributors: "Contributors" + users: "Users" + security: "Security" no_description: "No description has been added for this project." add_description: "Add description" tags: "Tags" @@ -99,6 +109,33 @@ en: add_tags: "add some now" community_rating: "Community Rating" in_a_nutshell: "In a Nutshell, %{name}" + lines_of_code: "Lines of Code" + contributors: "Contributors" + total_commits: "Total Commits" + note: "Note:" + organization: "Organization" + homepage: "Homepage" + documentation: "Documentation" + code_location: "Code Location" + add_code_location_link: "Add code location" + similar_projects_label: "Similar Projects" + managers_label: "Managers" + become_first_manager: "Become the first manager of %{what}" + news: "News" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + code_data: "Code Data" + languages_label: "Languages" + cost_estimates: "Cost Estimates" + security_label: "Security" + scm_data: "SCM Data" + commits_label: "Commits" + contributors_label: "Contributors" + community_data: "Community Data" + users_label: "Users" + ratings_reviews: "Ratings & Reviews" + user_contributor_locations: "User & Contributor Locations" + licenses_title: "Licenses" site_features: did_you_know: 'Did You Know' analysis_summary: @@ -130,9 +167,27 @@ en: community_recent_committers: most_recent: "Most Recent Contributors" no_one_recently: "No one has contributed to this project recently" + header_redesign: + i_use_this: 'I Use This!' + you_use_this: 'You use this' + login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' + no_description: 'No description available' header: i_use_this: 'I Use This!' + you_use_this: 'You use this' login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' i_use_this: i_use_this: 'I Use This!' is_a_duplicate: @@ -223,6 +278,13 @@ en: title: 'Project Security' title_nvd: 'This Project has No vulnerabilities Reported Against it' blog_link: 'About Project Security' + licenses_redesign: + licenses: 'Licenses' + permitted: 'Permitted' + forbidden: 'Forbidden' + required: 'Required' + disclaimer: 'These details are provided for information only. No information here is legal advice and should not be used as such.' + view_all_licenses: 'View All Licenses' licenses: title: 'Licenses' all: 'All Licenses' From 3e0ecc18a43f2e7ff25fac11c1934f7f905413c7 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Wed, 4 Mar 2026 00:56:50 +0530 Subject: [PATCH 05/53] OTWO-7557 fix on the css for project show page --- app/assets/stylesheets/charts.sass | 8 +- app/assets/stylesheets/page.sass | 1 - .../stylesheets/project_show_redesign.sass | 1954 ++++++++++++++++- app/decorators/analysis/chart.rb | 2 +- .../analysis/commit_history_chart.rb | 13 +- .../analysis/contributor_history_chart.rb | 2 + app/decorators/vulnerability/version_chart.rb | 4 +- app/views/projects/show.html.haml | 26 +- .../show/_analysis_summary_redesign.html.haml | 104 + .../show/_community_rating_redesign.html.haml | 28 + ...unity_recent_committers_redesign.html.haml | 21 + .../show/_languages_redesign.html.haml | 20 + .../show/_licenses_redesign.html.haml | 35 +- app/views/projects/show/_security.html.haml | 100 +- .../analysis/options_based_on_type.yml | 12 +- config/locales/projects.en.yml | 28 +- 16 files changed, 2207 insertions(+), 151 deletions(-) create mode 100644 app/views/projects/show/_analysis_summary_redesign.html.haml create mode 100644 app/views/projects/show/_community_rating_redesign.html.haml create mode 100644 app/views/projects/show/_community_recent_committers_redesign.html.haml create mode 100644 app/views/projects/show/_languages_redesign.html.haml diff --git a/app/assets/stylesheets/charts.sass b/app/assets/stylesheets/charts.sass index 9de5968b6..3fd13b400 100644 --- a/app/assets/stylesheets/charts.sass +++ b/app/assets/stylesheets/charts.sass @@ -398,10 +398,16 @@ .highcharts-tooltip .highcharts-color-9 @include project-contributions-color-9-fill -#code_analysis_chart, #activity_chart, #community_chart +#code_analysis_chart, #community_chart .highcharts-graph @include code-analysis-chart-color-0-stroke +#activity_chart + .highcharts-graph, + .highcharts-point + stroke: #5A2A82 + fill: #5A2A82 + #committer_history_chart .highcharts-graph @include code-analysis-chart-color-0-stroke diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index 2a36dc40d..5172140b2 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -146,7 +146,6 @@ header padding-left: 0 margin-left: -1rem .thirtyfive_project_activity_text - margin-top: 5.5rem line-height: 1.3rem .thirtyfive_project_activity_level_na top: 1rem diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass index d07308547..8509ed849 100644 --- a/app/assets/stylesheets/project_show_redesign.sass +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -13,7 +13,10 @@ $yellow-hover: #FFCC4D $gray-50: #f9fafb $gray-100: #f3f4f6 $gray-200: #e5e7eb -$gray-600: #6b7280 +$gray-300: #d1d5db +$gray-400: #9ca3af +$gray-500: #6b7280 +$gray-600: #4b5563 $gray-700: #374151 $gray-900: #111827 @@ -110,6 +113,15 @@ body:has(.project-show-redesign) * box-sizing: border-box + // Override any legacy project container color styles + color: $gray-900 + + a + color: $purple-primary + + &:hover + color: $purple-primary + // ============================================================================ // Project Header Section // ============================================================================ @@ -179,13 +191,15 @@ body:has(.project-show-redesign) flex-direction: column align-items: center justify-content: center - gap: 4px + gap: 8px min-width: 80px [class^='thirtyfive_project_activity_level_'] position: static top: auto margin-left: 0 + transform: scale(1.25) + transform-origin: center center .thirtyfive_project_activity_text margin: 0 @@ -506,32 +520,40 @@ body:has(.project-show-redesign) box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) .card-content - padding: 24px + padding: 20px @media (min-width: 768px) - padding: 32px + padding: 24px h3 - font-size: 20px - font-weight: 700 - color: $gray-900 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important line-height: 1.3 - margin: 0 0 16px + margin: 0 0 16px !important padding-bottom: 12px - border-bottom: 2px solid $gray-100 + border-bottom: 1px solid $gray-200 h4 - font-size: 16px - font-weight: 600 - color: $gray-900 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important line-height: 1.4 - margin: 0 0 12px + margin: 0 0 12px !important p - font-size: 15px - line-height: 1.6 - color: $gray-700 - margin: 0 + font-size: 14px !important + line-height: 1.5 + color: $gray-700 !important + margin: 0 0 16px !important + + &:last-child + margin-bottom: 0 + + &.note + font-size: 12px !important + color: $gray-500 !important + font-style: italic ul list-style: none @@ -585,10 +607,10 @@ body:has(.project-show-redesign) color: $purple-primary h3 - font-size: 18px - font-weight: 600 - color: $gray-900 - margin: 0 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin: 0 !important .chevron width: 20px @@ -609,16 +631,33 @@ body:has(.project-show-redesign) // ============================================================================ // Tags // ============================================================================ + #project_tags, + #project_tags_mobile + margin-top: 24px + + h4 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + color: $gray-900 !important + .tags-container display: flex flex-wrap: wrap gap: 8px - margin-top: 12px .tag - padding: 6px 12px + padding: 4px 12px background: $gray-100 - color: $gray-700 + color: $gray-900 !important font-size: 14px border-radius: 6px text-decoration: none @@ -626,7 +665,7 @@ body:has(.project-show-redesign) &:hover background: $purple-primary - color: white + color: white !important // ============================================================================ // Nutshell List @@ -635,36 +674,47 @@ body:has(.project-show-redesign) list-style: none padding: 0 margin: 0 + display: flex + flex-direction: column + gap: 12px li display: flex gap: 12px - padding: 12px 0 - font-size: 15px - line-height: 1.6 + padding-top: 12px + font-size: 14px !important + line-height: 1.5 border-top: 1px solid $gray-100 + color: $gray-700 !important &:first-child border-top: none padding-top: 0 svg - width: 20px - height: 20px - color: $purple-primary + width: 16px + height: 16px + color: $purple-primary !important flex-shrink: 0 margin-top: 2px .content - color: $gray-700 + color: $gray-700 !important + flex: 1 a - color: $purple-primary + color: $purple-primary !important font-weight: 600 text-decoration: none &:hover text-decoration: underline + color: $purple-primary !important + + .font-semibold, + span.font-semibold + font-weight: 600 + color: $gray-700 !important // ============================================================================ // Quick Reference List @@ -673,42 +723,173 @@ body:has(.project-show-redesign) list-style: none padding: 0 margin: 0 + display: flex + flex-direction: column + gap: 12px li display: flex justify-content: space-between align-items: flex-start gap: 16px - padding: 12px 0 - font-size: 15px + padding-bottom: 12px + font-size: 14px !important border-bottom: 1px solid $gray-100 &:last-child border-bottom: none + padding-bottom: 0 .label - color: $gray-600 - font-weight: 500 + color: $gray-600 !important + font-weight: 500 !important + font-size: 14px !important flex-shrink: 0 + line-height: 1.5 .value - color: $purple-primary - font-weight: 600 + color: $purple-primary !important + font-weight: 600 !important + font-size: 14px !important text-align: right + flex: 1 + line-height: 1.5 a - color: $purple-primary + color: inherit !important text-decoration: none display: inline-flex align-items: center gap: 4px + font-size: 14px !important + font-weight: 600 !important &:hover text-decoration: underline + color: inherit !important svg - width: 14px - height: 14px + width: 12px + height: 12px + flex-shrink: 0 + color: currentColor !important + + i + font-size: 14px !important + color: $purple-primary !important + font-weight: 600 !important + + // Maximum specificity overrides for Quick Reference to prevent legacy interference + #page #project_container .project-show-redesign + .gradient-card .card-content h3 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + + .quick-ref-list li + font-size: 14px !important + + .label + font-size: 14px !important + font-weight: 500 !important + color: $gray-600 !important + + .value + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + i + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + .mobile-accordion .accordion-card .accordion-header .header-left h3 + font-size: 16px !important + font-weight: 600 !important + + // Languages section override + .languages-content .language-legend .language-item + font-size: 12px !important + + .language-name + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + font-size: 12px !important + + .language-percent + color: $gray-500 !important + font-size: 12px !important + + // Summary sections override + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + + // ============================================================================ + // Even more specific overrides for Activity card summary sections + // ============================================================================ + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin-bottom: 4px !important + + p.summary-timespan + font-size: 12px !important + color: #9ca3af !important + margin-bottom: 8px !important + + p.summary-stat + font-size: 12px !important + color: #374151 !important + margin-bottom: 4px !important + + span.stat-number + font-weight: 600 !important + color: #111827 !important + + p.summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important // ============================================================================ // Licenses Section @@ -716,55 +897,100 @@ body:has(.project-show-redesign) .licenses-section margin-bottom: 32px + @media (min-width: 768px) + margin-bottom: 48px + .section-title - font-size: 24px - font-weight: 700 + display: block + visibility: visible + opacity: 1 + font-size: 20px + font-weight: 600 color: $gray-900 - margin-bottom: 16px + margin-bottom: 12px + line-height: 1.3 + + @media (min-width: 768px) + font-size: 24px + margin-bottom: 16px .license-card + margin-bottom: 16px + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:last-child + margin-bottom: 0 + .license-header width: 100% display: flex align-items: center justify-content: space-between - padding: 20px 24px + padding: 16px background: none border: none cursor: pointer + text-align: left + + @media (min-width: 768px) + padding: 24px + + @media (min-width: 1024px) + cursor: default .header-left display: flex align-items: center - gap: 12px + gap: 8px svg width: 20px height: 20px color: $purple-primary + flex-shrink: 0 + + @media (min-width: 768px) + width: 20px + height: 20px h4 - font-size: 18px + font-size: 16px font-weight: 600 color: $purple-primary margin: 0 + line-height: 1.3 + + @media (min-width: 768px) + font-size: 18px .chevron width: 20px height: 20px - color: $gray-600 + color: $gray-500 transition: transform 0.2s + flex-shrink: 0 + + @media (min-width: 1024px) + display: none &.expanded transform: rotate(180deg) .license-content display: none - padding: 0 24px 24px + border-top: 1px solid $gray-200 + padding: 16px &.expanded display: block + @media (min-width: 768px) + padding: 24px + @media (min-width: 1024px) display: block @@ -778,18 +1004,59 @@ body:has(.project-show-redesign) .license-detail-card border-radius: 8px - padding: 20px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + @media (min-width: 768px) + padding: 20px &.permitted - background: linear-gradient(135deg, #faf5ff 0%, #f3e8ff 100%) + background: linear-gradient(to bottom right, #faf5ff 0%, #f3e8ff 100%) + + h5 + color: $purple-primary + + svg + color: $purple-primary + + li + color: $gray-700 + + svg + color: $purple-primary &.forbidden - background: linear-gradient(135deg, #fff7ed 0%, #ffedd5 100%) + background: linear-gradient(to bottom right, #fff7ed 0%, #fed7aa 100%) + + h5 + color: #c2410c + + svg + color: #c2410c + + li + color: $gray-700 + + svg + color: #c2410c &.required - background: linear-gradient(135deg, #fefce8 0%, #fef9c3 100%) + background: linear-gradient(to bottom right, #fefce8 0%, #fef3c7 100%) + + h5 + color: #a16207 + + svg + color: #a16207 + + li + color: $gray-700 + + svg + color: #a16207 h5 + font-size: 14px font-weight: 600 margin-bottom: 12px display: flex @@ -799,10 +1066,12 @@ body:has(.project-show-redesign) svg width: 16px height: 16px + flex-shrink: 0 ul list-style: none padding: 0 + margin: 0 li font-size: 14px @@ -810,34 +1079,269 @@ body:has(.project-show-redesign) display: flex align-items: center gap: 8px + line-height: 1.4 + + &:last-child + margin-bottom: 0 svg width: 12px height: 12px + flex-shrink: 0 + margin-top: 0 .license-disclaimer - margin-top: 24px + margin-top: 20px padding-top: 16px border-top: 1px solid $gray-200 text-align: center + @media (min-width: 768px) + margin-top: 24px + p - font-size: 13px - color: $gray-600 + font-size: 12px + color: $gray-500 font-style: italic + margin: 0 + line-height: 1.5 .view-all-link margin-top: 16px text-align: right a - font-size: 14px - font-weight: 600 - color: $purple-primary + font-size: 14px !important + font-weight: 500 !important + color: $purple-primary !important text-decoration: none + &:visited, + &:active + color: $purple-primary !important + &:hover text-decoration: underline + color: $purple-primary !important + + // ============================================================================ + // Project Security Section + // ============================================================================ + .security-section + margin-bottom: 32px + + @media (min-width: 768px) + margin-bottom: 48px + + .section-title + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + + @media (min-width: 768px) + font-size: 24px !important + margin-bottom: 16px !important + + .security-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 16px + + @media (min-width: 768px) + gap: 24px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .card-subtitle + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 16px !important + line-height: 1.4 + + @media (min-width: 768px) + font-size: 16px !important + + .security-link + color: $gray-900 !important + text-decoration: none + + &:hover + color: $purple-primary !important + text-decoration: underline + + .subtitle-detail + visibility: visible !important + opacity: 1 !important + display: block !important + font-size: 12px !important + font-weight: 400 !important + color: #6b7280 !important + margin-top: 4px + + @media (min-width: 768px) + display: inline !important + margin-left: 8px + + #vulnerability_version_chart + .chart-container + min-height: 250px + background: $gray-50 + border-radius: 8px + padding: 16px + + .no-data + text-align: center + padding: 60px 20px + color: $gray-600 + font-size: 14px + + // Vulnerability Report Indices + .vulnerability-indices + display: flex + flex-direction: column + gap: 24px + margin-top: 16px + + .index-section + .index-title + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 500 !important + color: #4b5563 !important + margin-bottom: 8px !important + line-height: 1.4 + + .gradient-bar-container + position: relative + height: 32px + margin-bottom: 8px + + @media (min-width: 768px) + height: 40px + + .gradient-bar + width: 100% + height: 100% + background: linear-gradient(to right, #5A2A82 0%, #ffb91a 50%, #f59e0b 100%) + border-radius: 8px + + .indicator-pointer + position: absolute + top: 0 + width: 4px + height: 100% + background: #1D0631 + border-radius: 9999px + cursor: pointer + transition: all 0.2s + + &:hover + background: $gray-900 + + .index-labels + display: flex + justify-content: space-between + font-size: 12px + color: #6b7280 + + .label-left, + .label-right + max-width: 45% + + .report-link-container + margin-top: 16px + text-align: center + + .report-link + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + // Did You Know Card + .did-you-know-card + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%) + border-radius: 12px + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + &.single + max-width: 600px + margin: 0 auto + + .card-content + padding: 20px + + @media (min-width: 768px) + padding: 24px + + .did-you-know-title + display: flex !important + visibility: visible !important + opacity: 1 !important + font-size: 16px !important + font-weight: 600 !important + color: #78350f !important + margin-bottom: 12px !important + align-items: center + gap: 8px + + @media (min-width: 768px) + font-size: 18px !important + + svg + width: 20px !important + height: 20px !important + color: #78350f !important + + .feature-list + list-style: disc + padding-left: 24px + margin: 0 + display: flex + flex-direction: column + gap: 8px + + li + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + color: #78350f !important + line-height: 1.5 + + .security-footer-link + margin-top: 16px + text-align: right + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px // ============================================================================ // Footer Navigation @@ -995,7 +1499,14 @@ body:has(.project-show-redesign) html.dark .project-show-redesign background-color: $purple-dark - color: #e2e8f0 + color: #e2e8f0 !important + + // Override any legacy color styles + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important .project-content background: transparent @@ -1017,17 +1528,24 @@ html.dark .card-content h3 - color: #f8fafc - border-bottom-color: rgba(159, 122, 186, 0.3) + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) h4 - color: #f1f5f9 + color: white !important p - color: #cbd5e1 + color: #cbd5e1 !important + + &.note + color: #94a3b8 !important a - color: $yellow-accent + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important .mobile-accordion .accordion-card @@ -1042,81 +1560,330 @@ html.dark .chevron color: #94a3b8 + #project_tags, + #project_tags_mobile + h4 + color: white !important + + svg + color: white !important + .tags-container .tag - background: rgba(90, 42, 130, 0.35) - color: #e2e8f0 + background: $purple-dark + color: $gray-300 !important &:hover background: $yellow-accent - color: $purple-dark + color: $purple-dark !important .nutshell-list li - border-top-color: rgba(159, 122, 186, 0.25) + border-top-color: rgba(90, 42, 130, 0.2) + color: $gray-300 !important svg - color: $yellow-accent + color: $yellow-accent !important .content - color: #cbd5e1 + color: $gray-300 !important a - color: $yellow-accent + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .font-semibold, + span.font-semibold + color: $gray-300 !important .quick-ref-list li - border-bottom-color: rgba(159, 122, 186, 0.25) + border-bottom-color: rgba(90, 42, 130, 0.2) .label - color: #94a3b8 + color: $gray-400 !important + font-size: 14px !important + font-weight: 500 !important .value - color: #e2e8f0 + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important a - color: $yellow-accent + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + i + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + // Maximum specificity dark mode overrides + html.dark #page #project_container .project-show-redesign + .gradient-card .card-content h3 + color: white !important + + .quick-ref-list li + .label + color: $gray-400 !important + font-size: 14px !important + + .value + color: $yellow-accent !important + font-size: 14px !important + + a, + i + color: $yellow-accent !important + font-size: 14px !important + + // Languages section dark mode override + .languages-content .language-legend .language-item + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + // Summary sections dark mode override + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Even more specific dark mode overrides for Activity card summary sections + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #6b7280 !important + + p.summary-stat + color: #d1d5db !important + + span.stat-number + color: white !important + + p.summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Maximum specificity for rating section + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + font-size: 12px !important + color: #374151 !important + + .rating-link + color: #5A2A82 !important + + .rating-display + .rating-value + font-size: 12px !important + color: #111827 !important + font-weight: 600 !important + + // Maximum specificity dark mode for rating section + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + color: #d1d5db !important + + .rating-link + color: #FFB91A !important + + .rating-display + .rating-value + color: white !important + + html.dark .licenses-section .section-title - color: #f8fafc + color: white .license-card + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) + .header-left svg color: $yellow-accent h4 - color: #f8fafc + color: $yellow-accent .chevron color: #94a3b8 .license-content - .license-detail-card - &.permitted - background: rgba(22, 101, 52, 0.25) + border-top-color: rgba(90, 42, 130, 0.3) - &.forbidden - background: rgba(153, 27, 27, 0.25) + .license-details-grid + .license-detail-card + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) - &.required - background: rgba(133, 77, 14, 0.25) + h5 + color: $yellow-accent - h5, - li - color: #e2e8f0 + svg + color: $yellow-accent + + li + color: $gray-300 + + svg + color: $yellow-accent + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) + + h5 + color: #fb923c + + svg + color: #fb923c + + li + color: $gray-300 + + svg + color: #fb923c + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) + + h5 + color: #facc15 + + svg + color: #facc15 + + li + color: $gray-300 + + svg + color: #facc15 .license-disclaimer - border-top-color: rgba(159, 122, 186, 0.25) + border-top-color: rgba(90, 42, 130, 0.3) p color: #94a3b8 .view-all-link a - color: $yellow-accent + font-weight: 500 !important + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .security-section + .section-title + color: white !important + + .card-subtitle + color: white !important + + .security-link + color: white !important + + &:hover + color: $yellow-accent !important + + .subtitle-detail + color: #9ca3af !important + + #vulnerability_version_chart + .chart-container + background: rgba(90, 42, 130, 0.15) + + .no-data + color: #94a3b8 + + .index-section + .index-title + color: #9ca3af !important + + .gradient-bar-container + .indicator-pointer + background: white !important + + &:hover + background: $yellow-accent !important + + .report-link-container + .report-link + color: $yellow-accent !important + + .did-you-know-card + background: linear-gradient(135deg, #4a3a15 0%, #3a2a10 100%) !important + + .did-you-know-title + color: #fcd34d !important + + svg + color: #fcd34d !important + + .feature-list + li + color: #fef08a !important + + .security-footer-link + a + color: $yellow-accent !important + + .no-data + color: #94a3b8 .footer-nav-section background: #2D1548 @@ -1281,3 +2048,1008 @@ html.dark #page #project_container .project-show-redesign .project_row #did_you_ html.dark #page #project_container .project-show-redesign .project_row #did_you_know a color: $yellow-accent !important + +// ============================================================================ +// Final Override Layer - Ensure our styles take precedence +// ============================================================================ +#project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: $gray-900 !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: $gray-700 !important + + .nutshell-list li + color: $gray-700 !important + + .content + color: $gray-700 !important + + a + color: $purple-primary !important + + .quick-ref-list li + .label + color: $gray-600 !important + + .value + color: $purple-primary !important + + a + color: $purple-primary !important + +// Dark mode final overrides +html.dark #project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: white !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: #cbd5e1 !important + + .nutshell-list li + color: $gray-300 !important + + .content + color: $gray-300 !important + + a + color: $yellow-accent !important + + .quick-ref-list li + .label + color: $gray-400 !important + + .value + color: $yellow-accent !important + + a + color: $yellow-accent !important + +// ============================================================================ +// Analysis Grid (Code, Activity, Community) +// ============================================================================ +.project-show-redesign + .analysis-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + margin-bottom: 48px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .analysis-card + display: flex + flex-direction: column + + .card-content + display: flex + flex-direction: column + height: 100% + flex: 1 + + .section-header + font-size: 18px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + @media (min-width: 768px) + font-size: 20px !important + + .chart-title + font-size: 14px !important + font-weight: 600 !important + margin-bottom: 12px + display: block + + a + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + color: $purple-primary !important + + .subsection-title + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-top: 16px + margin-bottom: 8px + + .chart-container + min-height: 160px + background: $gray-50 + border-radius: 8px + padding: 8px + margin-bottom: 16px + overflow: hidden + + @media (min-width: 768px) + min-height: 180px + + &.activity-chart + background: #f3f3f5 + padding: 12px + + #code_history_chart.chart-container + background: transparent + border-radius: 0 + padding: 0 + min-height: 200px + + #community_chart + margin-bottom: 16px + + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .highcharts-series-1 + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px + + // Chart header with zoom controls + .chart-header + display: flex + align-items: flex-start + justify-content: space-between + margin-bottom: 12px + gap: 8px + + .chart-title + margin-bottom: 0 + + .zoom-controls + display: flex + align-items: center + gap: 4px + flex-shrink: 0 + + .zoom-label + font-size: 12px + color: $gray-400 + margin-right: 4px + + .zoom-btn + padding: 2px 6px + border-radius: 4px + font-size: 12px + border: none + background: transparent + color: $gray-500 + cursor: pointer + transition: all 0.15s + + &:hover + color: $purple-primary + + &.active + background: $purple-primary + color: white + font-weight: 600 + + // Summary Grid (30 day, 12 month) + .summary-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-top: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + gap: 20px + + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 2px !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + margin-bottom: 6px !important + line-height: 1.25 + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + margin-bottom: 2px !important + line-height: 1.3 + + &:last-of-type + margin-bottom: 0 !important + + &.thirty-day-summary + .summary-header + margin-bottom: 1px !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 1px !important + line-height: 1.25 + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + margin-bottom: 8px !important + line-height: 1.4 + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + +// ============================================================================ +// Summary Cascade Guard (override generic gradient-card paragraph rules) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + margin-top: 0 !important + + .summary-timespan, + .summary-stat, + .summary-change + margin-top: 0 !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 2px !important + + .summary-change + margin-bottom: 8px !important + +html.dark #project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + color: white !important + + .summary-timespan + color: #94a3b8 !important + + .summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Languages + Contributors Cascade Guard (Redesign only) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + display: flex !important + align-items: flex-start !important + gap: 16px !important + margin-top: 8px !important + + .language-pie-container + width: 90px !important + height: 90px !important + flex: 0 0 90px !important + + .language-pie-chart + width: 90px !important + height: 90px !important + display: block !important + + .language-legend + flex: 1 1 auto !important + min-width: 0 !important + display: flex !important + flex-direction: column !important + gap: 6px !important + + .language-item + display: flex !important + align-items: center !important + gap: 8px !important + min-width: 0 !important + + .language-name + flex: 1 1 auto !important + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $gray-700 !important + + a + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + + .language-percent + flex: 0 0 auto !important + margin-left: auto !important + color: $gray-500 !important + + .community-chart-wrap + .col-md-12.manage_padding + padding: 0 !important + + .contributors-grid + display: grid !important + grid-template-columns: repeat(2, minmax(0, 1fr)) !important + column-gap: 8px !important + row-gap: 6px !important + margin-top: 6px !important + + .contributor-item + display: flex !important + align-items: center !important + gap: 6px !important + min-width: 0 !important + + .contributor-avatar + width: 24px !important + height: 24px !important + border-radius: 50% !important + flex: 0 0 24px !important + + .contributor-name + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + .language-legend + .language-item + .language-name + color: $gray-300 !important + + a + color: $yellow-accent !important + + .language-percent + color: $gray-400 !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + // Languages Section + .languages-content + display: flex + align-items: flex-start + gap: 16px + margin-top: 8px + + .language-pie-container + flex-shrink: 0 + width: 90px + height: 90px + + .language-pie-chart + width: 100% + height: 100% + display: block + + .language-legend + flex: 1 + display: flex + flex-direction: column + gap: 6px + + .language-item + display: flex + align-items: center + gap: 8px + font-size: 12px !important + + .language-color-box + width: 10px + height: 10px + border-radius: 2px + flex-shrink: 0 + + .language-name + flex: 1 + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + text-decoration: none + font-size: 12px !important + + &:hover + text-decoration: underline + color: $purple-primary !important + + .language-percent + color: $gray-500 !important + font-weight: 500 + margin-left: auto + font-size: 12px !important + + // Contributors Grid + .contributors-grid + display: grid + grid-template-columns: repeat(2, 1fr) + column-gap: 8px + row-gap: 6px + margin-top: 6px + + .contributor-item + display: flex + align-items: center + gap: 6px + + .contributor-avatar + width: 24px + height: 24px + border-radius: 50% + flex-shrink: 0 + + .contributor-name + font-size: 12px !important + line-height: 1.2 + color: $purple-primary + text-decoration: none + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + &:hover + text-decoration: underline + + // Ratings Section + .ratings-section + margin-top: 12px + padding-top: 12px + border-top: 1px solid $gray-200 + + .rating-content + .rating-info + font-size: 12px !important + line-height: 1.3 + color: $gray-700 + margin-bottom: 2px + + .rating-link + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + .rating-display + display: flex + align-items: center + gap: 6px + + .stars-container + display: inline-block + height: 23px + line-height: 23px + overflow: hidden + + .rating_stars, + [id$='_rating_stars'] + display: inline-block + height: 23px + line-height: 1 + + // Allow the rating stars helper's inline styles to work + span[style*='float: left'] + float: left !important + display: inline !important + height: 23px + + .rating-value + font-size: 12px !important + color: $gray-900 !important + font-weight: 600 !important + +// ============================================================================ +// Dark Mode - Analysis Grid +// ============================================================================ +html.dark .project-show-redesign + .section-header + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) + + .chart-title + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .subsection-title + color: white !important + + .chart-container + background: rgba(90, 42, 130, 0.15) + + &.activity-chart + background: #1D0631 + + #code_history_chart.chart-container + background: transparent + + #community_chart + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .highcharts-series-1 + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .zoom-controls + .zoom-label + color: #94a3b8 + + .zoom-btn + color: #94a3b8 + + &:hover + color: $yellow-accent + + &.active + background: $yellow-accent + color: #1D0631 + + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + .languages-content + .language-legend + .language-item + font-size: 12px !important + + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + &:hover + color: $yellow-accent !important + text-decoration: underline + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) + + .rating-content + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Ratings Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + margin-top: 12px !important + padding-top: 12px !important + border-top: 1px solid $gray-200 !important + + .subsection-title + margin-top: 0 !important + margin-bottom: 8px !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + + .rating-content + display: flex !important + flex-direction: column !important + gap: 2px !important + + .rating-top-row + display: flex !important + align-items: flex-start !important + justify-content: space-between !important + gap: 12px !important + + .rating-main + flex: 1 1 auto !important + min-width: 0 !important + + .rating-action + flex: 0 0 auto !important + text-align: right !important + white-space: nowrap !important + + .rating-link + display: inline-flex !important + align-items: center !important + justify-content: flex-end !important + gap: 6px !important + + .rating-action-icon + width: 12px !important + height: 12px !important + flex-shrink: 0 !important + + .rating-info + margin: 0 !important + font-size: 12px !important + line-height: 1.35 !important + color: $gray-700 !important + + &.rating-secondary + margin-top: 4px !important + + .rating-link + color: $purple-primary !important + text-decoration: none !important + font-weight: 500 !important + + &:hover + color: $purple-primary !important + text-decoration: underline !important + + .rating-display + display: flex !important + align-items: center !important + gap: 6px !important + margin-top: 2px !important + + .stars-container + display: inline-block !important + height: 23px !important + line-height: 23px !important + overflow: hidden !important + + .rating-value + font-size: 12px !important + line-height: 1.2 !important + font-weight: 600 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) !important + + .subsection-title + color: white !important + + .rating-content + .rating-top-row + .rating-action + .rating-info + color: $gray-300 !important + + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Dark Theme Final Guard (Licenses + Graphs) +// ============================================================================ +html.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + // Graph surfaces + chart typography + #activity_chart, + #community_chart, + #loc_chart, + #vulnerability_version_chart + .highcharts-background + fill: transparent !important + + .highcharts-plot-background, + .highcharts-plot-border, + .highcharts-label-box + fill: none !important + + .highcharts-axis-line, + .highcharts-tick, + .highcharts-grid-line + stroke: rgba(148, 163, 184, 0.35) !important + + .highcharts-axis-labels text, + .highcharts-axis-title, + .highcharts-legend-item text, + .highcharts-title, + .highcharts-subtitle + fill: #cbd5e1 !important + + .highcharts-tooltip-box + fill: #2D1548 !important + stroke: #5A2A82 !important + + .highcharts-tooltip text + fill: #f8fafc !important + +// ============================================================================ +// Summary Typography Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + margin: 0 0 2px 0 !important + color: $gray-900 !important + + p.summary-timespan, + p.summary-stat, + p.summary-change + font-size: 12px !important + line-height: 1.35 !important + margin-top: 0 !important + + p.summary-timespan + color: $gray-400 !important + margin-bottom: 6px !important + + p.summary-stat + color: $gray-700 !important + margin-bottom: 2px !important + + p.summary-change + margin-bottom: 8px !important + + .stat-number + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #94a3b8 !important + + p.summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Dark Mode Compatibility Guard (html.dark + body.dark) +// ============================================================================ +html.dark #project_container .project-show-redesign, +body.dark #project_container .project-show-redesign, +.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-detail-card + &.permitted, + &.forbidden, + &.required + border: 1px solid rgba(159, 122, 186, 0.25) !important + + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) !important + + h5, + h5 svg, + li svg + color: $yellow-accent !important + + li + color: $gray-300 !important + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) !important + + h5, + h5 svg, + li svg + color: #fb923c !important + + li + color: $gray-300 !important + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) !important + + h5, + h5 svg, + li svg + color: #facc15 !important + + li + color: $gray-300 !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + .footer-nav-section + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + svg + color: $yellow-accent !important + + h4 + color: #f8fafc !important + + ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + .accordion-header-btn + .header-left + svg + color: $yellow-accent !important + + span + color: #f8fafc !important + + .chevron + color: #94a3b8 !important + + .accordion-content-footer ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important diff --git a/app/decorators/analysis/chart.rb b/app/decorators/analysis/chart.rb index 193134c21..1e3b7015b 100644 --- a/app/decorators/analysis/chart.rb +++ b/app/decorators/analysis/chart.rb @@ -18,7 +18,7 @@ def series_and_range_data(default_options) end def first_ticks - series.first.ticks + series.first&.ticks end def min_month_as_ticks diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 942eadb86..6541fb0c5 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -9,9 +9,11 @@ def initialize(analysis) end def data - series_and_range_data(@defaults) + chart_data = series_and_range_data(@defaults) .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) .deep_merge(chart_watermark) + + apply_commits_palette(chart_data) end private @@ -25,6 +27,15 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.commits }] end + + def apply_commits_palette(chart_data) + series_config = chart_data[:series] || [] + series_config[0][:color] = '#2f7ed8' if series_config[0] + series_config[1][:color] = '#2f7ed8' if series_config[1] + chart_data + end end diff --git a/app/decorators/analysis/contributor_history_chart.rb b/app/decorators/analysis/contributor_history_chart.rb index d93c7bb05..e97911ba3 100644 --- a/app/decorators/analysis/contributor_history_chart.rb +++ b/app/decorators/analysis/contributor_history_chart.rb @@ -27,6 +27,8 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.contributors }] end end diff --git a/app/decorators/vulnerability/version_chart.rb b/app/decorators/vulnerability/version_chart.rb index dcaea58bd..aa2706ae3 100644 --- a/app/decorators/vulnerability/version_chart.rb +++ b/app/decorators/vulnerability/version_chart.rb @@ -27,7 +27,7 @@ def set_xaxis end def series - color_map = ['#0c1c33', '#183867', '#4b6b9a', '#a5b5cd', '#cce1e9'] + color_map = ['#4a148c', '#7b1fa2', '#9c27b0', '#ba68c8', '#ce93d8'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index] = { name: "CVE #{legend[0]}", @@ -41,7 +41,7 @@ def series end def bdsa_series - bdsa_color_map = ['rgb(90,16,12)', 'rgb(156,37,31)', 'rgb(231,140,135)', 'rgb(154,155,156)', '#D3D3D3'] + bdsa_color_map = ['#8b0000', '#c62828', '#f57c00', '#fbc02d', '#9e9e9e'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index + 5] = { name: "BDSA #{legend[0]}", diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 56b6dfcf5..879072ea9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -77,7 +77,7 @@ - tags = @project.tag_list.split(' ') - if tags.any? - tags.each do |tag| - = link_to tag, tagged_projects_path(tag: tag), class: 'tag' + = link_to tag, tags_path(names: tag), class: 'tag' - else %span.tag = t('.no_tags') @@ -246,7 +246,7 @@ - tags = @project.tag_list.split(' ') - if tags.any? - tags.each do |tag| - = link_to tag, tagged_projects_path(tag: tag), class: 'tag' + = link_to tag, tags_path(names: tag), class: 'tag' - else %span.tag= t('.no_tags') @@ -305,7 +305,7 @@ / Code, Activity, Community Grid - if @analysis.present? - = render partial: 'projects/show/analysis_summary' + = render partial: 'projects/show/analysis_summary_redesign' / Project Navigation Footer .footer-nav-section @@ -319,9 +319,9 @@ %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } %h4= t('.project_summary') %ul - %li= link_to t('.news'), '#' + %li= link_to t('.news'), project_rss_subscriptions_path(@project) %li= link_to t('.settings'), settings_project_path(@project) - %li= link_to t('.sharing_widgets'), '#' + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) %li= link_to t('.related_projects'), similar_project_path(@project) .nav-column @@ -333,7 +333,7 @@ %ul %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) - %li= link_to t('.security.title'), '#security' + %li= link_to t('.security.title'), security_project_path(@project) .nav-column .column-header @@ -357,8 +357,8 @@ %h4= t('.community_data') %ul %li= link_to t('.users'), users_project_path(@project) - %li= link_to t('.ratings_reviews'), '#' - %li= link_to t('.user_contributor_locations'), '#' + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) / Mobile: Accordion .footer-nav-accordion @@ -374,9 +374,9 @@ %polyline{ points: "6 9 12 15 18 9" } .accordion-content-footer{ style: "display: none;" } %ul - %li= link_to t('.news'), '#' + %li= link_to t('.news'), project_rss_subscriptions_path(@project) %li= link_to t('.settings'), settings_project_path(@project) - %li= link_to t('.sharing_widgets'), '#' + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) %li= link_to t('.related_projects'), similar_project_path(@project) .accordion-item @@ -392,7 +392,7 @@ %ul %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) - %li= link_to t('.security.title'), '#security' + %li= link_to t('.security.title'), security_project_path(@project) .accordion-item %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } @@ -424,8 +424,8 @@ .accordion-content-footer{ style: "display: none;" } %ul %li= link_to t('.users'), users_project_path(@project) - %li= link_to t('.ratings_reviews'), '#' - %li= link_to t('.user_contributor_locations'), '#' + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) :javascript function toggleAccordion(button) { diff --git a/app/views/projects/show/_analysis_summary_redesign.html.haml b/app/views/projects/show/_analysis_summary_redesign.html.haml new file mode 100644 index 000000000..73d21d806 --- /dev/null +++ b/app/views/projects/show/_analysis_summary_redesign.html.haml @@ -0,0 +1,104 @@ +/ Code, Activity, Community Grid +.analysis-grid + / Code Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.code') + + %h4.chart-title + = link_to t('.lines_of_code'), languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.present? + - chart_url = lines_of_code_project_analysis_path(@project, id: 'latest') + .chart.watermark440#loc_chart{ datasrc: chart_url, style: 'width: 100%; height: 200px;' } + - else + %p.no-data= t('.no_code_locations') + + %h4.subsection-title= t('.languages') + = render partial: 'projects/show/languages_redesign' + + / Activity Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.activity') + + .chart-header + %h4.chart-title + = link_to t('.commits_per_month'), summary_project_commits_path(@project) + + + - if @analysis.present? + - chart_url = commits_history_project_analysis_path(@project, id: 'latest') + #activity_chart.chart.watermark440{ datasrc: chart_url, style: 'width: 100%; min-height: 210px' } + - else + %p.no-data= t('.no_commits') + + .summary-grid + .summary-section.thirty-day-summary + - thirty_day_summary = @analysis&.thirty_day_summary + %h5.summary-header= t('.thirty_day_summary') + %p.summary-timespan + - if thirty_day_summary && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 30.days).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.commits_count.to_i) + = t('.commits') + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.committer_count.to_i) + = t('.contributors') + + .summary-section + %h5.summary-header= t('.twelve_month_summary') + %p.summary-timespan + - if @analysis.twelve_month_summary.present? && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 12.months).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + - if @analysis.twelve_month_summary.present? + - previous_summary = @analysis.previous_twelve_month_summary + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.commits_count) + Commits + - if previous_summary&.data? + - diff = previous_summary.commits_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_commits = previous_summary.commits_count + - if prev_commits.to_i > 0 + (#{(diff.abs.to_f / prev_commits.to_f * 100).floor}%) + from previous 12 + + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.committer_count) + Contributors + - if previous_summary&.data? + - diff = previous_summary.committers_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_committers = previous_summary.committer_count + - if prev_committers.to_i > 0 + (#{(diff.abs.to_f / prev_committers.to_f * 100).floor}%) + from previous 12 months + + / Community Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.community') + + .community-chart-wrap + = render partial: 'projects/show/committers_per_month_chart' + + %h4.subsection-title= t('.most_recent_contributors') + = render partial: 'projects/show/community_recent_committers_redesign' + + .ratings-section + %h4.subsection-title= t('.ratings') + = render partial: 'projects/show/community_rating_redesign', locals: { score: @score } diff --git a/app/views/projects/show/_community_rating_redesign.html.haml b/app/views/projects/show/_community_rating_redesign.html.haml new file mode 100644 index 000000000..6095a4a7a --- /dev/null +++ b/app/views/projects/show/_community_rating_redesign.html.haml @@ -0,0 +1,28 @@ +.rating-content + .rating-top-row + .rating-main + - if @project.ratings.any? + %p.rating-info + = link_to t((@project.ratings.count == 1 ? 'projects.show.community_rating.one_user_rate' : 'projects.show.community_rating.users_rate'), n: @project.ratings.count), summary_project_reviews_path(@project), class: 'rating-link' + + .rating-display + .stars-container + != rating_stars('average_rating_stars', @project.rating_average.to_f || 0) + %span.rating-value + = "#{number_with_precision(@project.rating_average || 0, precision: 1)} /5.0" + - else + %p.rating-info= t('projects.show.community_rating.be_the_first') + + .rating-action + %p.rating-info + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.click_to_add') + + - unless current_user && Review.by_account(current_user).for_project(@project).exists? + %p.rating-info.rating-secondary + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.review_this_project') diff --git a/app/views/projects/show/_community_recent_committers_redesign.html.haml b/app/views/projects/show/_community_recent_committers_redesign.html.haml new file mode 100644 index 000000000..46aea669e --- /dev/null +++ b/app/views/projects/show/_community_recent_committers_redesign.html.haml @@ -0,0 +1,21 @@ +- if @analysis && @analysis.all_time_summary + - recent_contributors = @analysis.all_time_summary.recent_contribution_persons + - if recent_contributors.any? + .contributors-grid + - recent_contributors.take(8).each do |p| + :ruby + contrib_id = if p.account_id + Contribution.generate_id_from_project_id_and_account_id(@project.id, p.account_id) + else + p.id + end + person_name = obfuscate_email(p.person_name) + truncated_name = person_name.reverse.truncate(20).reverse + + .contributor-item + = image_tag avatar_img_path(p, 24), class: 'contributor-avatar', alt: person_name + = link_to truncated_name, project_contributor_path(@project.id, contrib_id), class: 'contributor-name', title: person_name + - else + %p.no-data= t('.no_one_recently') +- else + %p.no-data= t('.no_contributors') diff --git a/app/views/projects/show/_languages_redesign.html.haml b/app/views/projects/show/_languages_redesign.html.haml new file mode 100644 index 000000000..e7f861312 --- /dev/null +++ b/app/views/projects/show/_languages_redesign.html.haml @@ -0,0 +1,20 @@ +- if @analysis && !@analysis.empty? + :ruby + languages_image_options = { project_id: @project.to_param, id: 'latest', height: 90, width: 90 } + language_data = Analysis::LanguagePercentages.new(@analysis).collection + + .languages-content + .language-pie-container + = image_tag languages_project_analysis_url(languages_image_options), class: 'language-pie-chart' + + .language-legend + - language_data.take(5).each do |id, name, attr| + - percent = attr[:percent] > 0 ? attr[:percent] : '<1' + - color = attr[:color] + .language-item + .language-color-box{ style: "background-color: ##{color}" } + %span.language-name + = id ? link_to(name, language_path(id)) : name + %span.language-percent= "#{percent}%" +- else + %p.no-data= t('.no_source') diff --git a/app/views/projects/show/_licenses_redesign.html.haml b/app/views/projects/show/_licenses_redesign.html.haml index 39ad61977..a1fe0ec22 100644 --- a/app/views/projects/show/_licenses_redesign.html.haml +++ b/app/views/projects/show/_licenses_redesign.html.haml @@ -4,13 +4,13 @@ - @project.licenses.each do |license| .license-card.gradient-card - %button.license-header{ onclick: "toggleLicense(this)", class: "license-toggle-#{license.id}" } + %button.license-header{ onclick: "toggleLicense(this)", class: "license-toggle-#{license.id}", type: "button" } .header-left - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %path{ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" } %h4= license.name - %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %polyline{ points: "6 9 12 15 18 9" } .license-content{ class: "license-content-#{license.id}" } @@ -18,22 +18,22 @@ / Permitted Column .license-detail-card.permitted %h5 - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } = t('.permitted') %ul - license.permitted_license_permissions.each do |p| %li{ 'data-tipso': p.description, title: p.description } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } = p.name / Forbidden Column .license-detail-card.forbidden %h5 - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %circle{ cx: "12", cy: "12", r: "10" } %line{ x1: "15", y1: "9", x2: "9", y2: "15" } %line{ x1: "9", y1: "9", x2: "15", y2: "15" } @@ -41,7 +41,7 @@ %ul - license.forbidden_license_permissions.each do |p| %li{ 'data-tipso': p.description, title: p.description } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %circle{ cx: "12", cy: "12", r: "10" } %line{ x1: "15", y1: "9", x2: "9", y2: "15" } %line{ x1: "9", y1: "9", x2: "15", y2: "15" } @@ -50,18 +50,18 @@ / Required Column .license-detail-card.required %h5 - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %circle{ cx: "12", cy: "12", r: "10" } %line{ x1: "12", y1: "16", x2: "12", y2: "12" } - %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %path{ d: "M12 8h.01" } = t('.required') %ul - license.required_license_permissions.each do |p| %li{ 'data-tipso': p.description, title: p.description } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } %circle{ cx: "12", cy: "12", r: "10" } %line{ x1: "12", y1: "16", x2: "12", y2: "12" } - %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %path{ d: "M12 8h.01" } = p.name .license-disclaimer @@ -72,6 +72,11 @@ :javascript function toggleLicense(button) { + // Only toggle on mobile (screen width < 1024px) + if (window.innerWidth >= 1024) { + return; + } + const content = button.nextElementSibling; const chevron = button.querySelector('.chevron'); const isExpanded = content.classList.contains('expanded'); diff --git a/app/views/projects/show/_security.html.haml b/app/views/projects/show/_security.html.haml index 0391ab395..ea15f7392 100644 --- a/app/views/projects/show/_security.html.haml +++ b/app/views/projects/show/_security.html.haml @@ -1,18 +1,84 @@ - blog_url = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Project-Security' -.row.project_row - - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report - .col-md-12.security_title_container - %h4.security-panel-title= t('.title') - .clearfix - .row-eq-height - .col-md-4#vulnerability_per_version - = render partial: 'projects/show/vulnerabilities_per_version' - .col-md-4#vulnerability-report - .well= render partial: 'projects/show/vulnerability_report' - .col-md-4#did_you_know - = render 'projects/show/site_features' - = link_to t('.blog_link'), blog_url, class: 'security_blog_link', target: '_blank' - - else - .col-md-12#did_you_know - %h4= t('.title_nvd') - = render 'projects/show/site_features' +- if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report + #security.security-section + %h2.section-title= t('.title', default: 'Project Security') + + .security-grid + / Vulnerabilities per Version Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerabilities_per_version.title', default: 'Vulnerabilities per Version'), security_project_path(@project), class: 'security-link' + %span.subtitle-detail= t('.vulnerabilities_per_version.title_detail', default: '(Most recent versions)') + - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) + - chart_url = recent_vulnerabilities_version_chart_project_path(@project) + #vulnerability_version_chart.chart-container{ data: { src: chart_url } } + - else + %p.no-data= t('vulnerabilities.index.no_vulnerability') + + / Vulnerability Report Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerability_report.title', default: 'Vulnerability Report'), security_project_path(@project), class: 'security-link' + + - pvr = @project.project_vulnerability_report + - if pvr + - pvr_link = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-About-the-Project-Vulnerability-Report' + + .vulnerability-indices + / Security Confidence Index + .index-section + .index-title= t('.vulnerability_report.pss.title', default: 'Security Confidence Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr).to_f}%", 'data-tipso': pss_content(pvr), 'data-tipso-title': t('.vulnerability_report.pss.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pss.poor') + %span.label-right= t('.vulnerability_report.pss.good') + + / Vulnerability Exposure Index + .index-section + .index-title= t('.vulnerability_report.pvs.title', default: 'Vulnerability Exposure Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr, pvs: true).to_f}%", 'data-tipso': pvs_content(pvr), 'data-tipso-title': t('.vulnerability_report.pvs.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pvs.poor') + %span.label-right= t('.vulnerability_report.pvs.good') + + .report-link-container + = link_to t('.vulnerability_report.blog_link', default: 'About Project Vulnerability Report'), pvr_link, class: 'report-link', target: '_blank' + - else + %p.no-data= t('.vulnerability_report.no_data') + + / Did You Know Card + .did-you-know-card + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe + + .security-footer-link + = link_to "#{t('.blog_link', default: 'About Project Security')} →", blog_url, target: '_blank' + +- else + #security.security-section + %h2.section-title= t('.title_nvd', default: 'This Project has No vulnerabilities Reported Against it') + .did-you-know-card.single + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe diff --git a/config/charting/analysis/options_based_on_type.yml b/config/charting/analysis/options_based_on_type.yml index 46f2b4a25..f6de6294a 100644 --- a/config/charting/analysis/options_based_on_type.yml +++ b/config/charting/analysis/options_based_on_type.yml @@ -24,9 +24,10 @@ commits_history: series: - name: 'Commits' id: 'commits' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' @@ -40,12 +41,12 @@ code_history: stacking: 'normal' series: - name: 'Code' - color: '#482268' + color: '#1D0631' id: 'code' - name: 'Comments' - color: '#008893' + color: '#5A2A82' - name: 'Blanks' - color: '#99dde3' + color: '#9F7ABA' tooltip: shared: true @@ -56,9 +57,10 @@ committer_history: series: - name: 'Committers' id: 'committers' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index f9dd89f02..06c7de8b9 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -150,6 +150,25 @@ en: add_a_code_location: "Add a code location" no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display the commits per month chart. %{link} to see this information." community: "Community" + analysis_summary_redesign: + code: "Code" + lines_of_code: "Lines of Code" + languages: "Languages" + no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display this chart." + activity: "Activity" + commits_per_month: "Commits per Month" + zoom: "Zoom" + no_commits: "No commit data available" + thirty_day_summary: "30 Day Summary" + twelve_month_summary: "12 Month Summary" + not_available: "not available" + commits: "Commits" + contributors: "Contributors" + community: "Community" + contributors_per_month: "Contributors per Month" + no_contributors: "No contributor data available" + most_recent_contributors: "Most Recent Contributors" + ratings: "Ratings" committers_per_month_chart: contributors_per_month: "Contributors per Month" add_a_code_location: "Add a code location" @@ -162,8 +181,9 @@ en: be_the_first: "Be the first to rate this project" click_to_edit: "Click to edit your rating" click_to_add: "Click to add your rating" + click_to_add_review: "Click to add your review" clear: "Clear" - review_this_project: "Review this Project!" + review_this_project: "Review this project" community_recent_committers: most_recent: "Most Recent Contributors" no_one_recently: "No one has contributed to this project recently" @@ -234,7 +254,7 @@ en: vulnerability_report: no_data: 'No data available' pvs_tooltip: 'Score: %{score}
Percentage: %{percentage}
Rank: %{rank} of %{max_rank}' - title: 'Project Vulnerability Report' + title: 'Vulnerability Report' blog_link: 'About Project Vulnerability Report' pvs: title: 'Vulnerability Exposure Index' @@ -246,7 +266,7 @@ en: poor: 'Poor security track-record' vulnerabilities_per_version: title: 'Vulnerabilities per Version' - title_detail: '( last 10 releases )' + title_detail: '(Most recent versions)' no_data: 'No data available' thirty_day_summary: commit: "Commit" @@ -276,7 +296,7 @@ en: most_recent_commit: most recent commit %{time} security: title: 'Project Security' - title_nvd: 'This Project has No vulnerabilities Reported Against it' + title_nvd: 'This project has no vulnerabilities reported against it' blog_link: 'About Project Security' licenses_redesign: licenses: 'Licenses' From 13ba12f0fdebc10206348da46fb6b35206b072e5 Mon Sep 17 00:00:00 2001 From: bd-vaibhav Date: Fri, 6 Mar 2026 14:03:26 +0530 Subject: [PATCH 06/53] OTWO-7574 - Figma redesign for homepage (#1870) * OTWO-7574 - Figma redesign for homepage * OTWO-7574 Fixed card layout issues in homepage * OTWO-7574 Fixed card layout issue --- app/assets/javascripts/home.js.coffee | 2 +- app/assets/javascripts/rotating_stats.js | 29 --- app/assets/stylesheets/home.sass | 260 ++++++++++++--------- app/assets/stylesheets/home_search.sass | 64 ++--- app/assets/stylesheets/page.sass | 28 ++- app/views/home/_top_contributors.html.haml | 7 +- app/views/home/_top_lists.html.haml | 6 +- app/views/home/index.html.haml | 10 +- app/views/layouts/application.html.haml | 2 +- config/initializers/assets.rb | 2 +- 10 files changed, 211 insertions(+), 199 deletions(-) delete mode 100644 app/assets/javascripts/rotating_stats.js diff --git a/app/assets/javascripts/home.js.coffee b/app/assets/javascripts/home.js.coffee index 0e23b0b5b..560e1b882 100644 --- a/app/assets/javascripts/home.js.coffee +++ b/app/assets/javascripts/home.js.coffee @@ -2,7 +2,7 @@ $(document).on 'page:change', -> divs = $('p[id^="content-"]') i = 0 do -> - divs.eq(i).removeClass('hide').fadeIn(400).delay(6000).fadeOut 400, arguments.callee + divs.eq(i).removeClass('hide').fadeIn(200).delay(2500).fadeOut 200, arguments.callee i = ++i % divs.length $('#icon_text').click -> diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js deleted file mode 100644 index 39a368b7b..000000000 --- a/app/assets/javascripts/rotating_stats.js +++ /dev/null @@ -1,29 +0,0 @@ -// Rotating Stats Animation for Homepage -document.addEventListener('DOMContentLoaded', function() { - var globalStats = document.getElementById('global_statistics'); - - if (!globalStats) return; - - var statElements = globalStats.querySelectorAll('p'); - - if (statElements.length === 0) return; - - var currentIndex = 0; - - // Show first stat initially - statElements[0].classList.remove('hide'); - - // Rotate stats every 2 seconds - setInterval(function() { - // Hide current stat - statElements[currentIndex].classList.add('hide'); - - // Move to next stat - currentIndex = (currentIndex + 1) % statElements.length; - - // Show next stat after a brief delay - setTimeout(function() { - statElements[currentIndex].classList.remove('hide'); - }, 300); - }, 2000); -}); diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4978e69e3..8e46c6e06 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -4,15 +4,21 @@ // Main container with consistent 1280px max-width throughout // Isolated from global body font-size: 1.3rem override -.hero_section - padding: 0 16px 40px - background: #fff !important +// Common section background +.section_bg + background: #f9fafb !important transition: background 0.3s ease + html.dark & + background: #1D0631 !important + +.hero_section + @extend .section_bg + padding: 0 16px 4px box-sizing: border-box @media (min-width: 640px) - padding: 0 24px 40px + padding: 0 24px 16px @media (min-width: 1024px) - padding: 0 32px 40px + padding: 0 32px 16px // Dark mode support html.dark & background: #1D0631 !important @@ -107,12 +113,11 @@ // Old placeholder and btn_join_now styles removed - handled in new components .landing + @extend .section_bg margin-top: 20px margin-left: 0 padding: 32px 16px width: 100% - background: #f9fafb !important - transition: background 0.3s ease @media (min-width: 640px) padding: 32px 24px @media (min-width: 1024px) @@ -338,8 +343,9 @@ button padding: 12px 24px // Dark mode support - Yellow background with purple text html.dark & - background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) - border-color: #ffb91a + background: linear-gradient(90deg, #2D1548 0%, #2D1548 50%, #2D1548 100%) + border-color: rgba(255, 255, 255, 0.1) + .stat_item text-align: center @@ -354,9 +360,10 @@ button font-size: 18px @media (min-width: 768px) font-size: 20px - // Dark mode - Purple text on yellow background + // Dark mode - Yellow text on purple background html.dark & - color: #5A2A82 + color: #ffb91a + .stat_label font-size: 8px @@ -370,9 +377,10 @@ button font-size: 9px @media (min-width: 768px) font-size: 10px - // Dark mode - Purple text on yellow background + // Dark mode - Gray text on purple background html.dark & - color: #5A2A82 + color: #9ca3af + .stat_divider display: none @@ -383,17 +391,17 @@ button height: 32px background: #9ca3af opacity: 1 - // Dark mode - Purple divider on yellow background + // Dark mode - White divider with low opacity on purple background html.dark & @media (min-width: 768px) - background: #5A2A82 - opacity: 0.3 + background: #ffffff + opacity: 0.2 + // Content Section .content_section + @extend .section_bg padding: 32px 0 - background: white !important - transition: background 0.3s ease // Dark mode support html.dark & background: #1D0631 !important @@ -429,24 +437,22 @@ button // Top Contributors Card - Matches ContributorJourney.tsx .top_contributors_card - position: relative - padding: 3px + background: white border-radius: 16px - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - margin-bottom: 24px overflow: hidden - transition: box-shadow 0.3s + margin-bottom: 4px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s + display: flex + flex-direction: column &:hover - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) html.dark & - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) - > * - background: white - border-radius: 16px - overflow: hidden - html.dark & - background: #1D0631 // Card Header .top_contributors_card .card_header @@ -456,7 +462,7 @@ button padding: 24px 24px 16px 24px background: white html.dark & - background: #1D0631 + background: #2D1548 .top_contributors_card .header_icon display: flex @@ -512,6 +518,30 @@ button display: flex flex-direction: column gap: 8px + max-height: 300px + overflow-y: scroll + scrollbar-width: thin + scrollbar-color: rgba(90, 42, 130, 0.4) rgba(90, 42, 130, 0.1) + @media (min-width: 768px) + max-height: 500px + &::-webkit-scrollbar + width: 8px + &::-webkit-scrollbar-track + background: rgba(90, 42, 130, 0.1) + border-radius: 3px + &::-webkit-scrollbar-thumb + background: rgba(90, 42, 130, 0.4) + border-radius: 3px + &:hover + background: rgba(90, 42, 130, 0.6) + html.dark & + scrollbar-color: rgba(255, 185, 26, 0.4) rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-track + background: rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-thumb + background: rgba(255, 185, 26, 0.4) + &:hover + background: rgba(255, 185, 26, 0.6) .top_contributors_card .contributor_item display: flex @@ -536,10 +566,8 @@ button background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) border-color: #5A2A82 -// Show only top 4 contributors on mobile -.top_contributors_card .contributor_item:nth-child(n+5) - @media (max-width: 767px) - display: none +// All contributors visible on mobile to enable scrolling +// Mobile users can scroll to see all contributors // Avatar .top_contributors_card .contributor_avatar @@ -662,51 +690,45 @@ button font-weight: 600 color: #5A2A82 text-decoration: none - border-top: 1px solid rgba(90, 42, 130, 0.2) - transition: color 0.2s + border: 2px solid rgba(90, 42, 130, 0.2) + border-radius: 12px + margin-top: 8px + transition: all 0.2s cursor: pointer - - span - border-bottom: 1px solid #5A2A82 + background: transparent &:hover color: #1D0631 - span - border-bottom-color: #1D0631 + border-color: #5A2A82 + background: rgba(90, 42, 130, 0.05) i font-size: 16px html.dark & - color: white !important - border-top-color: #ffb91a - border-top-width: 2px - border-radius: 12px - margin-top: 8px - span - border-bottom-color: white + color: #ffb91a !important + border-color: #ffb91a &:hover - color: white !important + background: #ffb91a + color: #1D0631 !important + // What's New Card - Figma Design .whats_new_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .card_header_section padding: 16px 16px 12px 16px !important @@ -715,7 +737,7 @@ button padding: 24px 24px 16px 24px !important // Dark mode support html.dark & - background: #1D0631 + background: #2D1548 .whats_new_title font-size: 18px !important @@ -768,24 +790,21 @@ button // Join Now Card - Figma Design (p-6 wrapper) .join_now_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + padding: 24px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - padding: 24px !important - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .join_header display: flex @@ -953,9 +972,12 @@ button // User Journeys Section .user_journeys_section background: #1D0631 - padding: 64px 0 + padding: 32px 0 !important + width: 100vw + margin-left: calc(-50vw + 50%) + margin-right: calc(-50vw + 50%) @media (max-width: 768px) - padding: 48px 0 + padding: 16px 0 !important // Matches Figma: max-w-7xl mx-auto .container @@ -998,10 +1020,10 @@ button .journey_card position: relative - padding: 3px border-radius: 12px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + border: 0.8px solid #ffb91a + background: #1D0631 margin-bottom: 16px transition: all 0.3s @@ -1171,13 +1193,12 @@ button // Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 .project_swimlanes_section - padding: 32px 16px - background: #f9fafb - transition: background 0.3s ease + @extend .section_bg + padding: 8px 16px !important @media (min-width: 640px) - padding: 32px 24px + padding: 8px 24px !important @media (min-width: 1024px) - padding: 32px 32px + padding: 8px 32px !important html.dark & background: #1D0631 @@ -1226,29 +1247,36 @@ button // Compact Project Card Styles .compact_project_card position: relative - padding: 3px - border-radius: 8px + border-radius: 16px overflow: hidden transition: all 0.3s ease - background: transparent cursor: pointer + border: 1px solid rgba(229, 231, 235, 0.8) + background: white + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + display: flex + flex-direction: column + &:hover - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - .compact_card_inner - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + border-color: #5A2A82 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + + html.dark & + background: #2D1548 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + border-color: #ffb91a + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) .compact_project_card .compact_card_inner - background: white - border-radius: 8px padding: 12px transition: all 0.3s ease height: 100% - border: 1px solid #e5e7eb + display: flex + flex-direction: column @media (min-width: 640px) padding: 16px - html.dark & - background: #1D0631 - border-color: #4b5563 .compact_project_card .compact_card_header display: flex @@ -1347,6 +1375,11 @@ button .compact_project_card .compact_card_description margin-bottom: 12px + height: 42px + display: flex + align-items: center + @media (min-width: 640px) + height: 44px p font-size: 12px color: #6b7280 @@ -1432,7 +1465,6 @@ button .hidden_card width: 100% - margin-bottom: 16px // Show More Button .show_more_btn @@ -1442,7 +1474,7 @@ button color: #5A2A82 border: 2px solid #5A2A82 padding: 12px 16px - border-radius: 8px + border-radius: 12px font-weight: 600 cursor: pointer transition: all 0.3s ease @@ -1453,11 +1485,11 @@ button color: white html.dark & background: transparent - color: white - border-color: white + color: #ffb91a + border-color: #ffb91a &:hover - background: white - color: #5A2A82 + background: #ffb91a + color: #1D0631 // Pagination Controls .pagination_controls @@ -1516,7 +1548,7 @@ button // View All Projects Button - Matches Figma: pt-4 pb-8 .view_all_container - padding-top: 16px + padding-top: 32px padding-bottom: 32px padding-left: 16px padding-right: 16px @@ -1540,8 +1572,8 @@ button gap: 8px padding: 12px 24px background: #ffb91a - color: #000000 - border-radius: 8px + color: #1D0631 + border-radius: 12px font-weight: 600 font-size: 14px text-decoration: none @@ -1552,10 +1584,10 @@ button &:hover background: rgba(255, 185, 26, 0.9) box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) - color: #000000 + color: #1D0631 html.dark & - color: #ffffff !important + color: #1D0631 !important &:hover - color: #ffffff !important + color: #1D0631 !important i font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass index b1c0d45e8..b873b26f5 100644 --- a/app/assets/stylesheets/home_search.sass +++ b/app/assets/stylesheets/home_search.sass @@ -16,34 +16,34 @@ width: 100% display: block - // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) - // Positioned to align with input text baseline - .search_icon - position: absolute - top: 50% - left: 8px - transform: translateY(-60%) - display: block - width: 14px - height: 14px - font-size: 14px - color: #9ca3af - pointer-events: none - z-index: 10 - transition: color 0.3s ease - text-align: center - font-style: normal - @media (min-width: 640px) - left: 24px - width: 20px - height: 20px - font-size: 20px - // Dark mode support - html.dark & - color: #6b7280 - // Ensure Font Awesome icon centers properly - &::before - display: block + // Search Icon Wrapper - Figma Design +.search_icon_wrapper + position: absolute + top: 0 + bottom: 0 + left: 8px + display: flex + align-items: center + pointer-events: none + @media (min-width: 640px) + left: 24px + +// Search Icon - Figma specs +.search_icon + width: 14px + height: 14px + font-size: 14px + line-height: 0.5 + color: #9ca3af + transition: color 0.3s ease + @media (min-width: 640px) + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 input.search_input @@ -131,15 +131,15 @@ form#search_form padding: 16px 16px 16px 56px !important font-size: 16px !important &:focus - border-color: #0E4B7A !important + border-color: #5A2A82 !important box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important outline: none !important // Dark mode support html.dark & background: #1D0631 !important color: #ffffff !important - border-color: #4b5563 !important + border-color: #5A2A82 !important &:focus - border-color: #2E8B9E !important + border-color: #ffb91a !important background: #1D0631 !important - box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important + box-shadow: 0 0 0 4px rgba(255, 185, 26, 0.1) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cb5fd0334..994ad5244 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -18,13 +18,17 @@ body color: #000 width: 100% overflow-x: hidden + background-color: #f9fafb !important .container#page, #page width: 100% !important max-width: 100% !important - background-color: white + background-color: #f9fafb margin: 0 !important padding: 0 !important + transition: background-color 0.3s ease + html.dark & + background-color: #1D0631 header // Figma design: gradient background for entire header @@ -313,7 +317,9 @@ header .navbar // Figma design: gradient background with purple theme - position: relative + position: sticky + top: 0 + z-index: 1000 background: linear-gradient(to right, #000000, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) @@ -603,16 +609,17 @@ header .mobile-menu display: none width: 100% - flex-basis: 100% background: linear-gradient(to right, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) border-top: 1px solid rgba(255, 255, 255, 0.15) padding: 20px 0 - position: relative + position: absolute + top: 64px + left: 0 + right: 0 z-index: 1002 overflow: visible - order: 2 box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) &.show display: block !important @@ -694,7 +701,7 @@ header background-color: #FFB91A !important color: #000 !important border: none !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -720,7 +727,7 @@ header background-color: transparent !important color: #FFB91A !important border: 2px solid #FFB91A !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -736,6 +743,13 @@ header &:active transform: translateY(0) box-shadow: none + html.dark & + background-color: transparent !important + color: #FFB91A !important + border-color: #FFB91A !important + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + .dropdown#logged_user_menu position: relative diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml index d87f62121..1544ad3b9 100644 --- a/app/views/home/_top_contributors.html.haml +++ b/app/views/home/_top_contributors.html.haml @@ -22,14 +22,9 @@ %span.contributor_rank= "Rank ##{index + 1}" .contributor_stats - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 - - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project - - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 %span.stat_commits %i.fa.fa-dot-circle-o - = number_with_delimiter(commits) - %span.stat_projects - %i.fa.fa-code - = "#{project_count} projects" + = "#{number_with_delimiter(commits)} commits" .contributor_arrow %i.fa.fa-chevron-right diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index e0d18cc2b..02db80a5a 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -19,7 +19,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_active_projects.size > 2 - - @home.most_active_projects.drop(2).each do |project| + - @home.most_active_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -44,7 +44,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_popular_projects.size > 2 - - @home.most_popular_projects.drop(2).each do |project| + - @home.most_popular_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -69,7 +69,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_recent_projects.size > 2 - - @home.most_recent_projects.drop(2).each do |project| + - @home.most_recent_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index a9cfb02b6..82762ba3e 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,6 +1,5 @@ - content_for(:html_title) { t('.page_title') } - content_for(:javascript) do - = javascript_include_tag 'rotating_stats' = javascript_include_tag 'project_swimlanes' / Hero Section @@ -16,7 +15,8 @@ %form.search_form#search_form{ action: projects_path } %input{ type: :hidden, name: 'ref', value: 'homepage' } .search_input_wrapper - %i.fa.fa-search.search_icon + .search_icon_wrapper + %i.fa.fa-search.search_icon %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } / Rotating Stats @@ -76,8 +76,8 @@ = render partial: '/home/whats_new' = render partial: '/home/join_now_home' -/ User Journeys Section -= render partial: '/home/user_journeys' - / Project Swimlanes Section = home_top_lists + +/ User Journeys Section += render partial: '/home/user_journeys' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7c5f3dbe8..a8438e724 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,8 +18,8 @@ %body{ zoom: 1 } = yield :session_projects_banner + %header= render partial: 'layouts/partials/header' .container#page - %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear %footer= render partial: 'layouts/partials/footer' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index b35d3c3ee..052e2ce67 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -7,5 +7,5 @@ Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css api/vulnerability.sass api/vulnerability.js - rotating_stats.js project_swimlanes.js] + project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] From e09b43fb956b3f5829cc0397bddb485b1e667e88 Mon Sep 17 00:00:00 2001 From: Niharika1117 <101638919+Niharika1117@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:41:15 +0530 Subject: [PATCH 07/53] OTWO-7557 project show page design modification (#1871) * OTWO-7557 project show page design modification * OTWO-7557 fix on the css for project show page --- app/assets/stylesheets/charts.sass | 8 +- app/assets/stylesheets/dark_theme.sass | 20 + app/assets/stylesheets/page.sass | 40 +- .../stylesheets/project_show_redesign.sass | 3055 +++++++++++++++++ app/decorators/analysis/chart.rb | 2 +- .../analysis/commit_history_chart.rb | 13 +- .../analysis/contributor_history_chart.rb | 2 + app/decorators/vulnerability/version_chart.rb | 4 +- .../responsive_project_layout.html.haml | 4 +- app/views/projects/show.html.haml | 503 ++- .../show/_analysis_summary_redesign.html.haml | 104 + .../show/_community_rating_redesign.html.haml | 28 + ...unity_recent_committers_redesign.html.haml | 21 + .../projects/show/_header_redesign.html.haml | 128 + .../show/_languages_redesign.html.haml | 20 + .../show/_licenses_redesign.html.haml | 93 + app/views/projects/show/_security.html.haml | 100 +- .../analysis/options_based_on_type.yml | 12 +- config/locales/projects.en.yml | 90 +- 19 files changed, 4141 insertions(+), 106 deletions(-) create mode 100644 app/assets/stylesheets/project_show_redesign.sass create mode 100644 app/views/projects/show/_analysis_summary_redesign.html.haml create mode 100644 app/views/projects/show/_community_rating_redesign.html.haml create mode 100644 app/views/projects/show/_community_recent_committers_redesign.html.haml create mode 100644 app/views/projects/show/_header_redesign.html.haml create mode 100644 app/views/projects/show/_languages_redesign.html.haml create mode 100644 app/views/projects/show/_licenses_redesign.html.haml diff --git a/app/assets/stylesheets/charts.sass b/app/assets/stylesheets/charts.sass index 9de5968b6..3fd13b400 100644 --- a/app/assets/stylesheets/charts.sass +++ b/app/assets/stylesheets/charts.sass @@ -398,10 +398,16 @@ .highcharts-tooltip .highcharts-color-9 @include project-contributions-color-9-fill -#code_analysis_chart, #activity_chart, #community_chart +#code_analysis_chart, #community_chart .highcharts-graph @include code-analysis-chart-color-0-stroke +#activity_chart + .highcharts-graph, + .highcharts-point + stroke: #5A2A82 + fill: #5A2A82 + #committer_history_chart .highcharts-graph @include code-analysis-chart-color-0-stroke diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 9a7f3d844..5b8198f4f 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -148,3 +148,23 @@ html.dark .good color: #4ade80 !important + + // Project security: Did You Know block (legacy and redesign templates) + #project_container .project_row #did_you_know + background: #1D0631 !important + + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + &:hover + color: #93c5fd !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index 994ad5244..af7da55f2 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -130,7 +130,6 @@ header gap: 24px #page_contents max-width: 100% - padding: 0 20px .separator-div @include site-separator-color @@ -151,7 +150,6 @@ header padding-left: 0 margin-left: -1rem .thirtyfive_project_activity_text - margin-top: 5.5rem line-height: 1.3rem .thirtyfive_project_activity_level_na top: 1rem @@ -1137,6 +1135,44 @@ fieldset .language_percentage_indicator width: 3rem +// Dark mode guards for legacy project security/no-analysis blocks +html.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + +body.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + // Full-width layout styles .page-content-wrapper width: 100% diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass new file mode 100644 index 000000000..8509ed849 --- /dev/null +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -0,0 +1,3055 @@ +// Project Show Page Redesign - Clean Implementation Based on Figma Design +// ============================================================================ + +// Design System Variables +// ============================================================================ +$purple-dark: #1D0631 +$purple-primary: #5A2A82 +$purple-light: #9F7ABA +$yellow-accent: #FFB91A +$yellow-hover: #FFCC4D + +// Neutrals +$gray-50: #f9fafb +$gray-100: #f3f4f6 +$gray-200: #e5e7eb +$gray-300: #d1d5db +$gray-400: #9ca3af +$gray-500: #6b7280 +$gray-600: #4b5563 +$gray-700: #374151 +$gray-900: #111827 + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05) +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) + +// System Font Stack +$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + +// ============================================================================ +// Global Overrides - Remove Bootstrap padding/margins +// ============================================================================ + +// Remove navbar margin globally with maximum specificity +.navbar, +.navbar-default, +.navbar-static-top, +nav.navbar, +header .navbar, +.navbar-fixed-top, +.navbar-fixed-bottom + margin-bottom: 0 !important + margin-top: 0 !important + +// Additional Bootstrap navbar overrides +body .navbar + margin-bottom: 0 !important + +// Target specific header navbar +header + .navbar + margin-bottom: 0 !important + +// Remove container and page padding when showing redesigned project page +body:has(.project-show-redesign) + #project_container, + .container-fluid#project_container + padding: 0 !important + margin: 0 !important + max-width: 100% !important + + #page-contents, + .row#page-contents + margin: 0 !important + padding: 0 !important + + > div + padding: 0 !important + +// Direct child approach - more specific +#project_container + > .row#page-contents + > .col-xs-12, + > .col-sm-12, + > .col-md-12 + padding: 0 !important + + > .project-show-redesign + margin: 0 !important + +// Force remove padding from all Bootstrap column classes in project container +.project-show-redesign + ~ * + .container, + .container-fluid, + .row, + [class*="col-"] + padding-left: 0 !important + padding-right: 0 !important + +// ============================================================================ +// Main Container +// ============================================================================ +.project-show-redesign + width: 100% + min-height: 100vh + background-color: $gray-50 + font-family: $font-family + -webkit-font-smoothing: antialiased + -moz-osx-font-smoothing: grayscale + margin: 0 !important + + // Hide old project header elements + #project_masthead, + #project_header, + #project_icon, + #widgets, + #project_header_activity_indicator + display: none !important + + // Reset global styles that interfere + * + box-sizing: border-box + + // Override any legacy project container color styles + color: $gray-900 + + a + color: $purple-primary + + &:hover + color: $purple-primary + + // ============================================================================ + // Project Header Section + // ============================================================================ + .project-header-gradient + background: linear-gradient(135deg, $purple-dark 0%, $purple-primary 100%) + padding: 50px 16px 32px + position: relative + overflow: hidden + + // Decorative gradient circle + &::before + content: '' + position: absolute + top: -192px + right: -192px + width: 384px + height: 384px + background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%) + border-radius: 50% + + .header-content + margin: 0 auto + position: relative + z-index: 10 + + .header-main + display: flex + flex-direction: column + gap: 16px + + @media (min-width: 768px) + flex-direction: row + align-items: flex-start + justify-content: space-between + gap: 24px + + a, + button + font-family: $font-family + + // Desktop: Top-right user stats and button + .header-top-right + position: absolute + top: 49px + right: 16px + display: none + align-items: center + gap: 12px + z-index: 20 + + @media (min-width: 640px) + display: flex + right: 24px + + @media (min-width: 1024px) + right: 32px + + .stats-card + background: white + border-radius: 8px + padding: 12px 16px + box-shadow: $shadow-md + min-width: 140px + + .activity-indicator-wrapper + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 8px + min-width: 80px + + [class^='thirtyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + transform: scale(1.25) + transform-origin: center center + + .thirtyfive_project_activity_text + margin: 0 + width: auto + line-height: 1.2 + font-size: 12px + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card + .stats-inner + display: flex + align-items: center + gap: 12px + margin-bottom: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .user-count + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + + .i-use-this-btn + width: 100% + padding: 10px 16px + border-radius: 6px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + transition: all 0.2s + + &.using + background: $purple-primary + color: white + + &:hover + background: darken($purple-primary, 8%) + + &.not-using + background: $yellow-accent + color: $purple-dark + + &:hover + background: $yellow-hover + + svg + width: 16px + height: 16px + + // Project title and info section + .project-title-section + flex: 1 + min-width: 0 + max-width: 900px + + @media (min-width: 768px) + max-width: calc(100% - 280px) + + h1 + font-size: 32px + font-weight: 700 + color: white + line-height: 1.2 + letter-spacing: -0.02em + margin: 0 0 12px + + @media (min-width: 768px) + font-size: 48px + + @media (min-width: 1024px) + font-size: 48px + + .action-buttons + display: flex + flex-wrap: wrap + gap: 8px + margin-bottom: 20px + + .action-btn + display: inline-flex + align-items: center + gap: 6px + padding: 6px 12px + border-radius: 8px + font-size: 12px + font-weight: 500 + line-height: 1.25 + text-decoration: none !important + appearance: none + -webkit-appearance: none + background-image: none !important + box-shadow: none !important + border: 0 + cursor: pointer + transition: all 0.2s ease + + &:link, + &:visited, + &:hover, + &:active + color: white !important + text-decoration: none !important + + @media (min-width: 768px) + font-size: 14px + + svg + width: 14px + height: 14px + flex-shrink: 0 + stroke: currentColor + + // Outlined button style (for Settings and Report Duplicate) + &.action-btn-outline + background: rgba(255, 255, 255, 0.1) + border: 1px solid rgba(255, 255, 255, 0.2) + color: white !important + box-shadow: none + + &:hover + background: rgba(255, 255, 255, 0.2) + border-color: rgba(255, 255, 255, 0.2) + color: white !important + transform: none + + &:active + transform: none + background: rgba(255, 255, 255, 0.2) + + &:focus-visible + outline: 2px solid rgba(255, 255, 255, 0.55) + outline-offset: 2px + + .project-description + font-size: 16px + color: rgba(255, 255, 255, 0.9) + line-height: 1.6 + margin-bottom: 12px + + @media (min-width: 768px) + font-size: 17px + + .timestamp + font-size: 14px + color: rgba(255, 255, 255, 0.7) + line-height: 1.5 + + // Mobile: Bottom stats card + .header-mobile-stats + display: flex + gap: 12px + margin-top: 24px + + @media (min-width: 640px) + display: none + + .activity-card-mobile + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 4px + padding: 10px 12px + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + + [class^='twentyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + + .twentyfive_project_activity_text + margin: 0 + width: auto + min-height: 0 + font-size: 10px + line-height: 1.2 + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card-mobile + flex: 1 + display: flex + flex-direction: column + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + padding: 0 + overflow: hidden + + .stats-top + display: flex + align-items: center + gap: 8px + margin-bottom: 0 + padding: 10px 12px 6px + + svg + width: 16px + height: 16px + color: $yellow-accent + + .user-count + font-size: 18px + font-weight: 700 + color: $yellow-accent + + .users-label + font-size: 12px + color: rgba(255, 255, 255, 0.8) + + .i-use-this-btn-mobile + width: calc(100% - 20px) + margin: 0 10px 10px + padding: 8px 12px + border-radius: 8px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + line-height: 1.25 + + &.using + background: $purple-primary + color: white + + &.not-using + background: $yellow-accent + color: $purple-dark + + svg + width: 16px + height: 16px + + // ============================================================================ + // Main Content Area + // ============================================================================ + .project-content + margin: 0 auto + padding: 24px + + @media (min-width: 768px) + padding: 32px + + // ============================================================================ + // Stats Grid (3 columns) + // ============================================================================ + .stats-grid + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + + .stat-card + background: white + border-radius: 12px + padding: 20px 16px + text-align: center + box-shadow: $shadow-sm + transition: box-shadow 0.2s + + &:hover + box-shadow: $shadow-md + + .stat-value + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + margin-bottom: 8px + + @media (min-width: 768px) + font-size: 36px + + .stat-label + font-size: 14px + color: $gray-600 + font-weight: 500 + line-height: 1.2 + + // ============================================================================ + // Card Components + // ============================================================================ + .gradient-card + background: white + border-radius: 12px + // Enhanced shadow to match Figma design - more prominent + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + overflow: hidden + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + .card-content + padding: 20px + + @media (min-width: 768px) + padding: 24px + + h3 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + line-height: 1.3 + margin: 0 0 16px !important + padding-bottom: 12px + border-bottom: 1px solid $gray-200 + + h4 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + line-height: 1.4 + margin: 0 0 12px !important + + p + font-size: 14px !important + line-height: 1.5 + color: $gray-700 !important + margin: 0 0 16px !important + + &:last-child + margin-bottom: 0 + + &.note + font-size: 12px !important + color: $gray-500 !important + font-style: italic + + ul + list-style: none + padding: 0 + margin: 0 + + // ============================================================================ + // Desktop: 3-column grid + // ============================================================================ + .three-col-grid + display: none + + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + margin-bottom: 32px + + // ============================================================================ + // Mobile: Accordion cards + // ============================================================================ + .mobile-accordion + display: flex + flex-direction: column + gap: 16px + margin-bottom: 32px + + @media (min-width: 1024px) + display: none + + .accordion-card + .accordion-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 20px + background: none + border: none + cursor: pointer + text-align: left + + .header-left + display: flex + align-items: center + gap: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + + h3 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin: 0 !important + + .chevron + width: 20px + height: 20px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content + padding: 0 20px 20px + display: none + + &.expanded + display: block + + // ============================================================================ + // Tags + // ============================================================================ + #project_tags, + #project_tags_mobile + margin-top: 24px + + h4 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + color: $gray-900 !important + + .tags-container + display: flex + flex-wrap: wrap + gap: 8px + + .tag + padding: 4px 12px + background: $gray-100 + color: $gray-900 !important + font-size: 14px + border-radius: 6px + text-decoration: none + transition: all 0.15s + + &:hover + background: $purple-primary + color: white !important + + // ============================================================================ + // Nutshell List + // ============================================================================ + .nutshell-list + list-style: none + padding: 0 + margin: 0 + display: flex + flex-direction: column + gap: 12px + + li + display: flex + gap: 12px + padding-top: 12px + font-size: 14px !important + line-height: 1.5 + border-top: 1px solid $gray-100 + color: $gray-700 !important + + &:first-child + border-top: none + padding-top: 0 + + svg + width: 16px + height: 16px + color: $purple-primary !important + flex-shrink: 0 + margin-top: 2px + + .content + color: $gray-700 !important + flex: 1 + + a + color: $purple-primary !important + font-weight: 600 + text-decoration: none + + &:hover + text-decoration: underline + color: $purple-primary !important + + .font-semibold, + span.font-semibold + font-weight: 600 + color: $gray-700 !important + + // ============================================================================ + // Quick Reference List + // ============================================================================ + .quick-ref-list + list-style: none + padding: 0 + margin: 0 + display: flex + flex-direction: column + gap: 12px + + li + display: flex + justify-content: space-between + align-items: flex-start + gap: 16px + padding-bottom: 12px + font-size: 14px !important + border-bottom: 1px solid $gray-100 + + &:last-child + border-bottom: none + padding-bottom: 0 + + .label + color: $gray-600 !important + font-weight: 500 !important + font-size: 14px !important + flex-shrink: 0 + line-height: 1.5 + + .value + color: $purple-primary !important + font-weight: 600 !important + font-size: 14px !important + text-align: right + flex: 1 + line-height: 1.5 + + a + color: inherit !important + text-decoration: none + display: inline-flex + align-items: center + gap: 4px + font-size: 14px !important + font-weight: 600 !important + + &:hover + text-decoration: underline + color: inherit !important + + svg + width: 12px + height: 12px + flex-shrink: 0 + color: currentColor !important + + i + font-size: 14px !important + color: $purple-primary !important + font-weight: 600 !important + + // Maximum specificity overrides for Quick Reference to prevent legacy interference + #page #project_container .project-show-redesign + .gradient-card .card-content h3 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + + .quick-ref-list li + font-size: 14px !important + + .label + font-size: 14px !important + font-weight: 500 !important + color: $gray-600 !important + + .value + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + i + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + .mobile-accordion .accordion-card .accordion-header .header-left h3 + font-size: 16px !important + font-weight: 600 !important + + // Languages section override + .languages-content .language-legend .language-item + font-size: 12px !important + + .language-name + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + font-size: 12px !important + + .language-percent + color: $gray-500 !important + font-size: 12px !important + + // Summary sections override + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + + // ============================================================================ + // Even more specific overrides for Activity card summary sections + // ============================================================================ + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin-bottom: 4px !important + + p.summary-timespan + font-size: 12px !important + color: #9ca3af !important + margin-bottom: 8px !important + + p.summary-stat + font-size: 12px !important + color: #374151 !important + margin-bottom: 4px !important + + span.stat-number + font-weight: 600 !important + color: #111827 !important + + p.summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + + // ============================================================================ + // Licenses Section + // ============================================================================ + .licenses-section + margin-bottom: 32px + + @media (min-width: 768px) + margin-bottom: 48px + + .section-title + display: block + visibility: visible + opacity: 1 + font-size: 20px + font-weight: 600 + color: $gray-900 + margin-bottom: 12px + line-height: 1.3 + + @media (min-width: 768px) + font-size: 24px + margin-bottom: 16px + + .license-card + margin-bottom: 16px + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:last-child + margin-bottom: 0 + + .license-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 16px + background: none + border: none + cursor: pointer + text-align: left + + @media (min-width: 768px) + padding: 24px + + @media (min-width: 1024px) + cursor: default + + .header-left + display: flex + align-items: center + gap: 8px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + @media (min-width: 768px) + width: 20px + height: 20px + + h4 + font-size: 16px + font-weight: 600 + color: $purple-primary + margin: 0 + line-height: 1.3 + + @media (min-width: 768px) + font-size: 18px + + .chevron + width: 20px + height: 20px + color: $gray-500 + transition: transform 0.2s + flex-shrink: 0 + + @media (min-width: 1024px) + display: none + + &.expanded + transform: rotate(180deg) + + .license-content + display: none + border-top: 1px solid $gray-200 + padding: 16px + + &.expanded + display: block + + @media (min-width: 768px) + padding: 24px + + @media (min-width: 1024px) + display: block + + .license-details-grid + display: grid + gap: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 24px + + .license-detail-card + border-radius: 8px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + @media (min-width: 768px) + padding: 20px + + &.permitted + background: linear-gradient(to bottom right, #faf5ff 0%, #f3e8ff 100%) + + h5 + color: $purple-primary + + svg + color: $purple-primary + + li + color: $gray-700 + + svg + color: $purple-primary + + &.forbidden + background: linear-gradient(to bottom right, #fff7ed 0%, #fed7aa 100%) + + h5 + color: #c2410c + + svg + color: #c2410c + + li + color: $gray-700 + + svg + color: #c2410c + + &.required + background: linear-gradient(to bottom right, #fefce8 0%, #fef3c7 100%) + + h5 + color: #a16207 + + svg + color: #a16207 + + li + color: $gray-700 + + svg + color: #a16207 + + h5 + font-size: 14px + font-weight: 600 + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + flex-shrink: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + li + font-size: 14px + margin-bottom: 8px + display: flex + align-items: center + gap: 8px + line-height: 1.4 + + &:last-child + margin-bottom: 0 + + svg + width: 12px + height: 12px + flex-shrink: 0 + margin-top: 0 + + .license-disclaimer + margin-top: 20px + padding-top: 16px + border-top: 1px solid $gray-200 + text-align: center + + @media (min-width: 768px) + margin-top: 24px + + p + font-size: 12px + color: $gray-500 + font-style: italic + margin: 0 + line-height: 1.5 + + .view-all-link + margin-top: 16px + text-align: right + + a + font-size: 14px !important + font-weight: 500 !important + color: $purple-primary !important + text-decoration: none + + &:visited, + &:active + color: $purple-primary !important + + &:hover + text-decoration: underline + color: $purple-primary !important + + // ============================================================================ + // Project Security Section + // ============================================================================ + .security-section + margin-bottom: 32px + + @media (min-width: 768px) + margin-bottom: 48px + + .section-title + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + + @media (min-width: 768px) + font-size: 24px !important + margin-bottom: 16px !important + + .security-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 16px + + @media (min-width: 768px) + gap: 24px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .card-subtitle + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 16px !important + line-height: 1.4 + + @media (min-width: 768px) + font-size: 16px !important + + .security-link + color: $gray-900 !important + text-decoration: none + + &:hover + color: $purple-primary !important + text-decoration: underline + + .subtitle-detail + visibility: visible !important + opacity: 1 !important + display: block !important + font-size: 12px !important + font-weight: 400 !important + color: #6b7280 !important + margin-top: 4px + + @media (min-width: 768px) + display: inline !important + margin-left: 8px + + #vulnerability_version_chart + .chart-container + min-height: 250px + background: $gray-50 + border-radius: 8px + padding: 16px + + .no-data + text-align: center + padding: 60px 20px + color: $gray-600 + font-size: 14px + + // Vulnerability Report Indices + .vulnerability-indices + display: flex + flex-direction: column + gap: 24px + margin-top: 16px + + .index-section + .index-title + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 500 !important + color: #4b5563 !important + margin-bottom: 8px !important + line-height: 1.4 + + .gradient-bar-container + position: relative + height: 32px + margin-bottom: 8px + + @media (min-width: 768px) + height: 40px + + .gradient-bar + width: 100% + height: 100% + background: linear-gradient(to right, #5A2A82 0%, #ffb91a 50%, #f59e0b 100%) + border-radius: 8px + + .indicator-pointer + position: absolute + top: 0 + width: 4px + height: 100% + background: #1D0631 + border-radius: 9999px + cursor: pointer + transition: all 0.2s + + &:hover + background: $gray-900 + + .index-labels + display: flex + justify-content: space-between + font-size: 12px + color: #6b7280 + + .label-left, + .label-right + max-width: 45% + + .report-link-container + margin-top: 16px + text-align: center + + .report-link + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + // Did You Know Card + .did-you-know-card + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%) + border-radius: 12px + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + &.single + max-width: 600px + margin: 0 auto + + .card-content + padding: 20px + + @media (min-width: 768px) + padding: 24px + + .did-you-know-title + display: flex !important + visibility: visible !important + opacity: 1 !important + font-size: 16px !important + font-weight: 600 !important + color: #78350f !important + margin-bottom: 12px !important + align-items: center + gap: 8px + + @media (min-width: 768px) + font-size: 18px !important + + svg + width: 20px !important + height: 20px !important + color: #78350f !important + + .feature-list + list-style: disc + padding-left: 24px + margin: 0 + display: flex + flex-direction: column + gap: 8px + + li + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + color: #78350f !important + line-height: 1.5 + + .security-footer-link + margin-top: 16px + text-align: right + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px + + // ============================================================================ + // Footer Navigation + // ============================================================================ + .footer-nav-section + background: white + border-radius: 16px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + margin-bottom: 24px + + @media (min-width: 640px) + padding: 32px + margin-bottom: 32px + + a + &:link, + &:visited, + &:hover, + &:active + text-decoration: none + + // Desktop: 4-column grid + .footer-nav-grid + display: none + + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(4, 1fr) + gap: 32px + + .nav-column + .column-header + display: flex + align-items: center + gap: 8px + margin-bottom: 12px + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + svg + width: 16px + height: 16px + color: $purple-primary + + h4 + font-size: 14px + font-weight: 600 + color: $gray-900 + margin: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 8px + + &:last-child + margin-bottom: 0 + + a + font-size: 14px + color: #5A2A82 !important + text-decoration: none + transition: color 0.15s + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + + // Mobile: Accordion + .footer-nav-accordion + display: flex + flex-direction: column + + @media (min-width: 768px) + display: none + + .accordion-item + border-bottom: 1px solid $gray-200 + + &:last-child + border-bottom: none + + .accordion-header-btn + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 12px 0 + background: none + border: none + cursor: pointer + + .header-left + display: flex + align-items: center + gap: 10px + + svg + width: 16px + height: 16px + color: $purple-primary + + span + font-size: 14px + font-weight: 600 + color: $gray-900 + + .chevron + width: 16px + height: 16px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content-footer + padding-bottom: 12px + padding-left: 24px + display: none + + &.expanded + display: block + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 6px + + a + font-size: 14px + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + +// ============================================================================ +// Dark Theme Overrides (Project Show Redesign) +// ============================================================================ +html.dark + .project-show-redesign + background-color: $purple-dark + color: #e2e8f0 !important + + // Override any legacy color styles + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .project-content + background: transparent + + .stats-grid + .stat-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .stat-value + color: $yellow-accent + + .stat-label + color: #94a3b8 + + .gradient-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .card-content + h3 + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) + + h4 + color: white !important + + p + color: #cbd5e1 !important + + &.note + color: #94a3b8 !important + + a + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .mobile-accordion + .accordion-card + .accordion-header + .header-left + svg + color: $yellow-accent + + h3 + color: #f8fafc + + .chevron + color: #94a3b8 + + #project_tags, + #project_tags_mobile + h4 + color: white !important + + svg + color: white !important + + .tags-container + .tag + background: $purple-dark + color: $gray-300 !important + + &:hover + background: $yellow-accent + color: $purple-dark !important + + .nutshell-list + li + border-top-color: rgba(90, 42, 130, 0.2) + color: $gray-300 !important + + svg + color: $yellow-accent !important + + .content + color: $gray-300 !important + + a + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .font-semibold, + span.font-semibold + color: $gray-300 !important + + .quick-ref-list + li + border-bottom-color: rgba(90, 42, 130, 0.2) + + .label + color: $gray-400 !important + font-size: 14px !important + font-weight: 500 !important + + .value + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + a + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + i + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + // Maximum specificity dark mode overrides + html.dark #page #project_container .project-show-redesign + .gradient-card .card-content h3 + color: white !important + + .quick-ref-list li + .label + color: $gray-400 !important + font-size: 14px !important + + .value + color: $yellow-accent !important + font-size: 14px !important + + a, + i + color: $yellow-accent !important + font-size: 14px !important + + // Languages section dark mode override + .languages-content .language-legend .language-item + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + // Summary sections dark mode override + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Even more specific dark mode overrides for Activity card summary sections + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #6b7280 !important + + p.summary-stat + color: #d1d5db !important + + span.stat-number + color: white !important + + p.summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Maximum specificity for rating section + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + font-size: 12px !important + color: #374151 !important + + .rating-link + color: #5A2A82 !important + + .rating-display + .rating-value + font-size: 12px !important + color: #111827 !important + font-weight: 600 !important + + // Maximum specificity dark mode for rating section + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + color: #d1d5db !important + + .rating-link + color: #FFB91A !important + + .rating-display + .rating-value + color: white !important + + html.dark + .licenses-section + .section-title + color: white + + .license-card + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) + + .header-left + svg + color: $yellow-accent + + h4 + color: $yellow-accent + + .chevron + color: #94a3b8 + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) + + .license-details-grid + .license-detail-card + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) + + h5 + color: $yellow-accent + + svg + color: $yellow-accent + + li + color: $gray-300 + + svg + color: $yellow-accent + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) + + h5 + color: #fb923c + + svg + color: #fb923c + + li + color: $gray-300 + + svg + color: #fb923c + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) + + h5 + color: #facc15 + + svg + color: #facc15 + + li + color: $gray-300 + + svg + color: #facc15 + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) + + p + color: #94a3b8 + + .view-all-link + a + font-weight: 500 !important + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .security-section + .section-title + color: white !important + + .card-subtitle + color: white !important + + .security-link + color: white !important + + &:hover + color: $yellow-accent !important + + .subtitle-detail + color: #9ca3af !important + + #vulnerability_version_chart + .chart-container + background: rgba(90, 42, 130, 0.15) + + .no-data + color: #94a3b8 + + .index-section + .index-title + color: #9ca3af !important + + .gradient-bar-container + .indicator-pointer + background: white !important + + &:hover + background: $yellow-accent !important + + .report-link-container + .report-link + color: $yellow-accent !important + + .did-you-know-card + background: linear-gradient(135deg, #4a3a15 0%, #3a2a10 100%) !important + + .did-you-know-title + color: #fcd34d !important + + svg + color: #fcd34d !important + + .feature-list + li + color: #fef08a !important + + .security-footer-link + a + color: $yellow-accent !important + + .no-data + color: #94a3b8 + + .footer-nav-section + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) + + svg + color: $yellow-accent + + h4 + color: #f8fafc + + ul + li + a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) + + .accordion-header-btn + .header-left + svg + color: $yellow-accent + + span + color: #f8fafc + + .chevron + color: #94a3b8 + + .accordion-content-footer + ul + li + a + color: $yellow-accent + + &:visited, + &:active + color: $yellow-accent + + // Legacy sections rendered inside redesign page (security + analysis summary) + .project_row + color: #e2e8f0 + + h2, h3, h4, h5 + color: #f8fafc !important + + p, small, td, th, li, span + color: #cbd5e1 + + a + color: $yellow-accent + + .well, + .activity_well, + .community_well, + #vulnerability-report .well + background-color: #2D1548 !important + border-color: rgba(159, 122, 186, 0.3) !important + + .right_border + border-right-color: rgba(159, 122, 186, 0.3) !important + + .security_blog_link + color: $yellow-accent !important + + #did_you_know + background-color: transparent !important + + h4 + color: #f8fafc !important + + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + ul, + li, + .indent, + p, + span + color: #cbd5e1 !important + + a + color: $yellow-accent !important + + &:hover + color: $yellow-hover !important + text-decoration: underline + + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + color: #cbd5e1 !important + + .title + h3 + color: #f8fafc !important + + i, + [class^='icon-'], + [class*=' icon-'] + color: $yellow-accent !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + + a + color: $yellow-accent !important + + .btn.btn-primary, + .btn.btn-large.btn-primary + background-color: $yellow-accent !important + border-color: $yellow-accent !important + color: $purple-dark !important + + &:hover, + &:focus, + &:active + background-color: $yellow-hover !important + border-color: $yellow-hover !important + color: $purple-dark !important + +// Final cascade guard for legacy styles overriding Did You Know dark theme +html.dark #page #project_container .project-show-redesign .project_row #did_you_know + background: transparent !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know h4 + color: #f8fafc !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know p, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know li, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .indent, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know span + color: #cbd5e1 !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know a + color: $yellow-accent !important + +// ============================================================================ +// Final Override Layer - Ensure our styles take precedence +// ============================================================================ +#project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: $gray-900 !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: $gray-700 !important + + .nutshell-list li + color: $gray-700 !important + + .content + color: $gray-700 !important + + a + color: $purple-primary !important + + .quick-ref-list li + .label + color: $gray-600 !important + + .value + color: $purple-primary !important + + a + color: $purple-primary !important + +// Dark mode final overrides +html.dark #project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: white !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: #cbd5e1 !important + + .nutshell-list li + color: $gray-300 !important + + .content + color: $gray-300 !important + + a + color: $yellow-accent !important + + .quick-ref-list li + .label + color: $gray-400 !important + + .value + color: $yellow-accent !important + + a + color: $yellow-accent !important + +// ============================================================================ +// Analysis Grid (Code, Activity, Community) +// ============================================================================ +.project-show-redesign + .analysis-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + margin-bottom: 48px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .analysis-card + display: flex + flex-direction: column + + .card-content + display: flex + flex-direction: column + height: 100% + flex: 1 + + .section-header + font-size: 18px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + @media (min-width: 768px) + font-size: 20px !important + + .chart-title + font-size: 14px !important + font-weight: 600 !important + margin-bottom: 12px + display: block + + a + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + color: $purple-primary !important + + .subsection-title + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-top: 16px + margin-bottom: 8px + + .chart-container + min-height: 160px + background: $gray-50 + border-radius: 8px + padding: 8px + margin-bottom: 16px + overflow: hidden + + @media (min-width: 768px) + min-height: 180px + + &.activity-chart + background: #f3f3f5 + padding: 12px + + #code_history_chart.chart-container + background: transparent + border-radius: 0 + padding: 0 + min-height: 200px + + #community_chart + margin-bottom: 16px + + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .highcharts-series-1 + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px + + // Chart header with zoom controls + .chart-header + display: flex + align-items: flex-start + justify-content: space-between + margin-bottom: 12px + gap: 8px + + .chart-title + margin-bottom: 0 + + .zoom-controls + display: flex + align-items: center + gap: 4px + flex-shrink: 0 + + .zoom-label + font-size: 12px + color: $gray-400 + margin-right: 4px + + .zoom-btn + padding: 2px 6px + border-radius: 4px + font-size: 12px + border: none + background: transparent + color: $gray-500 + cursor: pointer + transition: all 0.15s + + &:hover + color: $purple-primary + + &.active + background: $purple-primary + color: white + font-weight: 600 + + // Summary Grid (30 day, 12 month) + .summary-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-top: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + gap: 20px + + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 2px !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + margin-bottom: 6px !important + line-height: 1.25 + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + margin-bottom: 2px !important + line-height: 1.3 + + &:last-of-type + margin-bottom: 0 !important + + &.thirty-day-summary + .summary-header + margin-bottom: 1px !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 1px !important + line-height: 1.25 + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + margin-bottom: 8px !important + line-height: 1.4 + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + +// ============================================================================ +// Summary Cascade Guard (override generic gradient-card paragraph rules) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + margin-top: 0 !important + + .summary-timespan, + .summary-stat, + .summary-change + margin-top: 0 !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 2px !important + + .summary-change + margin-bottom: 8px !important + +html.dark #project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + color: white !important + + .summary-timespan + color: #94a3b8 !important + + .summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Languages + Contributors Cascade Guard (Redesign only) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + display: flex !important + align-items: flex-start !important + gap: 16px !important + margin-top: 8px !important + + .language-pie-container + width: 90px !important + height: 90px !important + flex: 0 0 90px !important + + .language-pie-chart + width: 90px !important + height: 90px !important + display: block !important + + .language-legend + flex: 1 1 auto !important + min-width: 0 !important + display: flex !important + flex-direction: column !important + gap: 6px !important + + .language-item + display: flex !important + align-items: center !important + gap: 8px !important + min-width: 0 !important + + .language-name + flex: 1 1 auto !important + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $gray-700 !important + + a + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + + .language-percent + flex: 0 0 auto !important + margin-left: auto !important + color: $gray-500 !important + + .community-chart-wrap + .col-md-12.manage_padding + padding: 0 !important + + .contributors-grid + display: grid !important + grid-template-columns: repeat(2, minmax(0, 1fr)) !important + column-gap: 8px !important + row-gap: 6px !important + margin-top: 6px !important + + .contributor-item + display: flex !important + align-items: center !important + gap: 6px !important + min-width: 0 !important + + .contributor-avatar + width: 24px !important + height: 24px !important + border-radius: 50% !important + flex: 0 0 24px !important + + .contributor-name + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + .language-legend + .language-item + .language-name + color: $gray-300 !important + + a + color: $yellow-accent !important + + .language-percent + color: $gray-400 !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + // Languages Section + .languages-content + display: flex + align-items: flex-start + gap: 16px + margin-top: 8px + + .language-pie-container + flex-shrink: 0 + width: 90px + height: 90px + + .language-pie-chart + width: 100% + height: 100% + display: block + + .language-legend + flex: 1 + display: flex + flex-direction: column + gap: 6px + + .language-item + display: flex + align-items: center + gap: 8px + font-size: 12px !important + + .language-color-box + width: 10px + height: 10px + border-radius: 2px + flex-shrink: 0 + + .language-name + flex: 1 + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + text-decoration: none + font-size: 12px !important + + &:hover + text-decoration: underline + color: $purple-primary !important + + .language-percent + color: $gray-500 !important + font-weight: 500 + margin-left: auto + font-size: 12px !important + + // Contributors Grid + .contributors-grid + display: grid + grid-template-columns: repeat(2, 1fr) + column-gap: 8px + row-gap: 6px + margin-top: 6px + + .contributor-item + display: flex + align-items: center + gap: 6px + + .contributor-avatar + width: 24px + height: 24px + border-radius: 50% + flex-shrink: 0 + + .contributor-name + font-size: 12px !important + line-height: 1.2 + color: $purple-primary + text-decoration: none + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + &:hover + text-decoration: underline + + // Ratings Section + .ratings-section + margin-top: 12px + padding-top: 12px + border-top: 1px solid $gray-200 + + .rating-content + .rating-info + font-size: 12px !important + line-height: 1.3 + color: $gray-700 + margin-bottom: 2px + + .rating-link + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + .rating-display + display: flex + align-items: center + gap: 6px + + .stars-container + display: inline-block + height: 23px + line-height: 23px + overflow: hidden + + .rating_stars, + [id$='_rating_stars'] + display: inline-block + height: 23px + line-height: 1 + + // Allow the rating stars helper's inline styles to work + span[style*='float: left'] + float: left !important + display: inline !important + height: 23px + + .rating-value + font-size: 12px !important + color: $gray-900 !important + font-weight: 600 !important + +// ============================================================================ +// Dark Mode - Analysis Grid +// ============================================================================ +html.dark .project-show-redesign + .section-header + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) + + .chart-title + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .subsection-title + color: white !important + + .chart-container + background: rgba(90, 42, 130, 0.15) + + &.activity-chart + background: #1D0631 + + #code_history_chart.chart-container + background: transparent + + #community_chart + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .highcharts-series-1 + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .zoom-controls + .zoom-label + color: #94a3b8 + + .zoom-btn + color: #94a3b8 + + &:hover + color: $yellow-accent + + &.active + background: $yellow-accent + color: #1D0631 + + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + .languages-content + .language-legend + .language-item + font-size: 12px !important + + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + &:hover + color: $yellow-accent !important + text-decoration: underline + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) + + .rating-content + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Ratings Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + margin-top: 12px !important + padding-top: 12px !important + border-top: 1px solid $gray-200 !important + + .subsection-title + margin-top: 0 !important + margin-bottom: 8px !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + + .rating-content + display: flex !important + flex-direction: column !important + gap: 2px !important + + .rating-top-row + display: flex !important + align-items: flex-start !important + justify-content: space-between !important + gap: 12px !important + + .rating-main + flex: 1 1 auto !important + min-width: 0 !important + + .rating-action + flex: 0 0 auto !important + text-align: right !important + white-space: nowrap !important + + .rating-link + display: inline-flex !important + align-items: center !important + justify-content: flex-end !important + gap: 6px !important + + .rating-action-icon + width: 12px !important + height: 12px !important + flex-shrink: 0 !important + + .rating-info + margin: 0 !important + font-size: 12px !important + line-height: 1.35 !important + color: $gray-700 !important + + &.rating-secondary + margin-top: 4px !important + + .rating-link + color: $purple-primary !important + text-decoration: none !important + font-weight: 500 !important + + &:hover + color: $purple-primary !important + text-decoration: underline !important + + .rating-display + display: flex !important + align-items: center !important + gap: 6px !important + margin-top: 2px !important + + .stars-container + display: inline-block !important + height: 23px !important + line-height: 23px !important + overflow: hidden !important + + .rating-value + font-size: 12px !important + line-height: 1.2 !important + font-weight: 600 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) !important + + .subsection-title + color: white !important + + .rating-content + .rating-top-row + .rating-action + .rating-info + color: $gray-300 !important + + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Dark Theme Final Guard (Licenses + Graphs) +// ============================================================================ +html.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + // Graph surfaces + chart typography + #activity_chart, + #community_chart, + #loc_chart, + #vulnerability_version_chart + .highcharts-background + fill: transparent !important + + .highcharts-plot-background, + .highcharts-plot-border, + .highcharts-label-box + fill: none !important + + .highcharts-axis-line, + .highcharts-tick, + .highcharts-grid-line + stroke: rgba(148, 163, 184, 0.35) !important + + .highcharts-axis-labels text, + .highcharts-axis-title, + .highcharts-legend-item text, + .highcharts-title, + .highcharts-subtitle + fill: #cbd5e1 !important + + .highcharts-tooltip-box + fill: #2D1548 !important + stroke: #5A2A82 !important + + .highcharts-tooltip text + fill: #f8fafc !important + +// ============================================================================ +// Summary Typography Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + margin: 0 0 2px 0 !important + color: $gray-900 !important + + p.summary-timespan, + p.summary-stat, + p.summary-change + font-size: 12px !important + line-height: 1.35 !important + margin-top: 0 !important + + p.summary-timespan + color: $gray-400 !important + margin-bottom: 6px !important + + p.summary-stat + color: $gray-700 !important + margin-bottom: 2px !important + + p.summary-change + margin-bottom: 8px !important + + .stat-number + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #94a3b8 !important + + p.summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Dark Mode Compatibility Guard (html.dark + body.dark) +// ============================================================================ +html.dark #project_container .project-show-redesign, +body.dark #project_container .project-show-redesign, +.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-detail-card + &.permitted, + &.forbidden, + &.required + border: 1px solid rgba(159, 122, 186, 0.25) !important + + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) !important + + h5, + h5 svg, + li svg + color: $yellow-accent !important + + li + color: $gray-300 !important + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) !important + + h5, + h5 svg, + li svg + color: #fb923c !important + + li + color: $gray-300 !important + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) !important + + h5, + h5 svg, + li svg + color: #facc15 !important + + li + color: $gray-300 !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + .footer-nav-section + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + svg + color: $yellow-accent !important + + h4 + color: #f8fafc !important + + ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + .accordion-header-btn + .header-left + svg + color: $yellow-accent !important + + span + color: #f8fafc !important + + .chevron + color: #94a3b8 !important + + .accordion-content-footer ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important diff --git a/app/decorators/analysis/chart.rb b/app/decorators/analysis/chart.rb index 193134c21..1e3b7015b 100644 --- a/app/decorators/analysis/chart.rb +++ b/app/decorators/analysis/chart.rb @@ -18,7 +18,7 @@ def series_and_range_data(default_options) end def first_ticks - series.first.ticks + series.first&.ticks end def min_month_as_ticks diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 942eadb86..6541fb0c5 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -9,9 +9,11 @@ def initialize(analysis) end def data - series_and_range_data(@defaults) + chart_data = series_and_range_data(@defaults) .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) .deep_merge(chart_watermark) + + apply_commits_palette(chart_data) end private @@ -25,6 +27,15 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.commits }] end + + def apply_commits_palette(chart_data) + series_config = chart_data[:series] || [] + series_config[0][:color] = '#2f7ed8' if series_config[0] + series_config[1][:color] = '#2f7ed8' if series_config[1] + chart_data + end end diff --git a/app/decorators/analysis/contributor_history_chart.rb b/app/decorators/analysis/contributor_history_chart.rb index d93c7bb05..e97911ba3 100644 --- a/app/decorators/analysis/contributor_history_chart.rb +++ b/app/decorators/analysis/contributor_history_chart.rb @@ -27,6 +27,8 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.contributors }] end end diff --git a/app/decorators/vulnerability/version_chart.rb b/app/decorators/vulnerability/version_chart.rb index dcaea58bd..aa2706ae3 100644 --- a/app/decorators/vulnerability/version_chart.rb +++ b/app/decorators/vulnerability/version_chart.rb @@ -27,7 +27,7 @@ def set_xaxis end def series - color_map = ['#0c1c33', '#183867', '#4b6b9a', '#a5b5cd', '#cce1e9'] + color_map = ['#4a148c', '#7b1fa2', '#9c27b0', '#ba68c8', '#ce93d8'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index] = { name: "CVE #{legend[0]}", @@ -41,7 +41,7 @@ def series end def bdsa_series - bdsa_color_map = ['rgb(90,16,12)', 'rgb(156,37,31)', 'rgb(231,140,135)', 'rgb(154,155,156)', '#D3D3D3'] + bdsa_color_map = ['#8b0000', '#c62828', '#f57c00', '#fbc02d', '#9e9e9e'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index + 5] = { name: "BDSA #{legend[0]}", diff --git a/app/views/layouts/responsive_project_layout.html.haml b/app/views/layouts/responsive_project_layout.html.haml index 63f832a98..b4d49b9a6 100644 --- a/app/views/layouts/responsive_project_layout.html.haml +++ b/app/views/layouts/responsive_project_layout.html.haml @@ -20,6 +20,6 @@ %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear - %footer.fluid_footer= render partial: 'layouts/partials/fluid_footer' - = render 'cookies/consent_banner' unless cookies[:cookie_consented] + %footer= render partial: 'layouts/partials/footer' + = render 'cookies/consent_banner' unless cookies[:cookie_consented] = render partial: 'layouts/partials/js_scripts' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index f6a029249..879072ea9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,7 +1,9 @@ :ruby content_for(:html_title) { t('.page_title', name: @project.name) } page_context[:select_top_menu_nav] = 'select_projects' - page_context[:select_footer_nav] = :project_summary + page_context[:select_footer_nav] = nil # Disable old footer navigation + page_context[:footer_menu_list] = nil # Disable old footer navigation + page_context[:page_header] = nil # Disable default header, using redesigned header instead - content_for :twitter_card do %meta{ content: 'summary', name: 'twitter:card' } @@ -12,75 +14,434 @@ %meta{ content: @project.logo.attachment.url(:med), name: 'twitter:image' } %meta{ content: 'https://www.openhub.net', name: 'twitter:domain' } -%div.separator-div - = project_analysis_timestamp(@project) - -.clear -= render partial: 'projects/show/is_a_duplicate', locals: { project: @project } - -.row.row-eq-height.margin_top_two.project_row - .col-md-4.project_summary_container - .well - %h4.text-left= t('.project_summary') - - size_breach = project_description_size_breached?(@project) - %section#project_summary{ itemprop: 'description', class: ('vertical_scroll_bar' if size_breach) } - - if @project.description.present? - = simple_format @project.description.strip_tags - - else - = t('.no_description') - = link_to t('.add_description'), edit_project_path(@project) - - %section#project_tags{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %h4.title= tag_icon_link(@project) - %p.tags - %span - - tags = @project.tag_list.split(' ') - - if tags.any? - = tag_links(tags) +.project-show-redesign{ itemscope: '', itemtype: 'http://schema.org/SoftwareSourceCode' } + / Project Header with Gradient + = render partial: 'projects/show/header_redesign' + + .project-content + / Stats Grid - 3 columns (Lines of Code, Contributors, Total Commits) + .stats-grid + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.code_total, delimiter: ',') + - else + N/A + .stat-label= t('.lines_of_code') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.committers_all_time) + - else + 0 + .stat-label= t('.contributors') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.commit_count) + - else + 0 + .stat-label= t('.total_commits') + + / Desktop: 3-column grid (Project Summary, In a Nutshell, Quick Reference) + .three-col-grid + / Project Summary + .gradient-card + .card-content + %h3= t('.project_summary') + - if @project.description.present? + %p= simple_format(@project.description.strip_tags) + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + - if @project.is_a_duplicate + %p.note + = t('.note') + There are two distinct + = link_to 'Mozilla Core', '#' + and + = link_to 'Mozilla Chrome', '#' + projects for complete coverage. + + %section#project_tags + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tags_path(names: tag), class: 'tag' + - else + %span.tag + = t('.no_tags') + - if @project.edit_authorized? + = link_to t('.add_tags'), project_tags_path(@project) + + / In a Nutshell + .gradient-card + .card-content + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + .content + representing + = link_to "#{number_with_delimiter @analysis.code_total} lines of code", languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.main_language + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + .content + is + = link_to "mostly written in #{@analysis.main_language.nice_name}", languages_summary_project_analysis_path(@project, id: 'latest') + with average comment density + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "23 6 13.5 15.5 8.5 10.5 1 18" } + %polyline{ points: "17 6 23 6 23 12" } + .content + has a + %span.font-semibold mature codebase + maintained by a + %span.font-semibold very large development team + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %polyline{ points: "12 6 12 12 16 14" } + .content + took an estimated + = link_to "#{number_with_delimiter @analysis.man_years.ceil} years of effort", estimated_cost_project_path(@project) + (COCOMO model) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" } + %line{ x1: "16", y1: "2", x2: "16", y2: "6" } + %line{ x1: "8", y1: "2", x2: "8", y2: "6" } + %line{ x1: "3", y1: "10", x2: "21", y2: "10" } + .content + - if @analysis.first_commit_time? + starting + %span.font-semibold= @analysis.first_commit_time.strftime('%B %Y') + - if @analysis.last_commit_time? + , ending + %span.font-semibold= "#{time_ago_in_words(@analysis.last_commit_time)} ago" + - else + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference + .gradient-card + .card-content + %h3= t('.quick_reference.quick_reference') + %ul.quick-ref-list + - if @project.organization + %li + %span.label= t('.organization') + %span.value + = link_to @project.organization.name, organization_path(@project.organization) + + %li + %span.label= t('.homepage') + %span.value + - homepage_link = @project.decorate.sorted_link_list['Homepage']&.first + - if homepage_link + = link_to homepage_link.url, itemprop: 'url' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + = truncate(homepage_link.url.gsub(/^https?:\/\//, ''), length: 30) + - else + N/A + + %li + %span.label= t('.documentation') + %span.value + - doc_link = @project.decorate.sorted_link_list['Documentation']&.first + - if doc_link + = link_to doc_link.url do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M4 19.5A2.5 2.5 0 0 1 6.5 17H20" } + %path{ d: "M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" } + View Docs + - else + N/A + + %li + %span.label= t('.code_location') + %span.value + - if @project.enlistments.present? + = link_to truncate(@project.enlistments.first.code_location.url, length: 40), @project.enlistments.first.code_location.url + - else + = link_to t('.add_code_location'), project_enlistments_path(@project) + + %li + %span.label= t('.similar_projects') + %span.value + %div#similar_projects{ data: { project_id: @project.to_param } } + + %li + %span.label= t('.managers') + %span.value + - if @project.active_managers.present? + = link_to "#{@project.active_managers.count} managers", project_managers_path(@project) + - else + = link_to t('.become_first', what: @project.name), new_project_manager_path(@project) + + / Mobile: Collapsible accordion cards + .mobile-accordion + / Project Summary - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h3= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @project.description.present? + %p= simple_format(@project.description.strip_tags, {}, wrapper_tag: 'span') + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + %section#project_tags_mobile + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tags_path(names: tag), class: 'tag' + - else + %span.tag= t('.no_tags') + + / In a Nutshell - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "12", y1: "20", x2: "12", y2: "10" } + %line{ x1: "18", y1: "20", x2: "18", y2: "4" } + %line{ x1: "6", y1: "20", x2: "6", y2: "16" } + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + / Add remaining factoids as in desktop version - else - = t('.no_tags') - - if @project.edit_authorized? - = link_to t('.add_tags'), project_tags_path(@project) - - if @project.project_badges.active.present? - %section#project_badges - %h4.title= link_to t('project_badges.index.title'), project_project_badges_path(@project) - = show_badges - = more_badges_link - - .col-md-4.nutshell_container - .well - - p_name = truncate_project_name(@project.name, 40) - %h4.nutshell_heading!= t('.in_a_nutshell', name: @project.name.length > 40 ? p_name : p_name + '...') - - if @analysis.present? - = render 'projects/show/factoids' - - else - = render partial: 'projects/show/no_analysis_summary' - .col-md-4.quick_reference_container - .well - = render partial: 'projects/show/quick_reference' - -.row.mezzo -= render partial: 'projects/show/licenses' - -.row.mezzo -.project_security - = render partial: 'projects/show/security' - -.row.mezzo -- if @analysis.empty? - .analysis_blank_container - .row.project_row - .col-md-12.blank_message_container - .col-md-8.analysis_alert_container - = render partial: 'projects/show/no_analysis_summary' - .col-md-4#proj_rating= render 'projects/show/community_rating', title: t('.community_rating'), score: @score - - if @project.enlistments.empty? && @analysis.empty? - = image_tag('sample_ohloh_analysis.jpg', class: 'col-md-12') -- else - = render partial: 'projects/show/analysis_summary' - -- if @scan_analytics.present? - .row.mezzo - #scan_data_row - .project_row= render partial: 'projects/show/scan_analytics_summary', collection: @scan_analytics, as: :scan_analytics + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + %h3= t('.quick_reference.quick_reference') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + %ul.quick-ref-list + / Same content as desktop version + - if @project.organization + %li + %span.label= t('.organization') + %span.value= link_to @project.organization.name, organization_path(@project.organization) + / Add remaining items + + / Licenses Section + = render partial: 'projects/show/licenses_redesign' + + / Project Security Section + = render partial: 'projects/show/security' + + / Code, Activity, Community Grid + - if @analysis.present? + = render partial: 'projects/show/analysis_summary_redesign' + + / Project Navigation Footer + .footer-nav-section + / Desktop: 4-column grid + .footer-nav-grid + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h4= t('.project_summary') + %ul + %li= link_to t('.news'), project_rss_subscriptions_path(@project) + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) + %li= link_to t('.related_projects'), similar_project_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %h4= t('.code_data') + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), security_project_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %h4= t('.scm_data') + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %h4= t('.community_data') + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) + + / Mobile: Accordion + .footer-nav-accordion + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %span= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.news'), project_rss_subscriptions_path(@project) + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) + %li= link_to t('.related_projects'), similar_project_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %span= t('.code_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), security_project_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %span= t('.scm_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span= t('.community_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) + +:javascript + function toggleAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } + + function toggleFooterAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } diff --git a/app/views/projects/show/_analysis_summary_redesign.html.haml b/app/views/projects/show/_analysis_summary_redesign.html.haml new file mode 100644 index 000000000..73d21d806 --- /dev/null +++ b/app/views/projects/show/_analysis_summary_redesign.html.haml @@ -0,0 +1,104 @@ +/ Code, Activity, Community Grid +.analysis-grid + / Code Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.code') + + %h4.chart-title + = link_to t('.lines_of_code'), languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.present? + - chart_url = lines_of_code_project_analysis_path(@project, id: 'latest') + .chart.watermark440#loc_chart{ datasrc: chart_url, style: 'width: 100%; height: 200px;' } + - else + %p.no-data= t('.no_code_locations') + + %h4.subsection-title= t('.languages') + = render partial: 'projects/show/languages_redesign' + + / Activity Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.activity') + + .chart-header + %h4.chart-title + = link_to t('.commits_per_month'), summary_project_commits_path(@project) + + + - if @analysis.present? + - chart_url = commits_history_project_analysis_path(@project, id: 'latest') + #activity_chart.chart.watermark440{ datasrc: chart_url, style: 'width: 100%; min-height: 210px' } + - else + %p.no-data= t('.no_commits') + + .summary-grid + .summary-section.thirty-day-summary + - thirty_day_summary = @analysis&.thirty_day_summary + %h5.summary-header= t('.thirty_day_summary') + %p.summary-timespan + - if thirty_day_summary && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 30.days).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.commits_count.to_i) + = t('.commits') + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.committer_count.to_i) + = t('.contributors') + + .summary-section + %h5.summary-header= t('.twelve_month_summary') + %p.summary-timespan + - if @analysis.twelve_month_summary.present? && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 12.months).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + - if @analysis.twelve_month_summary.present? + - previous_summary = @analysis.previous_twelve_month_summary + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.commits_count) + Commits + - if previous_summary&.data? + - diff = previous_summary.commits_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_commits = previous_summary.commits_count + - if prev_commits.to_i > 0 + (#{(diff.abs.to_f / prev_commits.to_f * 100).floor}%) + from previous 12 + + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.committer_count) + Contributors + - if previous_summary&.data? + - diff = previous_summary.committers_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_committers = previous_summary.committer_count + - if prev_committers.to_i > 0 + (#{(diff.abs.to_f / prev_committers.to_f * 100).floor}%) + from previous 12 months + + / Community Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.community') + + .community-chart-wrap + = render partial: 'projects/show/committers_per_month_chart' + + %h4.subsection-title= t('.most_recent_contributors') + = render partial: 'projects/show/community_recent_committers_redesign' + + .ratings-section + %h4.subsection-title= t('.ratings') + = render partial: 'projects/show/community_rating_redesign', locals: { score: @score } diff --git a/app/views/projects/show/_community_rating_redesign.html.haml b/app/views/projects/show/_community_rating_redesign.html.haml new file mode 100644 index 000000000..6095a4a7a --- /dev/null +++ b/app/views/projects/show/_community_rating_redesign.html.haml @@ -0,0 +1,28 @@ +.rating-content + .rating-top-row + .rating-main + - if @project.ratings.any? + %p.rating-info + = link_to t((@project.ratings.count == 1 ? 'projects.show.community_rating.one_user_rate' : 'projects.show.community_rating.users_rate'), n: @project.ratings.count), summary_project_reviews_path(@project), class: 'rating-link' + + .rating-display + .stars-container + != rating_stars('average_rating_stars', @project.rating_average.to_f || 0) + %span.rating-value + = "#{number_with_precision(@project.rating_average || 0, precision: 1)} /5.0" + - else + %p.rating-info= t('projects.show.community_rating.be_the_first') + + .rating-action + %p.rating-info + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.click_to_add') + + - unless current_user && Review.by_account(current_user).for_project(@project).exists? + %p.rating-info.rating-secondary + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.review_this_project') diff --git a/app/views/projects/show/_community_recent_committers_redesign.html.haml b/app/views/projects/show/_community_recent_committers_redesign.html.haml new file mode 100644 index 000000000..46aea669e --- /dev/null +++ b/app/views/projects/show/_community_recent_committers_redesign.html.haml @@ -0,0 +1,21 @@ +- if @analysis && @analysis.all_time_summary + - recent_contributors = @analysis.all_time_summary.recent_contribution_persons + - if recent_contributors.any? + .contributors-grid + - recent_contributors.take(8).each do |p| + :ruby + contrib_id = if p.account_id + Contribution.generate_id_from_project_id_and_account_id(@project.id, p.account_id) + else + p.id + end + person_name = obfuscate_email(p.person_name) + truncated_name = person_name.reverse.truncate(20).reverse + + .contributor-item + = image_tag avatar_img_path(p, 24), class: 'contributor-avatar', alt: person_name + = link_to truncated_name, project_contributor_path(@project.id, contrib_id), class: 'contributor-name', title: person_name + - else + %p.no-data= t('.no_one_recently') +- else + %p.no-data= t('.no_contributors') diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml new file mode 100644 index 000000000..6283af5ef --- /dev/null +++ b/app/views/projects/show/_header_redesign.html.haml @@ -0,0 +1,128 @@ +.project-header-gradient + / Decorative gradient circle is handled by CSS + + / Top Right Stats & Button (Desktop only, hidden on mobile) + .header-top-right + .activity-indicator-wrapper + / Activity indicator will be rendered here + - project_activity_level_class(@project, :thirtyfive) + - project_activity_level_text(@project, :thirtyfive) + + .stats-card + .stats-inner + / Users icon + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + .user-count + = number_with_delimiter(@project.user_count) + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + + .header-content + .header-main + .project-title-section + %h1{ itemprop: 'name' } + = @project.name + + .action-buttons + = link_to settings_project_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "3" } + %path{ d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" } + = t('.settings') + + - if @project.is_a_duplicate + - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) + = link_to edit_project_duplicate_path(@project, @project.is_a_duplicate), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + = t('.edit_duplicate') + - else + = link_to new_project_duplicate_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M4 15s1-1 4-1 5 2 9 2V3s-4 0-6-2-9-1-3 1-4 1-4 1z" } + %line{ x1: "4", y1: "22", x2: "4", y2: "15" } + = t('.report_duplicate') + + %p.project-description{ itemprop: 'description' } + - if @project.description.present? + = truncate(@project.description.strip_tags, length: 200) + - else + = t('.no_description') + + %p.timestamp + = t('.analyzed', time: time_ago_in_words(@project.analysis_updated_or_project_created_time)) + = t('.based_on_code', time: time_ago_in_words(@project.best_analysis.try(:logged_at))) if @project.best_analysis.try(:logged_at) + + / Mobile Stats & Button (shown only on mobile) + .header-mobile-stats + .activity-card-mobile + - project_activity_level_class(@project, :twentyfive) + - project_activity_level_text(@project, :twentyfive) + + .stats-card-mobile + .stats-top + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span.user-count= number_with_delimiter(@project.user_count) + %span.users-label= t('.users') + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn-mobile.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + +/ Login Modal for non-logged in users +- unless logged_in? + .modal.fade#LoginModal{ 'aria-hidden': 'true', 'aria-labelledby': 'LoginModalLabel', role: 'dialog' } + .modal-dialog.modal-sm + .modal-content + .modal-header + %button.close{ 'aria-label': 'Close', 'data-dismiss': 'modal', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %h4.modal-title#LoginModalLabel= t('.login_required') + .model-body + = render 'sessions/sign_in' diff --git a/app/views/projects/show/_languages_redesign.html.haml b/app/views/projects/show/_languages_redesign.html.haml new file mode 100644 index 000000000..e7f861312 --- /dev/null +++ b/app/views/projects/show/_languages_redesign.html.haml @@ -0,0 +1,20 @@ +- if @analysis && !@analysis.empty? + :ruby + languages_image_options = { project_id: @project.to_param, id: 'latest', height: 90, width: 90 } + language_data = Analysis::LanguagePercentages.new(@analysis).collection + + .languages-content + .language-pie-container + = image_tag languages_project_analysis_url(languages_image_options), class: 'language-pie-chart' + + .language-legend + - language_data.take(5).each do |id, name, attr| + - percent = attr[:percent] > 0 ? attr[:percent] : '<1' + - color = attr[:color] + .language-item + .language-color-box{ style: "background-color: ##{color}" } + %span.language-name + = id ? link_to(name, language_path(id)) : name + %span.language-percent= "#{percent}%" +- else + %p.no-data= t('.no_source') diff --git a/app/views/projects/show/_licenses_redesign.html.haml b/app/views/projects/show/_licenses_redesign.html.haml new file mode 100644 index 000000000..a1fe0ec22 --- /dev/null +++ b/app/views/projects/show/_licenses_redesign.html.haml @@ -0,0 +1,93 @@ +- if @project.licenses.count > 0 + .licenses-section + %h2.section-title= t('.licenses') + + - @project.licenses.each do |license| + .license-card.gradient-card + %button.license-header{ onclick: "toggleLicense(this)", class: "license-toggle-#{license.id}", type: "button" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" } + %h4= license.name + + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %polyline{ points: "6 9 12 15 18 9" } + + .license-content{ class: "license-content-#{license.id}" } + .license-details-grid + / Permitted Column + .license-detail-card.permitted + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } + = t('.permitted') + %ul + - license.permitted_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } + = p.name + + / Forbidden Column + .license-detail-card.forbidden + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = t('.forbidden') + %ul + - license.forbidden_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = p.name + + / Required Column + .license-detail-card.required + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %path{ d: "M12 8h.01" } + = t('.required') + %ul + - license.required_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %path{ d: "M12 8h.01" } + = p.name + + .license-disclaimer + %p= t('.disclaimer') + + .view-all-link + = link_to "#{t('.view_all_licenses')} →", project_licenses_path(@project) + +:javascript + function toggleLicense(button) { + // Only toggle on mobile (screen width < 1024px) + if (window.innerWidth >= 1024) { + return; + } + + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.classList.contains('expanded'); + + if (isExpanded) { + content.classList.remove('expanded'); + content.style.display = 'none'; + chevron.classList.remove('expanded'); + } else { + content.classList.add('expanded'); + content.style.display = 'block'; + chevron.classList.add('expanded'); + } + } diff --git a/app/views/projects/show/_security.html.haml b/app/views/projects/show/_security.html.haml index 0391ab395..ea15f7392 100644 --- a/app/views/projects/show/_security.html.haml +++ b/app/views/projects/show/_security.html.haml @@ -1,18 +1,84 @@ - blog_url = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Project-Security' -.row.project_row - - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report - .col-md-12.security_title_container - %h4.security-panel-title= t('.title') - .clearfix - .row-eq-height - .col-md-4#vulnerability_per_version - = render partial: 'projects/show/vulnerabilities_per_version' - .col-md-4#vulnerability-report - .well= render partial: 'projects/show/vulnerability_report' - .col-md-4#did_you_know - = render 'projects/show/site_features' - = link_to t('.blog_link'), blog_url, class: 'security_blog_link', target: '_blank' - - else - .col-md-12#did_you_know - %h4= t('.title_nvd') - = render 'projects/show/site_features' +- if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report + #security.security-section + %h2.section-title= t('.title', default: 'Project Security') + + .security-grid + / Vulnerabilities per Version Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerabilities_per_version.title', default: 'Vulnerabilities per Version'), security_project_path(@project), class: 'security-link' + %span.subtitle-detail= t('.vulnerabilities_per_version.title_detail', default: '(Most recent versions)') + - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) + - chart_url = recent_vulnerabilities_version_chart_project_path(@project) + #vulnerability_version_chart.chart-container{ data: { src: chart_url } } + - else + %p.no-data= t('vulnerabilities.index.no_vulnerability') + + / Vulnerability Report Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerability_report.title', default: 'Vulnerability Report'), security_project_path(@project), class: 'security-link' + + - pvr = @project.project_vulnerability_report + - if pvr + - pvr_link = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-About-the-Project-Vulnerability-Report' + + .vulnerability-indices + / Security Confidence Index + .index-section + .index-title= t('.vulnerability_report.pss.title', default: 'Security Confidence Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr).to_f}%", 'data-tipso': pss_content(pvr), 'data-tipso-title': t('.vulnerability_report.pss.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pss.poor') + %span.label-right= t('.vulnerability_report.pss.good') + + / Vulnerability Exposure Index + .index-section + .index-title= t('.vulnerability_report.pvs.title', default: 'Vulnerability Exposure Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr, pvs: true).to_f}%", 'data-tipso': pvs_content(pvr), 'data-tipso-title': t('.vulnerability_report.pvs.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pvs.poor') + %span.label-right= t('.vulnerability_report.pvs.good') + + .report-link-container + = link_to t('.vulnerability_report.blog_link', default: 'About Project Vulnerability Report'), pvr_link, class: 'report-link', target: '_blank' + - else + %p.no-data= t('.vulnerability_report.no_data') + + / Did You Know Card + .did-you-know-card + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe + + .security-footer-link + = link_to "#{t('.blog_link', default: 'About Project Security')} →", blog_url, target: '_blank' + +- else + #security.security-section + %h2.section-title= t('.title_nvd', default: 'This Project has No vulnerabilities Reported Against it') + .did-you-know-card.single + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe diff --git a/config/charting/analysis/options_based_on_type.yml b/config/charting/analysis/options_based_on_type.yml index 46f2b4a25..f6de6294a 100644 --- a/config/charting/analysis/options_based_on_type.yml +++ b/config/charting/analysis/options_based_on_type.yml @@ -24,9 +24,10 @@ commits_history: series: - name: 'Commits' id: 'commits' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' @@ -40,12 +41,12 @@ code_history: stacking: 'normal' series: - name: 'Code' - color: '#482268' + color: '#1D0631' id: 'code' - name: 'Comments' - color: '#008893' + color: '#5A2A82' - name: 'Blanks' - color: '#99dde3' + color: '#9F7ABA' tooltip: shared: true @@ -56,9 +57,10 @@ committer_history: series: - name: 'Committers' id: 'committers' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 723000732..06c7de8b9 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -92,6 +92,16 @@ en: show: page_title: "The %{name} Open Source Project on Open Hub" project_summary: "Project Summary" + overview: "Overview" + news: "News" + settings: "Settings" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + languages: "Languages" + commits: "Commits" + contributors: "Contributors" + users: "Users" + security: "Security" no_description: "No description has been added for this project." add_description: "Add description" tags: "Tags" @@ -99,6 +109,33 @@ en: add_tags: "add some now" community_rating: "Community Rating" in_a_nutshell: "In a Nutshell, %{name}" + lines_of_code: "Lines of Code" + contributors: "Contributors" + total_commits: "Total Commits" + note: "Note:" + organization: "Organization" + homepage: "Homepage" + documentation: "Documentation" + code_location: "Code Location" + add_code_location_link: "Add code location" + similar_projects_label: "Similar Projects" + managers_label: "Managers" + become_first_manager: "Become the first manager of %{what}" + news: "News" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + code_data: "Code Data" + languages_label: "Languages" + cost_estimates: "Cost Estimates" + security_label: "Security" + scm_data: "SCM Data" + commits_label: "Commits" + contributors_label: "Contributors" + community_data: "Community Data" + users_label: "Users" + ratings_reviews: "Ratings & Reviews" + user_contributor_locations: "User & Contributor Locations" + licenses_title: "Licenses" site_features: did_you_know: 'Did You Know' analysis_summary: @@ -113,6 +150,25 @@ en: add_a_code_location: "Add a code location" no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display the commits per month chart. %{link} to see this information." community: "Community" + analysis_summary_redesign: + code: "Code" + lines_of_code: "Lines of Code" + languages: "Languages" + no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display this chart." + activity: "Activity" + commits_per_month: "Commits per Month" + zoom: "Zoom" + no_commits: "No commit data available" + thirty_day_summary: "30 Day Summary" + twelve_month_summary: "12 Month Summary" + not_available: "not available" + commits: "Commits" + contributors: "Contributors" + community: "Community" + contributors_per_month: "Contributors per Month" + no_contributors: "No contributor data available" + most_recent_contributors: "Most Recent Contributors" + ratings: "Ratings" committers_per_month_chart: contributors_per_month: "Contributors per Month" add_a_code_location: "Add a code location" @@ -125,14 +181,33 @@ en: be_the_first: "Be the first to rate this project" click_to_edit: "Click to edit your rating" click_to_add: "Click to add your rating" + click_to_add_review: "Click to add your review" clear: "Clear" - review_this_project: "Review this Project!" + review_this_project: "Review this project" community_recent_committers: most_recent: "Most Recent Contributors" no_one_recently: "No one has contributed to this project recently" + header_redesign: + i_use_this: 'I Use This!' + you_use_this: 'You use this' + login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' + no_description: 'No description available' header: i_use_this: 'I Use This!' + you_use_this: 'You use this' login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' i_use_this: i_use_this: 'I Use This!' is_a_duplicate: @@ -179,7 +254,7 @@ en: vulnerability_report: no_data: 'No data available' pvs_tooltip: 'Score: %{score}
Percentage: %{percentage}
Rank: %{rank} of %{max_rank}' - title: 'Project Vulnerability Report' + title: 'Vulnerability Report' blog_link: 'About Project Vulnerability Report' pvs: title: 'Vulnerability Exposure Index' @@ -191,7 +266,7 @@ en: poor: 'Poor security track-record' vulnerabilities_per_version: title: 'Vulnerabilities per Version' - title_detail: '( last 10 releases )' + title_detail: '(Most recent versions)' no_data: 'No data available' thirty_day_summary: commit: "Commit" @@ -221,8 +296,15 @@ en: most_recent_commit: most recent commit %{time} security: title: 'Project Security' - title_nvd: 'This Project has No vulnerabilities Reported Against it' + title_nvd: 'This project has no vulnerabilities reported against it' blog_link: 'About Project Security' + licenses_redesign: + licenses: 'Licenses' + permitted: 'Permitted' + forbidden: 'Forbidden' + required: 'Required' + disclaimer: 'These details are provided for information only. No information here is legal advice and should not be used as such.' + view_all_licenses: 'View All Licenses' licenses: title: 'Licenses' all: 'All Licenses' From 7e7a694d1a02858a6cef3654d22ed33f7b564fb9 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 08/53] OTWO-7546 added layout for home page assets precompile issue and test cases fix Css fix for after sign in user --- app/assets/javascripts/dropdown_handler.js | 71 ++ app/assets/javascripts/mobile_menu.js | 86 ++ app/assets/javascripts/rotating_stats.js | 29 + app/assets/javascripts/theme_toggle.js | 75 ++ app/assets/stylesheets/dark_theme.sass | 150 +++ app/assets/stylesheets/footer.sass | 241 +++-- app/assets/stylesheets/home.sass | 995 +++++++++++++++--- app/assets/stylesheets/oh-styles.sass | 17 +- app/assets/stylesheets/page.sass | 888 +++++++++++++--- app/views/home/_join_now_home.html.haml | 44 +- app/views/home/_top_contributors.html.haml | 33 + app/views/home/_user_journeys.html.haml | 272 +++++ app/views/home/_whats_new.html.haml | 11 +- app/views/home/index.html.haml | 102 +- app/views/layouts/partials/_footer.html.haml | 81 +- app/views/layouts/partials/_mast.html.haml | 131 ++- config/initializers/assets.rb | 2 +- config/locales/home.en.yml | 11 + test/controllers/home_controller_test.rb | 8 +- test/controllers/licenses_controller_test.rb | 2 +- .../organizations_controller_test.rb | 2 +- test/controllers/projects_controller_test.rb | 2 +- 22 files changed, 2764 insertions(+), 489 deletions(-) create mode 100644 app/assets/javascripts/dropdown_handler.js create mode 100644 app/assets/javascripts/mobile_menu.js create mode 100644 app/assets/javascripts/rotating_stats.js create mode 100644 app/assets/javascripts/theme_toggle.js create mode 100644 app/assets/stylesheets/dark_theme.sass create mode 100644 app/views/home/_top_contributors.html.haml create mode 100644 app/views/home/_user_journeys.html.haml diff --git a/app/assets/javascripts/dropdown_handler.js b/app/assets/javascripts/dropdown_handler.js new file mode 100644 index 000000000..f9aea4967 --- /dev/null +++ b/app/assets/javascripts/dropdown_handler.js @@ -0,0 +1,71 @@ +// Dropdown menu handler for logged user menu +(function($) { + 'use strict'; + + function initDropdown() { + var $dropdown = $('#logged_user_menu'); + + if ($dropdown.length === 0) { + return; // Dropdown not found (user not logged in) + } + + var $toggle = $dropdown.find('.dropdown-toggle'); + var $menu = $dropdown.find('.dropdown-menu'); + + // Remove any existing event handlers + $toggle.off('click.userDropdown'); + $(document).off('click.userDropdown keydown.userDropdown'); + $menu.find('a').off('click.userDropdown'); + + // Toggle dropdown on click + $toggle.on('click.userDropdown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var wasOpen = $dropdown.hasClass('open'); + + // Close all dropdowns first + $('.dropdown').removeClass('open'); + + // Toggle this dropdown + if (!wasOpen) { + $dropdown.addClass('open'); + } + + return false; + }); + + // Close dropdown when clicking outside + $(document).on('click.userDropdown', function(e) { + var $target = $(e.target); + if (!$dropdown.is($target) && $dropdown.has($target).length === 0) { + $dropdown.removeClass('open'); + } + }); + + // Close dropdown when clicking a menu item + $menu.find('a').on('click.userDropdown', function() { + setTimeout(function() { + $dropdown.removeClass('open'); + }, 150); + }); + + // Close dropdown on ESC key + $(document).on('keydown.userDropdown', function(e) { + if (e.keyCode === 27) { + $dropdown.removeClass('open'); + } + }); + } + + // Initialize when DOM is ready + $(document).ready(function() { + initDropdown(); + }); + + // Re-initialize on Turbolinks page change + $(document).on('page:change', function() { + initDropdown(); + }); + +})(jQuery); diff --git a/app/assets/javascripts/mobile_menu.js b/app/assets/javascripts/mobile_menu.js new file mode 100644 index 000000000..d57090864 --- /dev/null +++ b/app/assets/javascripts/mobile_menu.js @@ -0,0 +1,86 @@ +// Mobile Menu Toggle Functionality +var MobileMenu = { + init: function() { + console.log('MobileMenu: Initializing...'); + this.bindEvents(); + }, + + toggleMenu: function() { + console.log('MobileMenu: Toggle clicked!'); + var mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenu) { + if (mobileMenu.classList.contains('show')) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } else { + mobileMenu.classList.add('show'); + console.log('MobileMenu: Menu opened'); + } + } else { + console.log('MobileMenu: WARNING - Mobile menu element not found!'); + } + }, + + closeMenu: function() { + var mobileMenu = document.getElementById('mobile-menu'); + if (mobileMenu) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } + }, + + bindEvents: function() { + var self = this; + var toggleBtn = document.getElementById('mobile-menu-toggle'); + + console.log('MobileMenu: Toggle button found?', !!toggleBtn); + + if (toggleBtn) { + toggleBtn.onclick = function(e) { + e.preventDefault(); + self.toggleMenu(); + return false; + }; + console.log('MobileMenu: Click handler attached'); + } else { + console.log('MobileMenu: WARNING - Mobile menu toggle button not found!'); + } + + // Close menu when clicking on a link + var mobileMenuLinks = document.querySelectorAll('.mobile-menu-items a, .mobile-menu-user a, .mobile-menu-signin a'); + console.log('MobileMenu: Found', mobileMenuLinks.length, 'menu links'); + + for (var i = 0; i < mobileMenuLinks.length; i++) { + mobileMenuLinks[i].onclick = function() { + self.closeMenu(); + }; + } + + // Close menu when window is resized to desktop size + window.addEventListener('resize', function() { + if (window.innerWidth >= 1024) { + self.closeMenu(); + } + }); + } +}; + +// Initialize on DOM ready +if (typeof $ !== 'undefined') { + $(document).on('page:change', function() { + console.log('MobileMenu: page:change event fired'); + MobileMenu.init(); + }); + + $(document).ready(function() { + console.log('MobileMenu: document.ready event fired'); + MobileMenu.init(); + }); +} else { + // Fallback if jQuery is not available + document.addEventListener('DOMContentLoaded', function() { + console.log('MobileMenu: DOMContentLoaded event fired'); + MobileMenu.init(); + }); +} diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js new file mode 100644 index 000000000..39a368b7b --- /dev/null +++ b/app/assets/javascripts/rotating_stats.js @@ -0,0 +1,29 @@ +// Rotating Stats Animation for Homepage +document.addEventListener('DOMContentLoaded', function() { + var globalStats = document.getElementById('global_statistics'); + + if (!globalStats) return; + + var statElements = globalStats.querySelectorAll('p'); + + if (statElements.length === 0) return; + + var currentIndex = 0; + + // Show first stat initially + statElements[0].classList.remove('hide'); + + // Rotate stats every 2 seconds + setInterval(function() { + // Hide current stat + statElements[currentIndex].classList.add('hide'); + + // Move to next stat + currentIndex = (currentIndex + 1) % statElements.length; + + // Show next stat after a brief delay + setTimeout(function() { + statElements[currentIndex].classList.remove('hide'); + }, 300); + }, 2000); +}); diff --git a/app/assets/javascripts/theme_toggle.js b/app/assets/javascripts/theme_toggle.js new file mode 100644 index 000000000..44c98daaf --- /dev/null +++ b/app/assets/javascripts/theme_toggle.js @@ -0,0 +1,75 @@ +// Theme Toggle Functionality +var ThemeToggle = { + init: function() { + console.log('ThemeToggle: Initializing...'); + var savedTheme = this.getSavedTheme(); + console.log('ThemeToggle: Saved theme is', savedTheme); + this.applyTheme(savedTheme); + this.bindEvents(); + }, + + getSavedTheme: function() { + try { + return localStorage.getItem('theme') || 'light'; + } catch (e) { + return 'light'; + } + }, + + applyTheme: function(theme) { + var html = document.documentElement; + var moonIcon = document.getElementById('theme-icon-moon'); + var sunIcon = document.getElementById('theme-icon-sun'); + + if (theme === 'dark') { + html.classList.add('dark'); + if (moonIcon) moonIcon.classList.add('hidden'); + if (sunIcon) sunIcon.classList.remove('hidden'); + } else { + html.classList.remove('dark'); + if (moonIcon) moonIcon.classList.remove('hidden'); + if (sunIcon) sunIcon.classList.add('hidden'); + } + + try { + localStorage.setItem('theme', theme); + } catch (e) { + console.log('Could not save theme preference'); + } + }, + + toggleTheme: function() { + var currentTheme = this.getSavedTheme(); + var newTheme = currentTheme === 'light' ? 'dark' : 'light'; + this.applyTheme(newTheme); + }, + + bindEvents: function() { + var self = this; + var themeToggleBtn = document.getElementById('theme-toggle'); + + console.log('ThemeToggle: Theme toggle button found?', !!themeToggleBtn); + + if (themeToggleBtn) { + themeToggleBtn.onclick = function(e) { + e.preventDefault(); + console.log('ThemeToggle: Toggle clicked!'); + self.toggleTheme(); + return false; + }; + console.log('ThemeToggle: Click handler attached'); + } else { + console.log('ThemeToggle: WARNING - Theme toggle button not found!'); + } + } +}; + +// Initialize on DOM ready +$(document).on('page:change', function() { + ThemeToggle.init(); +}); + +// Also initialize immediately if DOM is already loaded +$(document).ready(function() { + ThemeToggle.init(); +}); diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass new file mode 100644 index 000000000..e240d718d --- /dev/null +++ b/app/assets/stylesheets/dark_theme.sass @@ -0,0 +1,150 @@ +// Dark Theme Styles +// Applied when .dark class is added to html element + +html.dark + // Body and background colors + body + background-color: #0f172a !important + color: #e2e8f0 !important + + // Page container + #page, .container#page + background-color: #0f172a !important + + // Header stays with purple gradient (no change needed) + // Footer stays with purple gradient (no change needed) + + // Content areas + #page-contents, #page_contents + background-color: #1e293b + color: #e2e8f0 + + // Cards and wells + .well + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Tables + table + background-color: #1e293b + color: #e2e8f0 + td, th + border-color: #334155 !important + color: #e2e8f0 !important + + // Links + a + color: #60a5fa !important + &:hover + color: #93c5fd !important + + // Buttons (keep primary yellow button, update others) + .btn + &:not(.btn-primary):not(.btn-success) + background-color: #334155 !important + color: #e2e8f0 !important + &:hover + background-color: #475569 !important + + // Forms + input, textarea, select + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + &::placeholder + color: #94a3b8 !important + + // Billboard/Showcase + .billboard + background-color: #1e293b !important + color: #e2e8f0 !important + + .showcase + background-color: #0f172a !important + + // Project container + #project_container + background-color: #0f172a !important + color: #e2e8f0 !important + + // Headings + h1, h2, h3, h4, h5, h6 + color: #f1f5f9 !important + + // Alerts + .alert + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Dropdown menus + .dropdown-menu + background-color: #1e293b !important + border-color: #334155 !important + li a + color: #e2e8f0 !important + &:hover + background-color: #334155 !important + + // Home page sections + .top_ten, .top_ten.middle, .top_ten.last + background-color: #1e293b !important + color: #e2e8f0 !important + + .home_page_row .col-md-4 + background-color: #1e293b !important + + // Navigation menu - keep white text on purple gradient + .navbar, #navbar-inner + .new_main_menu li a + color: white !important + + // Search inputs + .for_search_all_code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Badges + .badge + background-color: #334155 !important + color: #e2e8f0 !important + + // Panels + .panel + background-color: #1e293b !important + border-color: #334155 !important + .panel-heading + background-color: #334155 !important + color: #e2e8f0 !important + .panel-body + background-color: #1e293b !important + color: #e2e8f0 !important + + // Code blocks + pre, code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Borders + .mezzo + border-top-color: #334155 !important + + .right_border + border-right-color: #334155 !important + + // Text colors + .signature_color + color: #60a5fa !important + + // Keep error colors visible + .error + color: #ef4444 !important + + .bad + color: #f87171 !important + + .good + color: #4ade80 !important diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index dccca4602..1baf71240 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -1,81 +1,186 @@ footer - @include footer-colors + // Figma design: purple gradient background + position: relative + overflow: hidden + background: linear-gradient(to right, #1D0631, #5A2A82) !important + color: white !important + margin: 0 + padding: 0 + width: 100% + max-width: 100% + .footer-container + position: relative + max-width: 1280px + margin: 0 auto + padding: 48px 32px 24px 32px - margin: 20px 0 0 0 - min-height: 250px - padding-top: 10px - width: 960px - - @media (min-width: 320px) and (max-width: 480px) - width: auto - min-height: 194px !important - padding-top: 0px !important - margin: 0px 0 0 0 !important + // Mobile: < 640px + @media (max-width: 639px) + .footer-container + padding: 32px 16px 16px 16px + .footer-grid + gap: 32px + margin-bottom: 24px .footer-left - margin-left: 5px !important - margin-top: 20px !important - min-height: 150px !important - .footer-mid - line-height: 15px - margin-left: 30px !important - font-size: 8px !important - width: 32% !important - .footer-right - font-size: 8px !important - padding-right: 2px !important - width: 31% !important + .logo_img + height: 28px + .footer-mid, .footer-right + h3 + font-size: 10px + margin-bottom: 12px + a + font-size: 10px + li + margin-bottom: 8px .footer-bottom - margin-left: 0px !important - margin-top: 0px !important - @media (min-width: 540px) and (max-width: 1024px) - width: auto + .footer-bottom-content + gap: 8px + .copyright + font-size: 9px + text-align: center + .follow-us + span + font-size: 10px + a + width: 28px + height: 28px + i + font-size: 14px + + // Tablet: 640px - 767px + @media (min-width: 640px) and (max-width: 767px) + .footer-container + padding: 40px 24px 20px 24px + .footer-grid + gap: 40px + margin-bottom: 28px + + // Tablet landscape: 768px - 1023px + @media (min-width: 768px) and (max-width: 1023px) + .footer-container + padding: 44px 32px 22px 32px + .footer-grid + gap: 36px + + .footer-grid + display: grid + grid-template-columns: 1fr + gap: 48px + margin-bottom: 32px + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 48px .footer-left - float: left - margin-top: 20px - margin-left: 30px - width: 15% + display: flex + align-items: flex-start + justify-content: center + @media (min-width: 768px) + justify-content: flex-start a .logo_img - width: 100% - height: 100% - - .footer-mid - float: left - margin-top: 20px - margin-left: 120px - margin-right: 40px - width: 20% - font-size: 12px - a - @include footer-colors - padding: 4px 4px + width: auto + height: 32px + object-fit: contain + @media (min-width: 640px) + height: 40px - .footer-right - float: left - margin-top: 20px - margin-bottom: 20px - width: 30% - font-size: 12px - padding-right: 60px - text-align: left + .footer-mid, .footer-right + h3 + font-size: 11px + font-weight: 600 + color: white !important + margin-bottom: 16px + text-transform: uppercase + letter-spacing: 0.05em + @media (min-width: 640px) + font-size: 13px + margin-bottom: 12px + @media (min-width: 768px) + margin-bottom: 16px + ul + list-style: none + margin: 0 + padding: 0 + li + margin-bottom: 10px + @media (min-width: 640px) + margin-bottom: 8px + @media (min-width: 768px) + margin-bottom: 10px + p + margin: 0 0 10px 0 + @media (min-width: 640px) + margin: 0 0 8px 0 + @media (min-width: 768px) + margin: 0 0 10px 0 a - @include footer-colors - padding: 4px 6px + color: #d1d5db !important + text-decoration: none + font-size: 11px + font-weight: 400 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + &:hover + color: white !important .footer-bottom - @include footer-copyright-colors - - float: left - margin-top: 10px - margin-left: 30px - margin-bottom: 2em - bottom: 0 - a - @include footer-copyright-colors + border-top: 1px solid rgba(255, 255, 255, 0.1) + padding: 12px 0 + @media (min-width: 640px) + padding: 16px 0 + @media (min-width: 768px) + padding: 24px 0 + .footer-bottom-content + display: flex + flex-direction: column + align-items: center + justify-content: space-between + gap: 12px + @media (min-width: 640px) + flex-direction: row + .copyright + color: #9ca3af !important + font-size: 10px + order: 2 + @media (min-width: 640px) + font-size: 12px + order: 1 + .follow-us + display: flex + align-items: center + gap: 8px + order: 1 + @media (min-width: 640px) + order: 2 + span + color: #d1d5db !important + font-size: 12px + font-weight: 500 + @media (min-width: 640px) + font-size: 14px + a + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + transition: background-color 0.3s ease + &:hover + background-color: rgba(255, 255, 255, 0.2) + i + color: #d1d5db + font-size: 16px + transition: color 0.3s ease + &:hover i + color: white footer.fluid_footer width: auto + background: linear-gradient(to right, #1D0631, #5A2A82) !important .footer-left width: 12% ul @@ -83,16 +188,22 @@ footer.fluid_footer a padding: 1rem 2rem font-weight: bold + color: rgba(209, 213, 219, 1) !important + &:hover + color: white !important .footer-mid width: 25% text-align: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-right width: 40% float: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-bottom float: left margin-botton: 2em !important + color: rgba(156, 163, 175, 1) !important diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4c18ddd8c..0422db618 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,153 +1,161 @@ -.showcase - padding-top: 20px - padding-bottom: 30px - width: 980px - margin-left: -15px - padding-left: 5px - padding-right: 5px -@media only all and (min-width: 320px) and (max-width: 480px) - .showcase - width: auto - margin-left: 0px - .billboard - height: 185px !important - -.billboard - @include billboard-background-color - color: white - height: 160px - padding-top: 30px - padding-bottom: 30px +// Hero Section - Updated to match Figma exactly +.hero_section + padding-top: 60px + padding-bottom: 40px + padding-left: 16px + padding-right: 16px + background: #fff !important + transition: background 0.3s ease + @media (min-width: 640px) + padding-top: 72px + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-top: 88px + padding-left: 32px + padding-right: 32px + // Dark mode support + html.dark & + background: #1D0631 !important + +.hero_container + max-width: 80rem + margin: 0 auto + +// Hero Header - Figma Specs +.hero_header text-align: center + margin-bottom: 24px + +.hero_title + font-size: 1.875rem !important + font-weight: 700 + color: #5A2A82 + margin-bottom: 8px + line-height: 2.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 2.25rem !important + line-height: 2.5rem !important + @media (min-width: 768px) + font-size: 3rem !important + line-height: 1 !important + // Dark mode support + html.dark & + color: #ffffff + +.hero_subtitle + font-size: 0.875rem !important + color: #6b7280 + max-width: 42rem + margin: 0 auto + line-height: 1.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 1rem !important + line-height: 1.5rem !important + // Dark mode support + html.dark & + color: #9ca3af + +// Search Container - Figma Specs +.search_container + max-width: 48rem + margin: 0 auto 20px + +.search_form + margin-bottom: 0 + position: relative + +.search_input_wrapper + position: relative + +.search_icon + position: absolute + top: 50% + transform: translateY(-50%) + left: 8px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 1 + transition: color 0.3s ease + @media (min-width: 640px) + left: 24px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + +.search_input width: 100% - z-index: 2 - .billboard_search - // margin-top: 80px - .discover - padding: 0 - .discover_msg - font-size: 2.1em !important - font-weight: 400!important - margin-top: -10px - margin-bottom: 24px - h1 - line-height: 1.2em !important -@media only all and (min-width: 320px) and (max-width: 480px) - .billboard - .discover_msg - font-size: 13px !important - #global_statistics - p - font-size: 10px !important -@media (min-width: 540px) and (max-width: 1024px) - .showcase - width: auto - margin-left: 0px -.global_stats - margin-top: 25px -#icon_text - font-size: 1.6em - color: #646E81 - margin-left: -27px - cursor: pointer + padding: 10px 12px 10px 32px + font-size: 11px !important + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #fff + color: #111827 + font-family: inherit + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.2s ease + &::placeholder + color: #9ca3af + &:hover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + &:focus + border-color: #0E4B7A + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + &::placeholder + color: #d1d5db + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff + border-color: #374151 + &::placeholder + color: #6b7280 + &:focus + border-color: #2E8B9E + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// Rotating Stats - Figma Animation +.rotating_stats + text-align: center + margin-top: 12px + height: 20px #global_statistics - padding-top: 1px p - margin-top: -20px - text-align: center - font-size: 14px -.join_now - @include join-now-header-colors - padding-bottom: 5px - padding-left: 12px -.join_now_home - padding-top: 10px - font-size: 1.4em - p - font-size: 14px !important - .join_now_color - @include join-now-section-colors - li - font-size: 0.5em !important - height: 30px - width: 30px -.home_page_row - .whats_new - margin-right: -27px !important - .col-md-8 - h2 - font-weight: 500 !important - margin-right: -40px !important - margin-left: 30px !important - text-indent: -10px !important - .col-md-4 - @include site-well-color - padding-bottom: 20px - h2 - font-weight: 500 !important - margin-left: 5px !important - text-indent: -10px !important - -@media only all and (min-width: 320px) and (max-width: 480px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important - .for_search_all_code - width: auto !important - .join_now_home - padding-top: 5px - font-size: 12px - li - height: 10px - width: 10px -@media (min-width: 540px) and (max-width: 1024px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important -.btn.btn_join_now - @include join-now-button-colors - font-family: 'Roboto' !important - font-size: 22px !important - font-weight: bold !important - margin-left: 60px !important - width: 161px !important -::-webkit-input-placeholder - @include site-placeholder-color -\:-moz-placeholder - @include site-placeholder-color - opacity: 1 -::-moz-placeholder - @include site-placeholder-color - opacity: 1 -\:-ms-input-placeholder - @include site-placeholder-color - -input:focus::-webkit-input-placeholder - color: transparent -input:focus::-moz-placeholder - color: transparent -input:focus::-ms-input-placeholder - color: transparent -@media only all and (min-width: 320px) and (max-width: 480px) - .btn.btn_join_now - margin-left: 0px !important - width: 135px !important - font-size: 15px !important + font-size: 12px !important + font-weight: 500 + background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) + -webkit-background-clip: text + -webkit-text-fill-color: transparent + background-clip: text + margin: 0 + line-height: 1.5 + transition: opacity 0.3s ease + &.hide + opacity: 0 + display: block + &:not(.hide) + opacity: 1 + display: block + // Dark mode support + html.dark & + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) +// Old join_now and home_page_row styles removed - using new card-based design +// Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px - margin-left: -14px + margin-left: 0 + padding: 0 20px + width: 100% .top_ten_main padding-left: 40px !important padding-bottom: 6px !important @@ -191,30 +199,11 @@ input:focus::-ms-input-placeholder a.top_ten_icon p opacity: .75 background-color: #999 -.for_search_all_code - @include site-placeholder-color - height: 23px !important - // margin-top: -108px - width: 413px +// Old .for_search_all_code removed - using new .search_input instead .top_ten_link a border: 0 -.icon_search - font-size: 14px - font-weight: 300 - margin-top: -37px - margin-left: -71px - text-align: center -.wh_new - @include whats-new-header-colors - width: 100% - padding-bottom: 5px -.common - padding-left: 6px - padding-bottom: 3px - font-size: 1.4em - font-weight: normal - color: #000000 +// Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors .most_active_projects @@ -239,4 +228,684 @@ button #no-background background-color: white !important +// Feature Cards Section - Figma Specs +.features_section + max-width: 64rem + margin: 0 auto 32px + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 20px + +.feature_card + text-align: center + margin-bottom: 16px + @media (min-width: 768px) + margin-bottom: 0 + +.feature_icon + width: 40px + height: 40px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin: 0 auto 12px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.feature_icon_yellow + background: linear-gradient(135deg, #facc15 0%, #f97316 100%) + +.feature_icon_blue + background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) + +.feature_icon_green + background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%) + +.feature_title + font-size: 12px !important + font-weight: 600 + color: #111827 + margin-bottom: 4px + line-height: 1.3 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #ffffff + +.feature_description + font-size: 10px !important + color: #6b7280 + line-height: 1.5 + padding: 0 8px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af + +// Quick Stats Bar - Figma Specs (grid-cols-2 md:flex) +.quick_stats_bar + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + max-width: 48rem + margin: 0 auto + padding: 16px + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) + backdrop-filter: blur(4px) + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.6) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + @media (min-width: 768px) + display: flex + align-items: center + justify-content: center + gap: 24px + padding: 12px 24px + // Dark mode support - Yellow background with purple text + html.dark & + background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) + border-color: #ffb91a + +.stat_item + text-align: center + +.stat_value + font-size: 16px !important + font-weight: 700 + color: #5A2A82 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_label + font-size: 8px !important + color: #6b7280 + text-transform: uppercase + font-weight: 500 + letter-spacing: 0.05em + margin-top: 2px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 9px !important + @media (min-width: 768px) + font-size: 10px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_divider + display: none + transition: background 0.3s ease + @media (min-width: 768px) + display: block + width: 1px + height: 32px + background: #d1d5db + opacity: 0.3 + // Dark mode - Purple divider on yellow background + html.dark & + @media (min-width: 768px) + background: #5A2A82 + opacity: 0.3 + +// Content Section +.content_section + padding: 32px 0 + background: white !important + transition: background 0.3s ease + // Dark mode support + html.dark & + background: #1D0631 !important + +.contributors_column, .whats_new_column + @media (max-width: 768px) + margin-bottom: 24px + +.whats_new_join_stack + display: flex + flex-direction: column + gap: 24px + +// Top Contributors Card +.top_contributors_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + > div + background: white + border-radius: 14px + height: 100% + +.card_header + display: flex + align-items: flex-start + gap: 16px + padding: 24px + padding-bottom: 16px + background: white + +.header_icon + width: 48px + height: 48px + background: rgba(90, 42, 130, 0.1) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: #5A2A82 + +.header_content + flex: 1 + +.card_title + font-size: 18px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.card_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + line-height: 1.5 + +.contributors_list + padding: 16px + flex: 1 + overflow-y: auto + +.contributor_item + background: linear-gradient(90deg, #f9fafb 0%, #f3f4f6 100%) + border-radius: 12px + padding: 12px + border: 1px solid #e5e7eb + cursor: pointer + transition: all 0.3s + margin-bottom: 8px + display: flex + align-items: center + gap: 12px + &:hover + background: linear-gradient(90deg, #f5f3ff 0%, #ede9fe 100%) + border-color: #5A2A82 + +.contributor_avatar + flex-shrink: 0 + .avatar_image + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + border: 2px solid #9ca3af + +.contributor_info + flex: 1 + min-width: 0 + +.contributor_name_rank + display: flex + align-items: center + gap: 8px + margin-bottom: 2px + +.contributor_name + font-size: 14px + font-weight: 600 + color: #5A2A82 + margin: 0 + .contributor_link + color: inherit + text-decoration: none + &:hover + text-decoration: underline + +.contributor_rank + font-size: 9px + color: rgba(90, 42, 130, 0.6) + text-transform: uppercase + letter-spacing: 0.05em + flex-shrink: 0 + +.contributor_stats + display: flex + align-items: center + gap: 12px + font-size: 10px + color: rgba(90, 42, 130, 0.7) + .stat_item + display: flex + align-items: center + gap: 4px + i + font-size: 10px + +.contributor_arrow + flex-shrink: 0 + color: rgba(90, 42, 130, 0.4) + transition: color 0.3s + i + font-size: 16px + +.contributor_item:hover .contributor_arrow + color: #5A2A82 + +.view_all_link + padding: 16px + .btn_view_all + width: 100% + display: flex + align-items: center + justify-center + gap: 8px + font-size: 14px + font-weight: 600 + color: #5A2A82 + transition: color 0.3s + padding: 12px + border-top: 1px solid rgba(90, 42, 130, 0.2) + text-decoration: none + i + font-size: 16px + &:hover + color: #1D0631 + +// What's New Card +.whats_new_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.card_header_section + padding: 24px + padding-bottom: 16px + background: white + +.whats_new_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.whats_new_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + +.featured_image_section + padding: 0 24px 24px + +.featured_image_link + display: block + position: relative + border-radius: 12px + overflow: hidden + transition: transform 0.3s + &:hover + transform: scale(1.02) + .featured_image + transform: scale(1.05) + +.featured_image + width: 100% + height: auto + transition: transform 0.3s + +// Join Now Card +.join_now_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.join_header + display: flex + align-items: center + gap: 12px + padding: 24px + padding-bottom: 16px + +.join_icon + width: 48px + height: 48px + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: white + +.join_header_text + flex: 1 + +.join_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 2px + +.join_subtitle + font-size: 12px + color: #6b7280 + +.benefits_list + padding: 0 24px + margin-bottom: 24px + +.benefit_item + display: flex + align-items: flex-start + gap: 12px + margin-bottom: 12px + +.benefit_icon + width: 32px + height: 32px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + margin-top: 2px + +.benefit_icon_award + background: rgba(90, 42, 130, 0.1) + i + font-size: 16px + color: #5A2A82 + +.benefit_icon_trending + background: rgba(29, 6, 49, 0.1) + i + font-size: 16px + color: #1D0631 + +.benefit_content + flex: 1 + +.benefit_title + font-size: 13px + font-weight: 600 + color: #111827 + margin-bottom: 2px + +.benefit_description + font-size: 11px + color: #6b7280 + line-height: 1.5 + +.join_cta + padding: 0 24px 24px + +.btn_join_cta + width: 100% + padding: 12px 24px + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) + color: white + border-radius: 8px + font-weight: 600 + transition: all 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + display: flex + align-items: center + justify-content: center + gap: 8px + font-size: 16px + text-decoration: none + border: none + cursor: pointer + &:hover + background: linear-gradient(90deg, #1D0631dd 0%, #5A2A82dd 100%) + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) + color: white + text-decoration: none + i + font-size: 16px + +// User Journeys Section +.user_journeys_section + background: #1D0631 + padding: 64px 0 + @media (max-width: 768px) + padding: 48px 0 + +.journeys_header + text-align: center + margin-bottom: 48px + +.journeys_title + font-size: 30px + font-weight: 700 + color: white + margin-bottom: 12px + @media (max-width: 768px) + font-size: 24px + +.journeys_subtitle + font-size: 18px + color: #d1d5db + max-width: 768px + margin: 0 auto + @media (max-width: 768px) + font-size: 16px + +// Mobile View - Collapsible +.journeys_mobile + display: block + @media (min-width: 1024px) + display: none + +.journey_card + position: relative + padding: 3px + border-radius: 12px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 16px + transition: all 0.3s + +.journey_card_header + width: 100% + display: flex + align-items: center + gap: 16px + padding: 16px + text-align: left + transition: background 0.3s + background: #1D0631 + border: none + cursor: pointer + border-radius: 10px + &:hover + background: rgba(90, 42, 130, 0.2) + +.journey_header_icon + width: 40px + height: 40px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 20px + color: white + +.journey_header_title + flex: 1 + font-size: 14px + font-weight: 600 + color: white + +.journey_toggle_icon + flex-shrink: 0 + i + font-size: 20px + color: white + +.journey_card_content + display: none + padding: 0 16px 16px + background: #1D0631 + border-radius: 0 0 10px 10px + +.journey_card.expanded .journey_card_content + display: block + animation: slideDown 0.3s ease-out + +@keyframes slideDown + from + opacity: 0 + transform: translateY(-10px) + to + opacity: 1 + transform: translateY(0) + +.journey_description + font-size: 14px + color: #d1d5db + line-height: 1.5 + margin-bottom: 16px + +.journey_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +.steps_title + font-size: 12px + font-weight: 600 + color: #e5e7eb + text-transform: uppercase + letter-spacing: 0.05em + margin-bottom: 8px + +.steps_list + list-style: none + padding: 0 + margin: 0 + li + display: flex + align-items: flex-start + gap: 8px + font-size: 14px + color: #d1d5db + margin-bottom: 6px + &:last-child + margin-bottom: 0 + +.step_bullet + color: #ffb91a + font-weight: 700 + +// Desktop View - Grid +.journeys_desktop + display: none + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + +.journey_grid_card + border-radius: 12px + padding: 24px + border: 1px solid rgba(90, 42, 130, 0.4) + background: #1D0631 + transition: all 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) + border-color: #ffb91a + +.grid_card_icon + width: 56px + height: 56px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin-bottom: 16px + transition: transform 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 28px + color: white + +.journey_grid_card:hover .grid_card_icon + transform: scale(1.1) + +.grid_card_title + font-size: 20px + font-weight: 700 + color: white + margin-bottom: 8px + +.grid_card_description + font-size: 14px + color: #d1d5db + margin-bottom: 16px + line-height: 1.5 + +.grid_card_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index bda95eea6..8efad3364 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -55,13 +55,16 @@ @mixin top-navbar-colors color: white - background-color: #211e1e !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important @mixin top-navbar-menu-colors - color: white + color: white !important + font-weight: 500 + transition: color 0.3s ease &:hover padding-bottom: 0px - color: color-gradient($SECONDARY_YELLOW, 60) + color: rgba(255, 255, 255, 0.7) !important + text-decoration: none @mixin twitter-background-color background-color: #5A2A82 @@ -119,15 +122,15 @@ border-left: 3px solid color-gradient($PRIMARY_DARK_GRAY, 100) @mixin footer-colors - color: color-gradient($PRIMARY_PURPLE, 120) !important - background-color: color-gradient($FOOTER_BACKGROUND, 0) !important + color: white !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important a &:hover - color: color-gradient($FOOTER_BACKGROUND, 0) + color: white !important border-radius: 0 @mixin footer-copyright-colors - color: color-gradient($LIGHT_GRAY, 100) !important + color: rgba(156, 163, 175, 1) !important //////////////////////////////////////////////////////////////////// // diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cc813f4f1..dd1cf9d9d 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -1,21 +1,41 @@ @import "base" +// Mobile menu slide down animation +@keyframes slideDown + 0% + opacity: 0 + transform: translateY(-10px) + 100% + opacity: 1 + transform: translateY(0) + body - background-color: #211e1e !important - font-family: Roboto + background-color: white !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" font-size: 1.3rem !important margin: 0 + padding: 0 color: #000 + width: 100% + overflow-x: hidden -#page - width: 980px +.container#page, #page + width: 100% !important + max-width: 100% !important background-color: white - margin: 0 auto - padding: 10px 10px 0 10px + margin: 0 !important + padding: 0 !important header - background-color: white - margin: -10px -10px 0px -10px + // Figma design: gradient background for entire header + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-size: 16px + margin: 0 + padding: 0 + width: 100% + position: relative + z-index: 1 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -27,60 +47,94 @@ header overflow-x: hidden !important @media (min-width: 540px) and (max-width: 1024px) - #page - margin: 0 auto - padding: 10px 10px 0 10px - width: auto + #page, .container#page + margin: 0 !important + padding: 0 !important + width: 100% !important + max-width: 100% !important #nav-top-bar - margin: 11px 11px 0 11px + width: 100% + max-width: 1280px + margin: 0 auto + padding: 0 32px + display: flex + align-items: center .new_main_menu - list-style: none - margin: 0 - padding: 0 + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + gap: 18px !important li - display: inline-block - margin-right: 32px + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important a - @include top-navbar-menu-colors - font-size: 11pt - text-decoration: none + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: color 0.3s ease !important + white-space: nowrap !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + padding: 6px 0 !important + margin: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important #navbar-inner - @include top-navbar-colors - padding-top: 1px - margin-left: 4px - margin-right: 5px - height: 42px + // Navigation moved to main navbar - hide this section + display: none -@media (min-width: 320px) and (max-width: 340px) +@media (min-width: 320px) and (max-width: 480px) #nav-top-bar - margin: 11px 4px 0 0px !important + padding: 0 16px .new_main_menu + gap: 16px + .mobile-menu + .mobile-menu-content + padding: 0 8px + .mobile-menu-items li - margin-right: 2px !important a - font-size: 10px !important - -@media (min-width: 341px) and (max-width: 480px) + padding: 10px 12px + font-size: 13px + +@media (min-width: 481px) and (max-width: 768px) #nav-top-bar - margin: 11px 10px 0 11px !important - .new_main_menu - li - margin-right: 6px !important - a - font-size: 10px !important -#nav-top-bar .new_main_menu li a:hover - color: #fff - text-decoration: underline + padding: 0 24px + .new_main_menu + gap: 24px #page_contents + max-width: 100% + padding: 0 20px .separator-div @include site-separator-color #project_container background-color: white margin: 0 auto - padding: 10px 10px 0 10px + padding: 10px 20px 0 20px + width: 100% + max-width: 100% #page-contents width: auto #project_masthead @@ -99,132 +153,662 @@ header .quick_reference_heading, .nutshell_heading padding-left: 2rem -@media (min-width: 320px) and (max-width: 480px) +// sm: 640px+ +@media (min-width: 640px) .navbar - margin-right: 0px !important - .btn - font-size: 10px !important - .company-div - float: none !important - .navbar_large_text - font-size: 18px !important + .navbar-container + padding: 0 16px + gap: 12px + .logo-and-brand + gap: 6px + .logo-div + .logo_img + height: 28px + .company-div + .navbar_large_text + font-size: 16px + .separator-div + display: block + height: 24px + .actions-div + gap: 8px + .theme-toggle-btn + padding: 8px + .theme-icon + font-size: 20px + .mobile-menu-toggle + padding: 8px + i + font-size: 20px + .mobile-menu + .mobile-menu-content + padding: 0 12px + .mobile-menu-items + li + a + padding: 12px 16px + +// md: 768px+ +@media (min-width: 768px) + .navbar + .navbar-container + padding: 0 24px + gap: 16px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 32px + .separator-div + display: block + height: 26px + .logo-and-brand + gap: 10px + .mobile-menu + .mobile-menu-content + padding: 0 24px + .mobile-menu-items + li + a + padding: 14px 20px + font-size: 15px + +// lg: 1024px+ (Desktop) +@media (min-width: 1024px) + .navbar + min-height: 72px + .navbar-container + padding: 0 32px + min-height: 72px + .logo-and-brand + gap: 12px + .logo-div + .logo_img + height: 40px + .company-div + .navbar_large_text + font-size: 20px + .separator-div + height: 28px + .actions-div + gap: 16px + .nav-menu + display: flex !important + .new_main_menu + gap: 18px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 14px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + .desktop-actions + display: flex !important + .mobile-menu-toggle + display: none !important + .mobile-menu + display: none + +// xl: 1280px+ +@media (min-width: 1280px) + .navbar + min-height: 80px + .navbar-container + min-height: 80px + .logo-div + .logo_img + height: 48px + .company-div + .navbar_large_text + font-size: 24px + .nav-menu + .new_main_menu + gap: 28px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// 2xl: 1536px+ +@media (min-width: 1536px) + .navbar + min-height: 88px + .navbar-container + min-height: 88px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 30px + .nav-menu + .new_main_menu + gap: 36px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// Mobile/Tablet: < 1024px (show hamburger menu) +@media (max-width: 1023px) + .navbar + .nav-menu + display: none !important + .desktop-actions + display: none !important + .mobile-menu-toggle + display: flex !important .navbar + // Figma design: gradient background with purple theme + position: relative + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) border-width: 0 - margin-top: 15px - margin-right: 15px - margin-bottom: 5px !important - margin-left: 15px - padding: 0 0 5px 0 - min-height: 30px + border-bottom: 1px solid rgba(31, 41, 55, 0.8) + margin: 0 + padding: 0 + min-height: 64px + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2) + width: 100% + display: flex + align-items: flex-start + flex-wrap: wrap + overflow: visible + z-index: 100 + // Override global button styles for navbar + .btn + border: none !important + border-width: 0 !important + box-shadow: none !important + &:active + top: 0 !important + left: 0 !important + .navbar-container + max-width: 100% + margin: 0 auto + padding: 0 16px + width: 100% + min-height: 64px + display: flex !important + flex-direction: row !important + flex-wrap: nowrap !important + align-items: center + justify-content: space-between + gap: 12px + position: relative + .hidden + display: none !important + position: absolute !important + width: 0 !important + height: 0 !important + overflow: hidden !important + .logo-and-brand + display: flex + align-items: center + gap: 8px + flex-shrink: 0 + min-width: 0 .logo-div - height: 35px - float: left + display: flex + align-items: center + flex-shrink: 0 .logo_link text-decoration: none - margin-top: 3px - .logo_img - height: 100% + display: flex + align-items: center .company-div - @include top-navigation-colors - float: left + color: white !important + display: flex + align-items: center + min-width: 0 .navbar_small_text font-size: 10px !important .navbar_large_text - font-size: 24px + color: white !important + font-weight: 700 + line-height: 1.25 + letter-spacing: 0 + white-space: nowrap a - @include top-navigation-colors + color: white !important text-decoration: none - .spacing-div - float: left - width: 2px - height: 30px - background-color: white - margin: 0 auto + &:hover + color: #d1d5db !important + transition: color 0.3s ease .separator-div - @include site-separator-color - @include site-separator-background-color - float: left + background-color: rgba(255, 255, 255, 0.2) width: 1px - height: 30px - margin: 0 2px 0 2px + height: 20px + margin: 0 + display: none + .nav-menu + display: none + position: absolute + left: 50% + transform: translateX(-50%) + .new_main_menu + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + li + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important + a + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: all 0.3s ease !important + white-space: nowrap !important + position: relative !important + padding: 6px 0 !important + margin: 0 !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + &::after + content: '' !important + position: absolute !important + bottom: 0 !important + left: 0 !important + right: 0 !important + height: 2px !important + background-color: #FFB91A !important + border-radius: 2px !important .actions-div - float: right - .twitter-text - @include twitter-background-color - color: white - font-weight: bold - font-size: 0.80em + display: flex + align-items: center + gap: 12px + flex-shrink: 0 + flex-grow: 0 + .theme-toggle-btn + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + .theme-icon + font-size: 18px + color: white + transition: all 0.3s ease + .hidden + display: none + .mobile-menu-toggle + display: none + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + i + font-size: 18px + color: white .btn border: 0 - display: inline-block - font-size: 15px - line-height: 25px + display: inline-flex + align-items: center + justify-content: center + font-size: 14px + line-height: 1.5 margin: 0 - padding: 1px 6px 0 7px - .follow_btn - @include follow-button-colors - display: inline-block - padding: 0 0 0 4px - height: 24px - i, p, img - color: white - display: inline-block - vertical-align: top - i - color: #555 - vertical-align: middle - &:before - padding-right: 0 - p - font-weight: normal - line-height: 24px - text-transform: none - margin: 0 - border-radius: 0 4px 4px 0 - img - margin-right: -1px + padding: 8px 16px + border-radius: 8px + font-weight: 600 + transition: all 0.3s ease + cursor: pointer + text-decoration: none + white-space: nowrap ul list-style: none - margin: 3px 0 0 0 + margin: 0 + padding: 0 + display: flex + align-items: center + gap: 12px li - float: left - padding: 0 0 0 8px + padding: 0 + list-style: none #top_nav_actions - float: right - margin-right: 5px + display: flex + align-items: center + gap: 12px + margin: 0 + padding: 0 li - padding-top: 5px + padding: 0 + list-style: none + .btn-primary, .btn-success + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-width: 0 !important + font-weight: 600 !important + padding: 10px 20px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: transparent !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) !important + i + font-size: 14px + margin: 0 + .btn-outline + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + font-weight: 600 !important + padding: 8px 18px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + border-color: #FFB91A !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important @media (min-width: 320px) and (max-width: 340px) #top_nav_actions margin-right: 0px !important -#logged_user_menu - a - color: black !important +// Mobile Menu +.mobile-menu + display: none + width: 100% + flex-basis: 100% + background: linear-gradient(to right, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + border-top: 1px solid rgba(255, 255, 255, 0.15) + padding: 20px 0 + position: relative + z-index: 100 + overflow: visible + order: 2 + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) + &.show + display: block !important + visibility: visible !important + opacity: 1 !important + animation: slideDown 0.3s ease-out + .mobile-menu-content + max-width: 1280px + margin: 0 auto + padding: 0 16px + width: 100% + display: block + visibility: visible + .mobile-menu-items + list-style: none + margin: 0 + padding: 0 + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-weight: 500 + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + &.select + color: #FFB91A !important + font-weight: 600 + background-color: rgba(255, 185, 26, 0.15) + .mobile-menu-user + list-style: none + margin: 16px 0 0 0 + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.15) + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + .mobile-menu-signin + margin-top: 16px + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.1) + display: block + visibility: visible + .btn-mobile-signin + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + margin-bottom: 12px + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) + &:active + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) + .btn-mobile-join-now + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) + &:active + transform: translateY(0) + box-shadow: none + +.dropdown#logged_user_menu + position: relative + z-index: 1000 + .dropdown-toggle + color: white !important text-transform: none - text-decoration: none - margin: 0 0px 15px - padding-right: 10px - padding-top: 3px - ul - margin-top: 40px - border-radius: 0 + text-decoration: none !important + margin: 0 + padding: 10px 16px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + display: inline-flex !important + align-items: center + gap: 8px + font-size: 14px + font-weight: 500 + transition: all 0.3s ease + border: 1px solid transparent + cursor: pointer !important + position: relative + z-index: 1001 + pointer-events: auto !important + &:hover + background-color: rgba(255, 255, 255, 0.15) + border-color: rgba(255, 255, 255, 0.2) + text-decoration: none !important + transform: translateY(-1px) + &:active + transform: translateY(0) + i + font-size: 14px + pointer-events: none + ul.dropdown-menu + display: none + position: absolute + right: 0 + top: calc(100% + 24px) + margin-top: 8px + border-radius: 12px + border: 1px solid rgba(255, 255, 255, 0.15) + background-color: rgba(29, 6, 49, 0.97) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 4px 10px rgba(0, 0, 0, 0.2) + z-index: 1050 + min-width: 200px + padding: 8px 0 li - width: 150px + width: 100% a - color: black + color: white !important margin: 0 + padding: 12px 16px + transition: all 0.3s ease + border-radius: 8px + display: block + cursor: pointer &:hover - background-color: #0082C6 !important - color: #fff !important + background-color: rgba(255, 185, 26, 0.2) !important + color: #FFB91A !important + transform: translateX(4px) .divider width: 100% height: 1px - margin: 13px 0 + margin: 8px 0 overflow: hidden - border-bottom: 1px solid #e5e5e5 + border-bottom: 1px solid rgba(255, 255, 255, 0.15) + &.open + ul.dropdown-menu + display: block + +// Override Bootstrap's default dropdown styling +.navbar .dropdown.open .dropdown-menu + display: block !important + +#top_nav_actions + position: relative + z-index: 1000 #flash-msg .alert @@ -533,3 +1117,47 @@ fieldset width: 14rem .language_percentage_indicator width: 3rem + +// Full-width layout styles +.page-content-wrapper + width: 100% + max-width: 100% + margin: 0 auto + padding: 20px + +// Ensure home page content uses full width +.home-content, .explore-content, .billboard + width: 100% + max-width: 100% + margin: 0 auto + padding: 0 20px + +// Override Bootstrap navigation styles +.navbar .nav-menu .new_main_menu li.menu_item a, +.navbar ul.new_main_menu li.menu_item a, +ul.new_main_menu li.menu_item a + color: white !important + font-size: 14px !important + font-weight: 500 !important + text-decoration: none !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + padding: 6px 0 !important + margin: 0 !important + background: none !important + border: none !important + box-shadow: none !important + letter-spacing: 0 !important + &:hover, &:focus, &:active, &:visited + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + +@media (min-width: 1280px) + .navbar .nav-menu .new_main_menu li.menu_item a, + .navbar ul.new_main_menu li.menu_item a, + ul.new_main_menu li.menu_item a + font-size: 16px !important diff --git a/app/views/home/_join_now_home.html.haml b/app/views/home/_join_now_home.html.haml index 2fe5d5a3c..b2a24f233 100644 --- a/app/views/home/_join_now_home.html.haml +++ b/app/views/home/_join_now_home.html.haml @@ -1,16 +1,28 @@ -.join_now_home - %ul - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.claim') - %span.join_now_color= t('.contributions') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.manage') - %span.join_now_color= t('.projects_data') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.highlight') - %span.join_now_color= t('.use') - %br - %p - = link_to t('.join_now'), h(new_account_path(invite: params[:invite], ref: 'homepage')), class: 'btn btn_join_now' +.join_now_card + .card_inner + .join_header + .join_icon + %i.fa.fa-user-plus + .join_header_text + %h3.join_title= t('.join_now') + %p.join_subtitle= t('.become_part_community') + + .benefits_list + .benefit_item + .benefit_icon.benefit_icon_award + %i.fa.fa-trophy + .benefit_content + %h4.benefit_title= t('.track_contributions_title') + %p.benefit_description= t('.track_contributions_desc') + + .benefit_item + .benefit_icon.benefit_icon_trending + %i.fa.fa-line-chart + .benefit_content + %h4.benefit_title= t('.gain_recognition_title') + %p.benefit_description= t('.gain_recognition_desc') + + .join_cta + = link_to new_account_path(invite: params[:invite], ref: 'homepage'), class: 'btn btn_join_cta' do + %i.fa.fa-user-plus + = t('.create_account') diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml new file mode 100644 index 000000000..4f4cb2895 --- /dev/null +++ b/app/views/home/_top_contributors.html.haml @@ -0,0 +1,33 @@ +.top_contributors_card + .card_header + .header_icon + %i.fa.fa-users + .header_content + %h3.card_title= t('.top_contributors') + %p.card_subtitle= t('.most_active_developers') + + .contributors_list + - @top_contributors&.each_with_index do |contributor, index| + .contributor_item{class: "contributor-rank-#{index + 1}"} + .contributor_avatar + = link_to account_path(contributor) do + = gravatar_tag(contributor.email, size: 40, class: 'avatar_image', alt: contributor.name) + .contributor_info + .contributor_name_rank + %h4.contributor_name + = link_to contributor.name, account_path(contributor), class: 'contributor_link' + %span.contributor_rank= "##{index + 1}" + .contributor_stats + %span.stat_item + %i.fa.fa-code-fork + = number_with_delimiter(contributor.commits_count || 0) + %span.stat_item + %i.fa.fa-folder + = "#{contributor.projects_count || 0} projects" + .contributor_arrow + %i.fa.fa-chevron-right + + .view_all_link + = link_to accounts_path, class: 'btn btn_view_all' do + %i.fa.fa-users + = t('.view_all_contributors') diff --git a/app/views/home/_user_journeys.html.haml b/app/views/home/_user_journeys.html.haml new file mode 100644 index 000000000..a5d6cc56e --- /dev/null +++ b/app/views/home/_user_journeys.html.haml @@ -0,0 +1,272 @@ +.user_journeys_section + .container + .journeys_header + %h2.journeys_title= t('.your_journey_with_openhub') + %p.journeys_subtitle= t('.explore_ways_description') + + / Mobile View - Collapsible Cards + .journeys_mobile + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '0'} + .journey_header_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.journey_header_title= t('.discover_compare_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.discover_compare_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '1'} + .journey_header_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.journey_header_title= t('.assess_health_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.assess_health_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '2'} + .journey_header_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.journey_header_title= t('.manage_portfolio_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.manage_portfolio_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '3'} + .journey_header_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.journey_header_title= t('.analyze_code_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.analyze_code_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '4'} + .journey_header_icon.journey_icon_security + %i.fa.fa-shield + %h3.journey_header_title= t('.check_security_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.check_security_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '5'} + .journey_header_icon.journey_icon_contribute + %i.fa.fa-search + %h3.journey_header_title= t('.identify_opportunities_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.identify_opportunities_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + + / Desktop View - Grid Cards + .journeys_desktop + .journey_grid_card + .grid_card_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.grid_card_title= t('.discover_compare_title') + %p.grid_card_description= t('.discover_compare_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.grid_card_title= t('.assess_health_title') + %p.grid_card_description= t('.assess_health_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.grid_card_title= t('.manage_portfolio_title') + %p.grid_card_description= t('.manage_portfolio_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.grid_card_title= t('.analyze_code_title') + %p.grid_card_description= t('.analyze_code_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_security + %i.fa.fa-shield + %h3.grid_card_title= t('.check_security_title') + %p.grid_card_description= t('.check_security_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_contribute + %i.fa.fa-search + %h3.grid_card_title= t('.identify_opportunities_title') + %p.grid_card_description= t('.identify_opportunities_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + +:javascript + document.addEventListener('DOMContentLoaded', function() { + const toggleButtons = document.querySelectorAll('.journey_toggle'); + + toggleButtons.forEach(function(button) { + button.addEventListener('click', function() { + const journeyCard = this.closest('.journey_card'); + const content = journeyCard.querySelector('.journey_card_content'); + const icon = this.querySelector('.journey_toggle_icon i'); + + journeyCard.classList.toggle('expanded'); + + if (journeyCard.classList.contains('expanded')) { + icon.classList.remove('fa-plus'); + icon.classList.add('fa-minus'); + } else { + icon.classList.remove('fa-minus'); + icon.classList.add('fa-plus'); + } + }); + }); + }); diff --git a/app/views/home/_whats_new.html.haml b/app/views/home/_whats_new.html.haml index 130643baf..c045b66e9 100644 --- a/app/views/home/_whats_new.html.haml +++ b/app/views/home/_whats_new.html.haml @@ -1,2 +1,9 @@ -%a{ href: 'https://www.blackduck.com/resources/analyst-reports/open-source-security-risk-analysis.html', target: '_blank' } - %img{src: image_path('home/OSSRA-OH-banner.png'), style: 'margin-left: 25px; margin-top: 15px'} +.whats_new_card + .card_inner + .card_header_section + %h3.whats_new_title= t('.whats_new') + %p.whats_new_subtitle= t('.latest_updates_reports') + + .featured_image_section + %a{ href: 'https://www.blackduck.com/resources/analyst-reports/ossra-report.html', target: '_blank', class: 'featured_image_link' } + %img.featured_image{src: image_path('home/OSSRA-OH-banner.png'), alt: t('.ossra_report_alt')} diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index c47b3002b..473afba49 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,31 +1,81 @@ - content_for(:html_title) { t('.page_title') } -.showcase - .billboard - .row - .col-md-12 - %h2.discover_msg - = t('.discover_message') - .row - .col-md-12.billboard_search - %form.search#search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'homepage' } - %input.for_search_all_code{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text' } - %i#icon_text.fa.fa-search +- content_for(:javascript) { javascript_include_tag 'rotating_stats' } + +/ Hero Section +.hero_section + .hero_container + / Hero Header - Centered + .hero_header + %h1.hero_title= t('.discover_message') + %p.hero_subtitle= t('.worlds_largest_directory') + + / Search Bar - Centered & Compact + .search_container + %form.search_form#search_form{ action: projects_path } + %input{ type: :hidden, name: 'ref', value: 'homepage' } + .search_input_wrapper + %i.fa.fa-search.search_icon + %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } + + / Rotating Stats + .rotating_stats + #global_statistics + %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) + %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) + %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) + %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) + / Feature Cards - Moved to Top + .features_section + .feature_card + .feature_icon.feature_icon_yellow + %i.fa.fa-database + %h3.feature_title= t('.feature_directory_title') + %p.feature_description= t('.feature_directory_desc') + .feature_card + .feature_icon.feature_icon_blue + %i.fa.fa-heartbeat + %h3.feature_title= t('.feature_health_title') + %p.feature_description= t('.feature_health_desc') + .feature_card + .feature_icon.feature_icon_green + %i.fa.fa-user-circle + %h3.feature_title= t('.feature_portfolio_title') + %p.feature_description= t('.feature_portfolio_desc') + + / Quick Stats Bar - Ultra Compact & Elegant + .quick_stats_bar + .stat_item + .stat_value= "2.5M+" + .stat_label= t('.projects_label') + .stat_divider + .stat_item + .stat_value= number_with_delimiter(@home.person_count / 1000) + "K" + .stat_label= t('.contributors_label') + .stat_divider + .stat_item + .stat_value= "450+" + .stat_label= t('.languages_label') + .stat_divider + .stat_item + .stat_value= "12K" + .stat_label= t('.organizations_label') +/ Top Contributors and What's New Section +.content_section + .container .row - .col-md-12 - .global_stats - #global_statistics - %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) - %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) - %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) - %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) -.home_page_row - .col-md-4.col-xs-7.col-sm-6 - %h2.join_now= t('.join_now') - = render partial: '/home/join_now_home' - .col-md-8.col-xs-5.col-sm-6 - %h2.wh_new= t('.whats_new') - = render partial: '/home/whats_new' + / Top Contributors - 35% + .col-md-4.col-sm-12.contributors_column + = render partial: '/home/top_contributors' + + / What's New and Join Now - 65% + .col-md-8.col-sm-12.whats_new_column + .whats_new_join_stack + = render partial: '/home/whats_new' + = render partial: '/home/join_now_home' + +/ User Journeys Section += render partial: '/home/user_journeys' + .landing.col-md-12.col-sm-12.col-xs-12.last_section = home_top_lists diff --git a/app/views/layouts/partials/_footer.html.haml b/app/views/layouts/partials/_footer.html.haml index 53b3e40d7..d16f5375d 100644 --- a/app/views/layouts/partials/_footer.html.haml +++ b/app/views/layouts/partials/_footer.html.haml @@ -1,40 +1,51 @@ - raw_email = '%69%6Efo%40O%70en%48u%62.%6E%65t' -.footer-left - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } +.footer-container + .footer-grid + .footer-left + %a.logo_link{ href: 'https://www.blackduck.com' } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck' } -.footer-mid - = "ABOUT BLACK DUCK" - %p - %p - %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' - %p - %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' - %p - %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' - %p - %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' + .footer-mid + %h3 ABOUT BLACK DUCK + %ul + %li + %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' + %li + %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' + %li + %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' + %li + %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' -.footer-right - = "ABOUT OPEN HUB" - %p - %p - %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog - %p - %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') - %p - %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us + .footer-right + %h3 ABOUT OPEN HUB + %ul + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog + %li + %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') + %li + %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us - -.footer-bottom - %sup © - = Time.current.year - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span{ itemprop: 'publisher' } - %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." - = t('copyright.rights_text') + .footer-bottom + .footer-bottom-content + .copyright + %sup © + = Time.current.year + %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span{ itemprop: 'publisher' } + %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." + = t('copyright.rights_text') + .follow-us + %span Follow us: + :ruby + twitter_url = 'https://x.com/intent/follow?original_referer=' + twitter_url += CGI.escape request.url + twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' + %a{ href: twitter_url, target: '_blank', 'aria-label' => 'Follow us on Twitter' } + %i.icon-twitter diff --git a/app/views/layouts/partials/_mast.html.haml b/app/views/layouts/partials/_mast.html.haml index f28f75c58..e67c7f71c 100644 --- a/app/views/layouts/partials/_mast.html.haml +++ b/app/views/layouts/partials/_mast.html.haml @@ -1,52 +1,103 @@ +:ruby + explore_projects_class = params[:action] == 'explore/projects' ? 'select' : '' + explore_orgs_class = params[:action] == 'explore/orgs' ? 'select' : '' + tools_select = params[:action] == 'tools' ? 'select' : '' + .navbar - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span.hidden{ itemprop: 'author' } openhub.net - %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. - %span + .navbar-container + %span.hidden{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span.hidden{ itemprop: 'author' } openhub.net + %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. + .logo-and-brand %div.logo-div - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } - %div.spacing-div + %a.logo_link{ href: root_path } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck Open Hub' } %div.separator-div - %div.spacing-div %div.company-div %a{ href: root_path } %span.navbar_large_text Open Hub - %div.actions-div - %ul#top_nav_actions - %li.twitter_follow - :ruby - twitter_url = 'https://twitter.com/intent/follow?original_referer=' - twitter_url += CGI.escape request.url - twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' - %a.btn.follow_btn{ href: twitter_url, target: '_blank' } - %i.icon-twitter - %p.follow! #{t(:follow)} @ - %p.twitter-text  OH  - - if logged_in? - %li - .dropdown#logged_user_menu - %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } - %i.icon-user - = h(current_user.name) - %i.icon-caret-down - %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer - %li - = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %nav.nav-menu + %ul.new_main_menu{ class: page_context[:select_top_menu_nav] } + %li.menu_item.projects + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li.menu_item.people + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li.menu_item.organizations + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li.menu_item.tools + = link_to t(:tools_menu), tools_path, class: tools_select + %li.menu_item.blog + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li.menu_item.bdsa + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + %div.actions-div + %button#theme-toggle.theme-toggle-btn{ 'aria-label' => 'Toggle theme' } + %i#theme-icon-moon.icon-moon.theme-icon + %i#theme-icon-sun.icon-sun.theme-icon.hidden + %ul#top_nav_actions.desktop-actions + - if logged_in? + %li + .dropdown#logged_user_menu + %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } + %i.icon-user + = h(current_user.name) + %i.icon-caret-down + %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) - - if current_user_is_admin? - %li - = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) - %li.divider - %li - = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) - - else - %li - %a.btn.btn-mini.btn-primary.btn-header{ href: new_session_path }= t :sign_in + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li.divider + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + %li + %a.btn.btn-primary.btn-header{ href: new_session_path } + %i.icon-user + = t :sign_in + %li + %a.btn.btn-outline.btn-join-now{ href: new_account_path } + Join Now + %button#mobile-menu-toggle.mobile-menu-toggle{ 'aria-label' => 'Menu' } + %i#mobile-menu-icon.icon-reorder + #mobile-menu.mobile-menu + .mobile-menu-content + %ul.mobile-menu-items + %li + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li + = link_to t(:tools_menu), tools_path, class: tools_select + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + = render 'shared/search.html.haml' + - if logged_in? + %ul.mobile-menu-user + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - %a.btn.btn-mini.btn-success.btn-header{ href: new_account_path }= t :join_now + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + .mobile-menu-signin + %a.btn.btn-primary.btn-mobile-signin{ href: new_session_path } + %i.icon-user + = t :sign_in + %a.btn.btn-outline.btn-mobile-join-now{ href: new_account_path } + Join Now - if read_only_mode? %div diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e3160c2b9..3fe5a83df 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,5 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js] + api/vulnerability.sass api/vulnerability.js rotating_stats.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 9e5497ce0..075a8dbc6 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -3,6 +3,7 @@ en: index: page_title: 'Open Hub, the open source network' discover_message: 'Discover, Track and Compare Open Source' + worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' whats_new: "What's New" counting: 'Counting %{count} lines of code' @@ -10,6 +11,16 @@ en: connecting: 'Connecting %{count} open source contributors' tracking: 'Tracking %{count} source control repositories' search_projects: 'Search Projects...' + feature_directory_title: 'Massive Directory' + feature_directory_desc: 'Indexing over 2.5 million open source projects.' + feature_health_title: 'Assess Health' + feature_health_desc: 'Check activity levels and risk before you adopt.' + feature_portfolio_title: 'Manage Portfolio' + feature_portfolio_desc: 'Claim your commits and track your developer rankings.' + projects_label: 'Projects' + contributors_label: 'Contributors' + languages_label: 'Languages' + organizations_label: 'Organizations' top_lists: popular_projects: 'Most Popular Projects' active_projects: 'Most Active Projects' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb index 04d2523cc..3e27b7716 100644 --- a/test/controllers/home_controller_test.rb +++ b/test/controllers/home_controller_test.rb @@ -9,7 +9,13 @@ class HomeControllerTest < ActionController::TestCase best_account_analysis.account.update(best_vita_id: best_account_analysis.id, created_at: 4.days.ago) account_analysis_fact = best_account_analysis.account_analysis_fact account_analysis_fact.update(last_checkin: Time.current) - Rails.cache.stubs(:fetch).returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-recently_active_accounts-cache').returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-person_count-cache').returns(1_000_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-active_project_count-cache').returns(2_500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-repository_count-cache').returns(500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-vita_count-cache').returns([]) + Rails.cache.stubs(:fetch).with('HomeDecorator-most_active_projects-cache').returns([]) + Rails.cache.stubs(:fetch).with('homepage_top_lists').returns([]) get :index assert_response :success diff --git a/test/controllers/licenses_controller_test.rb b/test/controllers/licenses_controller_test.rb index b2ac3ce27..e04bba48b 100644 --- a/test/controllers/licenses_controller_test.rb +++ b/test/controllers/licenses_controller_test.rb @@ -52,7 +52,7 @@ class LicensesControllerTest < ActionController::TestCase get :show, params: { id: @license.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e59b18bb3..fc1e35bab 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -83,7 +83,7 @@ class OrganizationsControllerTest < ActionController::TestCase get :show, params: { id: @organization.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'should support show page via xml api' do diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index def7e2c23..0c4a958ea 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -292,7 +292,7 @@ class ProjectsControllerTest < ActionController::TestCase get :show, params: { id: project.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'show accepts being called via api' do From 90aede364fb38cb0be239775e559b594f1291dd2 Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Tue, 10 Mar 2026 18:06:08 +0530 Subject: [PATCH 09/53] OTWO-7596 Implementation of project search index page "OTWO-7596 Implementation of project search index page" --- app/assets/javascripts/search_dingus.js | 75 +- .../javascripts/session_projects.js.coffee | 145 ++-- app/assets/stylesheets/base.sass | 3 + app/assets/stylesheets/buttons.sass | 771 +++++++++++++----- app/assets/stylesheets/oh-styles.sass | 45 + app/assets/stylesheets/projects.sass | 645 +++++++++++++-- app/assets/stylesheets/search-dingus.sass | 649 +++++++++++---- app/assets/stylesheets/session_projects.sass | 288 ++++++- .../analysis/commit_history_chart.rb | 4 +- app/views/projects/_project_index.html.haml | 207 +++-- app/views/projects/index.html.haml | 43 +- app/views/session_projects/_menu.html.haml | 61 +- app/views/shared/_search.html.haml | 31 +- app/views/shared/_search_dingus.html.haml | 6 +- .../_page_entries_info.html.haml | 11 +- .../search_dingus/_search_bar.html.haml | 18 +- .../shared/search_dingus/_sort.html.haml | 59 +- 17 files changed, 2356 insertions(+), 705 deletions(-) diff --git a/app/assets/javascripts/search_dingus.js b/app/assets/javascripts/search_dingus.js index f98d1251a..86c7a9120 100644 --- a/app/assets/javascripts/search_dingus.js +++ b/app/assets/javascripts/search_dingus.js @@ -20,58 +20,103 @@ var ohloh = (function builder($) { init: function(){ var $scope = $('.ux-dropdown'); var $search_text_field = $scope.find('.text'); - var dropdownHead = $('a span.selection', $scope); + var dropdownHead = $('button span.selection, a span.selection', $scope); var getSelectedValue = function() { return dropdownHead.first().attr('val'); }; - $('.dropdown-menu li a', $scope).click(function() { + $('.dropdown-menu .dropdown-item', $scope).click(function() { var selectedText = $(this).attr('val') dropdownHead.attr('val',selectedText) dropdownHead.html( $(this).html() ) dropdownHead.parents('form').attr('action',selectedText) + // Update active class + $('.dropdown-menu .dropdown-item', $scope).removeClass('active') + $(this).addClass('active') if(selectedText != "//code.ohloh.net/search"){ dropdownHead.parents('form').removeAttr('target'); } }); - $('ul.dropdown-menu li a', $scope).on('click', 'a', function() { + $('.dropdown-menu .dropdown-item', $scope).on('click', function() { var $this = $(this); var section_name = $this.text(); - $('ul.dropdown-menu li a', $scope).removeClass('selected'); + $('.dropdown-menu .dropdown-item', $scope).removeClass('selected'); $this.addClass('selected'); - $('.ux-dropdown a span.selection', $scope).html(section_name); + $('.ux-dropdown button span.selection', $scope).html(section_name); }); $(document).ajaxStop($.unblockUI); if( (/\/p(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="p"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="p"]', $scope).addClass('default'); } if( (/\/orgs(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="orgs"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="orgs"]', $scope).addClass('default'); } if( (/\/(posts|forums)(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="posts"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="posts"]', $scope).addClass('default'); } if( (/\/(people|accounts|committers)(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="people"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="people"]', $scope).addClass('default'); } - $('a.default', $scope).trigger('click'); + $('.dropdown-item.default', $scope).trigger('click'); - $('.dropdown-menu li a', $scope).on('click', 'a', function(){ + $('.dropdown-menu .dropdown-item', $scope).on('click', function(){ $('input[name="query"].search').trigger('click').focus(); }); + // Handle custom sort dropdown + $('.custom-sort-dropdown .sort-dropdown-item').on('click', function(e) { + e.preventDefault(); + var $item = $(this); + var $dropdown = $item.closest('.custom-sort-dropdown'); + var selectedValue = $item.data('value'); + var selectedLabel = $item.text(); + + // Update button text + $dropdown.find('.selection').text(selectedLabel).data('value', selectedValue); + + // Update hidden input + $dropdown.find('.sort-value-input').val(selectedValue); + + // Update active class + $dropdown.find('.sort-dropdown-item').removeClass('active'); + $item.addClass('active'); + + // Submit form + $dropdown.closest('form').submit(); + }); + + // Handle search input with cancel button + $('#query').on('input', function() { + var $input = $(this); + var $cancelBtn = $input.siblings('.search-cancel-btn'); + + if ($input.val().length > 0) { + $cancelBtn.show(); + } else { + $cancelBtn.hide(); + } + }); + + // Handle search cancel button + $('.search-cancel-btn').on('click', function() { + var $input = $('#query'); + $input.val(''); + $(this).hide(); + $input.focus(); + }); + if( (/\?[query]=.{1,}/ig).test(window.location.href) ) { var q = window.location.href.split('?')[1].split('=')[1]; diff --git a/app/assets/javascripts/session_projects.js.coffee b/app/assets/javascripts/session_projects.js.coffee index a3c968661..0487d47d2 100644 --- a/app/assets/javascripts/session_projects.js.coffee +++ b/app/assets/javascripts/session_projects.js.coffee @@ -9,83 +9,128 @@ SessionProjects = url: "/session_projects?#{ timestamp }" success: SessionProjects.success + # Initialize compare checkboxes + SessionProjects.enable() + enable: -> - # Update all checkboxes on page to match those in the menu. + # Update all compare checkboxes on page to match those in the menu. # This is primarily so that state will be cleaned up after back button. - $('.sp_input').prop('checked', false) - $('.sp_form.styled').removeClass('selected') - $('#sp_menu .sp_input').each -> - $(".sp_input[project_id='#{ $(this).attr('project_id') }']").prop('checked', true) - $("#sp_form_#{ $(this).attr('project_id') }").addClass('selected') + $('.compare-checkbox').removeClass('selected') + $('.compare-checkbox i').removeClass('fa-check-square-o').addClass('fa-square-o') - if $('#sp_menu .sp_input').length < 3 + # Mark selected projects from menu + $('#sp_menu .remove-project').each -> + project_id = $(this).data('project-id') + $(".compare-checkbox[data-project-id='#{ project_id }']").addClass('selected') + $(".compare-checkbox[data-project-id='#{ project_id }'] i").removeClass('fa-square-o').addClass('fa-check-square-o') + + if $('#sp_menu .remove-project').length < 3 # All checkboxes are enabled. - $('.sp_input') - .unbind('change') - .bind('change', SessionProjects.change) - .prop('disabled', false) - .parent().removeClass('disabled') + $('.compare-checkbox') + .off('click') + .on('click', SessionProjects.change) + .removeClass('disabled') else # Project limit reached. Can only uncheck checkboxes. - $('.sp_input') - .filter(':checked') - .unbind('change') - .bind('change', SessionProjects.change) - .prop('disabled', false) - .parent().removeClass 'disabled' + $('.compare-checkbox.selected') + .off('click') + .on('click', SessionProjects.change) + .removeClass('disabled') SessionProjects.disableUnchecked() - # Activate the x icon. - $('#sp_menu .sp_input') - .unbind('click') - .bind('click', SessionProjects.change) + # Activate the remove icons in menu. + $('#sp_menu .remove-project') + .off('click') + .on('click', SessionProjects.remove) - # Animate the appearance/disappearance of menu. - height = '0' - height = '2.6em' if $('#sp_menu .sp_input').length - $('#sp_menu').animate { 'min-height': height }, duration: 300 + # Activate toggle button + $('#compare_toggle_btn') + .off('click') + .on('click', SessionProjects.toggleTray) + + # Show/hide the entire compare bar + if $('#sp_menu .remove-project').length > 0 + $('#sp_menu').fadeIn(200) + $('#projects_index_page').addClass('has-compare-tray') + else + $('#sp_menu').fadeOut(200) + $('#projects_index_page').removeClass('has-compare-tray') disableUnchecked: -> - $('.sp_input') - .not(':checked') - .unbind('change') - .prop('disabled', true) - .parent().addClass('disabled') + $('.compare-checkbox') + .not('.selected') + .off('click') + .addClass('disabled') - busy_span: " " - compare_span: "Compare" + toggleTray: -> + $tray = $('.compare-tray-content') + $icon = $('#compare_toggle_btn .toggle-icon') + + if $tray.hasClass('expanded') + $tray.removeClass('expanded') + $icon.removeClass('rotate') + else + $tray.addClass('expanded') + $icon.addClass('rotate') + false change: -> - checked = $(this).is(':checked') - checked = false if $(this).hasClass('icon-remove-sign') - # force false for remove-icons - project_id = $(this).attr('project_id') - sel = $('.sp_input[project_id="' + project_id + '"]') - sel.siblings('span').replaceWith SessionProjects.busy_span - sel.prop('checked', checked).prop 'disabled', true - SessionProjects.disableUnchecked() - if checked + $checkbox = $(this) + return false if $checkbox.hasClass('disabled') + + is_selected = $checkbox.hasClass('selected') + project_id = $checkbox.data('project-id') + + # Show loading state + $checkbox.addClass('disabled') + + if is_selected + # Uncheck $.ajax type: 'POST' - url: '/session_projects?project_id=' + project_id + url: '/session_projects/' + project_id + data: '_method': 'delete' success: SessionProjects.success + error: -> $checkbox.removeClass('disabled') else + # Check $.ajax type: 'POST' - url: '/session_projects/' + project_id - data: '_method': 'delete' + url: '/session_projects?project_id=' + project_id success: SessionProjects.success + error: (xhr) -> + if xhr.status == 403 + alert(xhr.responseText) + $checkbox.removeClass('disabled') + false + + remove: -> + project_id = $(this).data('project-id') + $.ajax + type: 'POST' + url: '/session_projects/' + project_id + data: '_method': 'delete' + success: SessionProjects.success false success: (data, textStatus, jqXHR) -> + # Remember if the tray was expanded + wasExpanded = $('.compare-tray-content').hasClass('expanded') + + # Replace the HTML $('#sp_menu').html(data) - if _(data).isEmpty() - $('#page').css('margin-top', '0px') - else - $('#page').css('margin-top', '52px') - $('.busy').replaceWith(SessionProjects.compare_span) SessionProjects.enable() + + # Restore expanded state if it was open + if wasExpanded + $('.compare-tray-content').addClass('expanded') + $('#compare_toggle_btn .toggle-icon').addClass('rotate') + false $(document).on 'page:change', -> SessionProjects.init() + +# Also initialize on regular page load +$ -> + SessionProjects.init() diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 43c4cbed2..5d76be46e 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -175,5 +175,8 @@ ul.unstyled, ol.unstyled .pagination @include site-pagination-colors +.modern-pagination + @include modern-pagination-styles + .font-1em font-size: 1em diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 8baec979b..2f9eff02c 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -1,93 +1,229 @@ -/** buttons **/ +/** buttons - Updated to match Black Duck / OpenHub redesign system **/ .btn - font-family: 'Roboto' !important - font-weight: bold !important - display: inline-block - color: #FFF !important + font-family: 'Roboto', sans-serif !important + font-weight: 500 !important + display: inline-flex !important + align-items: center !important + justify-content: center !important + gap: 8px !important + color: #374151 !important background-image: none !important - border: 5px solid - border-radius: 0 - box-shadow: none !important - -webkit-transition: all ease .15s - -moz-transition: all ease .15s - -o-transition: all ease .15s - transition: all ease .15s - cursor: pointer - vertical-align: middle - margin: 0 - position: relative - padding: 0 12px 1px - line-height: 32px - font-size: 14px - -.btn-large - padding: 0 14px 1px - line-height: 38px - border-width: 6px - font-size: 16px - -.btn-small - padding: 0 5.5px - line-height: 24px - border-width: 4px - font-size: 15px - /*margin: 2.5px; + border: 1px solid !important + border-radius: 6px !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + transition: all 0.2s ease !important + cursor: pointer !important + vertical-align: middle !important + margin: 0 !important + position: relative !important + padding: 8px 16px !important + line-height: normal !important + font-size: 0.875rem !important + text-decoration: none !important + white-space: nowrap !important + +.btn-large, .btn-lg + padding: 10px 24px !important + font-size: 1rem !important + +.btn-small, .btn-sm + padding: 6px 12px !important + font-size: 0.8125rem !important .btn-mini - padding: 0 5px - line-height: 22px - border-width: 2px - font-size: 12px + padding: 4px 8px !important + font-size: 0.75rem !important .btn-minier - padding: 0 4px - line-height: 18px - border-width: 1px - font-size: 11px + padding: 2px 6px !important + font-size: 0.6875rem !important -button.btn:active - top: 1px - left: 1px +button.btn:active, a.btn:active + top: 0 !important + left: 0 !important .btn, .btn-default - @include default-button-colors + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important -.btn.active, .btn-default.active - background-color: #9baab3 !important - border-color: #8a9ba6 + &:hover + border-color: #d1d5db !important + background-color: #f9fafb !important -.btn.no-border.active, .btn-default.no-border.active - background-color: #92a3ac !important - border-color: #92a3ac + &.active + background-color: #f3f4f6 !important + border-color: #d1d5db !important -.btn.disabled, .btn-default.disabled, .btn[disabled], .btn-default[disabled] - background-color: #abbac3 !important + &.disabled, &[disabled] + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important -.btn-header - width: 100px + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important -.i_use_this_btn - @include secondary-button-colors - width: 100% + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important -.btn-info - @include info-button-colors - &.no-hover:hover - background-color: #0082C6 !important - &.active, &.no-border.active - background-color: #66B7E8 !important - border-color: #66B7E8 - &.disabled, &[disabled] - background-color: #66CEF6 !important + &.active + background-color: #1D0631 !important + border-color: #ffb91a !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + opacity: 0.6 !important .btn-primary - @include primary-button-colors - &.no-hover:hover - background-color: #005481 !important - &.active, &.no-border.active - background-color: #0082C6 !important - border-color: #005481 + background-color: #5A2A82 !important + border-color: #5A2A82 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) !important + + &:hover + opacity: 0.9 !important + background-color: #5A2A82 !important + border-color: #5A2A82 !important + color: white !important + + &:active, &:focus + background-color: #4A1A72 !important + border-color: #4A1A72 !important + color: white !important + + &.active + background-color: #4A1A72 !important + border-color: #4A1A72 !important + + &.disabled, &[disabled] + background-color: #8A5AAA !important + border-color: #8A5AAA !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) !important + + &:hover + opacity: 0.9 !important + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + + &:active, &:focus + background-color: #e6a617 !important + border-color: #e6a617 !important + color: #1D0631 !important + + &.active + background-color: #e6a617 !important + border-color: #e6a617 !important + + &.disabled, &[disabled] + background-color: #ffb91a !important + border-color: #ffb91a !important + opacity: 0.4 !important + +.btn-secondary + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + border-color: #5A2A82 !important + background-color: white !important + color: #374151 !important + + &:active, &:focus + background-color: white !important + border-color: #5A2A82 !important + color: #374151 !important + + &.active + background-color: #f9fafb !important + border-color: #5A2A82 !important + + &.disabled, &[disabled] + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important + color: #d1d5db !important + + &:active, &:focus + background-color: #2D1548 !important + border-color: #ffb91a !important + + &.active + background-color: #1D0631 !important + border-color: #ffb91a !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + opacity: 0.6 !important + +.btn-outline + background-color: transparent !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + + &:hover + background-color: #f9fafb !important + border-color: #d1d5db !important + + &:active, &:focus + background-color: #f3f4f6 !important + + &.disabled, &[disabled] + background-color: transparent !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: transparent !important + border-color: #5A2A82 !important + color: #d1d5db !important + + &:hover + background-color: rgba(90, 42, 130, 0.15) !important + border-color: #ffb91a !important + + &:active, &:focus + background-color: rgba(90, 42, 130, 0.25) !important + + &.disabled, &[disabled] + background-color: transparent !important + color: #6b7280 !important + opacity: 0.6 !important .btn-refresh color: white !important @@ -98,222 +234,487 @@ button.btn:active padding: 10px 18px min-width: 48px transition: all 0.15s ease-in-out + &:hover background-color: rgba(90, 42, 130, 0.9) !important border-color: #5A2A82 !important transform: none + &:active background-color: #4A1A72 !important border-color: #4A1A72 !important + &.no-hover:hover background-color: #5A2A82 !important + &.active, &.no-border.active background-color: #4A1A72 !important border-color: #4A1A72 + &.disabled, &[disabled] background-color: #8A5AAA !important border-color: #8A5AAA !important opacity: 0.6 cursor: not-allowed + i font-size: 16px line-height: 1 - - // Dark mode support + + // Dark mode .dark & background-color: #ffb91a !important border-color: #ffb91a !important color: #1D0631 !important + &:hover background-color: rgba(255, 185, 26, 0.9) !important border-color: #ffb91a !important + &:active background-color: #e6a617 !important border-color: #e6a617 !important + &.active, &.no-border.active background-color: #e6a617 !important border-color: #e6a617 -.btn-success - @include join-now-button-colors - &.no-border:hover - border-color: #659965 - &.no-hover:hover - background-color: #437417 !important - &.active, &.no-border.active - background-color: #79B247 !important - border-color: #79B247 + &.disabled, &[disabled] + background-color: #ffb91a !important + border-color: #ffb91a !important + opacity: 0.4 + +.i_use_this_btn + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + width: 100% + + &:hover + border-color: #5A2A82 !important + background-color: white !important + color: #374151 !important + + &:active, &:focus + background-color: white !important + border-color: #5A2A82 !important + &.disabled, &[disabled] - background-color: #79B247 !important - background-color: #FFB819 !important + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + +.btn-header + width: 100px + +.btn-info + background-color: #3b82f6 !important + border-color: #3b82f6 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + &:hover - background-color: #E07C05 !important - border-color: #E07C05 !important + opacity: 0.9 !important + background-color: #3b82f6 !important + border-color: #3b82f6 !important + color: white !important + + &:active, &:focus + background-color: #2563eb !important + border-color: #2563eb !important + + &.active + background-color: #2563eb !important + border-color: #2563eb !important + + &.disabled, &[disabled] + background-color: #93c5fd !important + border-color: #93c5fd !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #3b82f6 !important + border-color: #3b82f6 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #2563eb !important + border-color: #2563eb !important + + &.disabled, &[disabled] + background-color: #3b82f6 !important + opacity: 0.4 !important + +.btn-success + background-color: #10b981 !important + border-color: #10b981 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #10b981 !important + border-color: #10b981 !important + color: white !important + + &:active, &:focus + background-color: #059669 !important + border-color: #059669 !important + + &.active + background-color: #059669 !important + border-color: #059669 !important + + &.disabled, &[disabled] + background-color: #6ee7b7 !important + border-color: #6ee7b7 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #10b981 !important + border-color: #10b981 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #059669 !important + border-color: #059669 !important + + &.disabled, &[disabled] + background-color: #10b981 !important + opacity: 0.4 !important + .btn-warning - @include warning-button-colors - &.no-hover:hover - background-color: #9F4F0A !important - &.active, &.no-border.active - background-color: #F58220 !important - border-color: #F58220 + background-color: #f59e0b !important + border-color: #f59e0b !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #f59e0b !important + border-color: #f59e0b !important + color: white !important + + &:active, &:focus + background-color: #d97706 !important + border-color: #d97706 !important + + &.active + background-color: #d97706 !important + border-color: #d97706 !important + + &.disabled, &[disabled] + background-color: #fbbf24 !important + border-color: #fbbf24 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #f59e0b !important + border-color: #f59e0b !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #d97706 !important + border-color: #d97706 !important + + &.disabled, &[disabled] + background-color: #f59e0b !important + opacity: 0.4 !important .btn-danger - @include danger-button-colors - &.no-hover:hover - background-color: #9F2609 !important - &.active, &.no-border.active - background-color: #F5471D !important - border-color: #F5471D + background-color: #ef4444 !important + border-color: #ef4444 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #ef4444 !important + border-color: #ef4444 !important + color: white !important + + &:active, &:focus + background-color: #dc2626 !important + border-color: #dc2626 !important + + &.active + background-color: #dc2626 !important + border-color: #dc2626 !important + &.disabled, &[disabled] - background-color: #F5471D !important + background-color: #fca5a5 !important + border-color: #fca5a5 !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #ef4444 !important + border-color: #ef4444 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #dc2626 !important + border-color: #dc2626 !important + + &.disabled, &[disabled] + background-color: #ef4444 !important + opacity: 0.4 !important + +// Dark neutral (unchanged - still works with branding) .btn-inverse background-color: #575757 !important - border-color: #575757 + border-color: #575757 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #303030 !important - &.no-border:hover - border-color: #303030 - &.no-hover:hover - background-color: #555555 !important - &.active + border-color: #303030 !important + + &.active, &.no-border.active background-color: #434343 !important border-color: #333333 - &.no-border.active - background-color: #3b3b3b !important - border-color: #3b3b3b + &.disabled, &[disabled] background-color: #555555 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #4b5563 !important + border-color: #4b5563 !important + color: white !important + + &:hover + background-color: #374151 !important + border-color: #374151 !important + + &.disabled, &[disabled] + opacity: 0.4 !important +// Muted purple accent (renamed intent: subtle brand accent) .btn-pink background-color: #7E7392 !important - border-color: #7E7392 + border-color: #7E7392 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #53456D !important - &.no-border:hover - border-color: #b73766 - &.no-hover:hover - background-color: #d6487e !important - &.active - background-color: #c74072 !important - border-color: #b33564 - &.no-border.active - background-color: #be386a !important - border-color: #be386a + border-color: #53456D !important + + &.active, &.no-border.active + background-color: #53456D !important + border-color: #53456D + &.disabled, &[disabled] - background-color: #d6487e !important + background-color: #7E7392 !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #7E7392 !important + border-color: #7E7392 !important + color: white !important + + &:hover + background-color: #53456D !important + border-color: #53456D !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Teal accent (renamed intent: secondary brand color) .btn-purple background-color: #45867A !important - border-color: #45867A + border-color: #45867A !important + color: white !important + font-weight: 600 !important + &:hover background-color: #166859 !important - &.no-border:hover - border-color: #281649 - &.no-hover:hover - background-color: #281649 !important + border-color: #166859 !important + &.active, &.no-border.active - background-color: #7865A4 !important - border-color: #7865A4 + background-color: #166859 !important + border-color: #166859 + &.disabled, &[disabled] - background-color: #7865A4 !important + background-color: #45867A !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #45867A !important + border-color: #45867A !important + color: white !important + &:hover + background-color: #166859 !important + border-color: #166859 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Neutral grey .btn-grey background-color: #797979 !important - border-color: #797979 + border-color: #797979 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #575757 !important - &.no-border:hover - border-color: #575757 - &.no-hover:hover - background-color: #575757 !important + border-color: #575757 !important + &.active, &.no-border.active - background-color: #797979 !important - border-color: #797979 + background-color: #575757 !important + border-color: #575757 + &.disabled, &[disabled] background-color: #9A9A9A !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #6b7280 !important + border-color: #6b7280 !important + color: white !important + + &:hover + background-color: #4b5563 !important + border-color: #4b5563 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Soft accent (green-ish, used for subtle highlights) .btn-yellow background-color: #B3D084 !important - border-color: #B3D084 - color: #996633 !important + border-color: #B3D084 !important + color: #374151 !important + font-weight: 600 !important + &:hover background-color: #A0C465 !important - &.no-border:hover - border-color: #A0C465 - &.no-hover:hover - background-color: #A0C465 !important + border-color: #A0C465 !important + &.active, &.no-border.active - background-color: #B3D084 !important - border-color: #B3D084 + background-color: #A0C465 !important + border-color: #A0C465 + &.disabled, &[disabled] background-color: #B3D084 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #B3D084 !important + border-color: #B3D084 !important + color: #1D0631 !important + &:hover + background-color: #A0C465 !important + border-color: #A0C465 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Light / muted (used for de-emphasized actions) .btn-light background-color: #DDD !important - border-color: #DDD + border-color: #DDD !important color: #888888 !important + font-weight: 500 !important + &:hover background-color: #BCBCBC !important - &.no-border:hover - border-color: #BCBCBC - &.no-hover:hover - background-color: #BCBCBC !important + border-color: #BCBCBC !important + &.active, &.no-border.active - background-color: #DDD !important - border-color: #DDD + background-color: #BCBCBC !important + border-color: #BCBCBC + &.disabled, &[disabled] background-color: #DDD !important + opacity: 0.6 !important + cursor: not-allowed !important + &.btn-mini:after left: -2px right: -2px top: -2px bottom: -2px + &.btn-small:after left: -4px right: -4px top: -4px bottom: -4px + &.btn-large:after left: -6px right: -6px top: -6px bottom: -6px + // Dark mode + .dark & + background-color: #374151 !important + border-color: #374151 !important + color: #9ca3af !important + + &:hover + background-color: #4b5563 !important + border-color: #4b5563 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + .btn &.disabled.active, &[disabled].active, &.disabled:focus, &[disabled]:focus, &.disabled:active, &[disabled]:active - outline: none + outline: none !important + top: 0 !important + left: 0 !important + &.disabled:active, &[disabled]:active - top: 0 - left: 0 - &.active - color: #efe5b5 - &:after - display: inline-block - content: "" - position: absolute - border-bottom: 1px solid #efe5b5 - left: -4px - right: -4px - bottom: -4px - &.btn-small:after - left: -3px - right: -3px - bottom: -3px - border-bottom-width: 1px - &.btn-large:after - left: -5px - right: -5px - bottom: -5px - border-bottom-width: 1px - &.btn-mini:after, &.btn-minier:after - left: -1px - right: -1px - bottom: -1px - border-bottom-width: 1px - &.btn-yellow:after - border-bottom-color: #c96338 - &.btn-light - color: #515151 - &:after - border-bottom-color: #B5B5B5 + top: 0 !important + left: 0 !important + + &:hover + top: 0 !important + left: 0 !important diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index db0833582..1a2fec7de 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -382,6 +382,51 @@ color: white background-color: color-gradient($SECONDARY_SLATE_BLUE, 120) !important +@mixin modern-pagination-styles + display: flex + align-items: center + justify-content: center + gap: 8px + margin-top: 32px + + .page-btn + width: 36px + height: 36px + border-radius: 8px + border: 1px solid #e5e7eb + background-color: #ffffff !important + color: #111827 + font-size: 14px + font-weight: 500 + cursor: pointer + transition: all 0.2s ease + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + + html.dark & + border-color: rgba(255, 255, 255, 0.1) + background-color: #2D1548 !important + color: #ffffff + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1) + + &:hover:not(:disabled) + border-color: #5A2A82 + + html.dark & + border-color: #ffb91a + + &.active + background-color: #5A2A82 !important + color: #ffffff + border-color: transparent + + html.dark & + background-color: #ffb91a !important + color: #1D0631 + + &:disabled + opacity: 0.4 + cursor: not-allowed + //////////////////////////////////////////////////////////////////// // // Project mixins diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index 2187fe1e3..af3de0fd2 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -28,10 +28,10 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ background-position: -#{($multiplier * $dimension)+($multiplier*0.8)}px 0 background-repeat: no-repeat position: absolute - top: 1rem + top: 16px .margin_top_two - margin-top: 2rem + margin-top: 32px .thirtyfive_project_activity_text width: 200px @@ -42,77 +42,6 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ display: -ms-flexbox display: flex -#projects_index_list, #managed_projects_list - margin: 20px 0 0 0 - .well - padding: 15px - h2 - line-height: inherit - .compare - margin: -15px -15px 0 0 - .sp_form.styled - background-color: #DBE4EE - padding: 6px 4px 6px 10px - margin: 0 - border-radius: 0 4px 0 0 - -webkit-border-radius: 0 4px 0 0 - &.selected - background-color: #0088cc - color: #fff - .logo - margin: 25px 20px 25px 0 - .info - width: 490px - i - float: right - .claimed-by-org - margin: 0 15px 0 10px - .desc - margin-bottom: 10px - &.last - display: none - margin-bottom: 20px - .stats - margin: 0 15px 0 0 - span - margin-left: 10px - a - width: 80px - text-align: right - display: inline-block - font-weight: bold - font-size: 1.2em - span - margin-left: 0 - font-size: 12px - .reviews-and-pai - position: relative - @each $level in $pai_levels - .twentyfive_project_activity_level_#{nth($level, 1)} - margin-left: 0 - top: 0 - .twentyfive_project_activity_text - margin: -5px 0 0 25px - text-align: center - width: 65px - min-height: 35px - .stars - margin: 10px 0 5px 4px - .reviews - clear: left - width: 85px - text-align: center - .add-info - clear: left - width: 574px - .main_language - margin-left: 85px - a - text-decoration: underline - .tags - clear: left - margin-top: 10px - .cocomo-form border: 2px solid #E0E4E8 .control-label @@ -137,7 +66,7 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ margin-top: 8px #widgets - width: 31rem + width: 496px #project_header padding-right: 0px @@ -153,15 +82,15 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ li margin: 15px 0 15px 30px #add_this - margin-left: 5.3rem - margin-top: 5.3rem + margin-left: 85px + margin-top: 85px a @include add-this-link-colors #project_container #project_masthead #widgets - width: 30rem + width: 480px // settings page styles .settings_module :cursor pointer @@ -261,7 +190,7 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ padding-left: 10px padding-right: 10px .range-good, .range-poor - font-size: 1.2rem + font-size: 19px text-align: center #pss_bg, #pvs_bg @@ -440,10 +369,10 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ .ctrl width: auto .title - padding-right: 1rem + padding-right: 16px #vulnerability-chart-divider - padding: 40px 4rem 20px 4rem !important + padding: 40px 64px 20px 64px !important .vulnerabilities-datatable tbody tr @@ -476,9 +405,9 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ margin-right: auto #project_badges_page - margin-bottom: 2rem + margin-bottom: 32px .title - margin-bottom: 2rem + margin-bottom: 32px .no-left-padding padding-left: 0 .badge_table_container @@ -486,22 +415,22 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ .table tbody tr td - padding-top: 1.5rem - padding-bottom: 1.5rem + padding-top: 24px + padding-bottom: 24px vertical-align: top !important .btn-mini.col-xs-12 - padding: 0.12rem 1.5rem + padding: 2px 24px width: 100% .url_update_btn color: green - font-size: 2.1rem + font-size: 34px cursor: pointer .edit_url_close_btn color: red - font-size: 2.1rem + font-size: 34px cursor: pointer #save_badge - margin-bottom: 0.5rem + margin-bottom: 8px .project_summary_container section @@ -569,3 +498,543 @@ dl.kpi margin: 0 30px .scan_data_title padding-left: 1em + +// Modern Card Layout Design - Figma Update +// SASS Variables for Light Mode +$primary-color: #5A2A82 +$primary-hover: rgba(90, 42, 130, 0.9) +$accent-color: #ffb91a +$bg-color: #f9fafb +$card-bg: #ffffff +$text-primary: #1f2937 +$text-secondary: #6b7280 +$border-color: rgba(229, 231, 235, 0.8) +$shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) +$shadow-md: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) +$shadow-lg: 0 8px 16px rgba(0,0,0,0.16) + +// Dark Mode Variables +$dark-primary-color: #ffb91a +$dark-primary-hover: rgba(255, 185, 26, 0.9) +$dark-accent-color: #5A2A82 +$dark-bg-color: #1D0631 +$dark-card-bg: #2D1548 +$dark-text-primary: #ffffff +$dark-text-secondary: #d1d5db +$dark-border-color: rgba(255, 255, 255, 0.1) +$dark-shadow-sm: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) +$dark-shadow-md: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) +$dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) + +// Modern Projects Index Page +#projects_index_page + background-color: $bg-color + min-height: 100vh + padding: 32px 0 + transition: background-color 0.3s ease, color 0.3s ease + html.dark & + background-color: $dark-bg-color + + .projects-container + max-width: 1280px + margin: 0 auto + padding: 0 16px + + // Header Section + .projects-header + display: flex + justify-content: space-between + align-items: center + margin-bottom: 24px + flex-wrap: wrap + gap: 16px + + h1 + font-size: 30px + font-weight: bold + color: $text-primary + margin: 0 + html.dark & + color: $dark-text-primary + + @media (min-width: 640px) + font-size: 36px + + .header-actions + display: flex + gap: 12px + align-items: center + flex-wrap: wrap + + .btn-browse-tags, + .btn-add-project + padding: 8px 16px + border-radius: 8px + font-weight: 600 + font-size: 14px + transition: all 0.2s ease + text-decoration: none + display: inline-flex + align-items: center + gap: 8px + white-space: nowrap + + i + font-size: 14px + flex-shrink: 0 + + .btn-browse-tags + background-color: $card-bg + border: 1px solid $border-color + color: $text-secondary + box-shadow: $shadow-sm + &:hover + border-color: $primary-color + html.dark & + background-color: $dark-card-bg + border-color: $dark-border-color + color: #ffffff !important + box-shadow: $dark-shadow-sm + &:hover + border-color: $dark-primary-color + + .btn-add-project + background-color: $primary-color + color: #ffffff + border: none + box-shadow: $shadow-md + &:hover + background-color: $primary-hover + html.dark & + background-color: $dark-primary-color + color: #1D0631 !important + box-shadow: $dark-shadow-md + &:hover + background-color: $dark-primary-hover + + +// Modern Project Card +.project-card + background-color: $card-bg + border-radius: 12px + border: 1px solid $border-color + padding: 20px 24px + margin-bottom: 16px + box-shadow: $shadow-sm + transition: all 0.3s ease + html.dark & + background-color: $dark-card-bg + border-color: $dark-border-color + box-shadow: $dark-shadow-sm + + &:hover + border-color: $primary-color + box-shadow: $shadow-md + html.dark & + border-color: $dark-primary-color + box-shadow: $dark-shadow-md + + .project-header + display: flex + justify-content: space-between + align-items: flex-start + margin-bottom: 16px + + .project-title-section + flex: 1 + + h2 + margin: 0 0 4px 0 + font-size: 18px + font-weight: 600 + + @media (min-width: 640px) + font-size: 20px + + a + color: $primary-color + text-decoration: none + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color !important + + .compare-section + .compare-checkbox + display: flex + align-items: center + gap: 6px + font-size: 14px + color: $text-secondary + cursor: pointer + transition: color 0.15s ease + + &:hover + color: $primary-color + + &.selected + color: $primary-color + font-weight: 500 + + i + color: $primary-color + + html.dark & + color: $dark-text-secondary + + &:hover + color: $dark-primary-color + + &.selected + color: $dark-primary-color + + i + color: $dark-primary-color + + .project-content + display: grid + grid-template-columns: 1fr auto + column-gap: 24px + row-gap: 0 + + @media (max-width: 768px) + grid-template-columns: 1fr + + .project-meta + grid-column: 1 + grid-row: 1 + display: flex + gap: 16px + font-size: 12px + color: $text-secondary + html.dark & + color: $dark-text-secondary + + @media (min-width: 640px) + font-size: 14px + + .claimed-by + span + color: $primary-color + font-weight: 500 + margin-left: 4px + + a + color: $primary-color + text-decoration: none + &:hover + text-decoration: underline + + html.dark & + color: $dark-primary-color + + .analyzed-date + font-style: italic + margin-left: auto + + > i + color: $primary-color !important + + abbr + color: inherit !important + + html.dark & + > i + color: $dark-primary-color !important + + .soft + color: $text-secondary + html.dark & + color: $dark-text-secondary + + .project-main + grid-column: 1 + grid-row: 2 + min-width: 300px + + .project-icon-desc + display: flex + gap: 12px + margin-bottom: 12px + + .project-icon + flex-shrink: 0 + width: 48px + height: 48px + background-color: $bg-color + border-radius: 12px + border: 1px solid $border-color + display: flex + align-items: center + justify-content: center + font-size: 24px + box-shadow: 0 1px 2px rgba(0,0,0,0.05) + html.dark & + background-color: $dark-bg-color + border-color: $dark-border-color + + @media (min-width: 640px) + width: 56px + height: 56px + font-size: 30px + + .project-description + flex: 1 + font-size: 14px + color: $text-secondary + line-height: 1.5 + html.dark & + color: $dark-text-secondary + + .read-more + color: $primary-color + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .project-lang-license + display: flex + gap: 24px + margin-bottom: 12px + font-size: 14px + color: $text-secondary + flex-wrap: wrap + html.dark & + color: $dark-text-secondary + + .language + a + color: $primary-color + font-weight: 500 + text-decoration: none + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .licenses + .license-label + font-weight: 500 + color: $text-primary + html.dark & + color: $dark-text-primary + + .project-tags + display: flex + flex-wrap: wrap + gap: 6px + align-items: center + + .tags-label + font-size: 12px + color: $text-secondary + margin-right: 4px + html.dark & + color: $dark-text-secondary + + .tag + padding: 2px 10px + border-radius: 9999px + font-size: 11px + font-weight: 500 + background-color: rgba(255, 185, 26, 0.2) + color: $accent-color + border: 1px solid rgba(255, 185, 26, 0.3) + transition: background-color 0.2s ease + cursor: pointer + text-decoration: none + &:hover + background-color: rgba(255, 185, 26, 0.3) + html.dark & + background-color: rgba(255, 185, 26, 0.15) + color: $dark-primary-color + border-color: rgba(255, 185, 26, 0.2) + &:hover + background-color: rgba(255, 185, 26, 0.25) + + .more-tags + color: $primary-color + font-size: 12px + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .project-stats + grid-column: 2 + grid-row: 1 / 3 + min-width: 280px + border-left: 1px solid $border-color + padding-left: 24px + html.dark & + border-color: $dark-border-color + + @media (max-width: 768px) + grid-column: 1 + grid-row: auto + border-left: none + border-top: 1px solid $border-color + padding-left: 0 + padding-top: 16px + + .stats-grid + display: grid + grid-template-columns: 1fr auto + gap: 10px 20px + align-items: center + + .stat-item + display: flex + align-items: center + gap: 10px + + .stat-value + font-size: 16px + font-weight: bold + color: $primary-color + min-width: 70px + text-align: right + html.dark & + color: $dark-primary-color + + @media (min-width: 640px) + font-size: 18px + + @media (min-width: 768px) + font-size: 20px + + .stat-label + font-size: 12px + color: $text-secondary + html.dark & + color: $dark-text-secondary + + @media (min-width: 640px) + font-size: 14px + + .activity-indicator + grid-row: span 2 + display: flex + flex-direction: column + align-items: center + gap: 4px + + .activity-icon + position: relative + width: 40px + height: 40px + display: flex + align-items: center + justify-content: center + + a[class^="twentyfive_project_activity_level_"] + position: static !important + margin-left: 0 !important + top: auto !important + display: block + transform: scale(1.6) + + .activity-label + font-size: 11px + color: $text-secondary + text-align: center + line-height: 1.2 + max-width: 80px + html.dark & + color: $dark-text-secondary + + .rating-section + display: flex + align-items: center + gap: 6px + + .stars + display: flex + gap: 2px + + .star + width: 14px + height: 14px + + &.filled + color: #ffb91a + + &.empty + color: #d1d5db + html.dark & + color: #4b5563 + + .review-count + font-size: 12px + color: $primary-color + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .use-this-button + display: flex + justify-content: center + align-items: center + + .btn-use-this + padding: 6px 16px + background-color: $primary-color + color: #ffffff + border: none + border-radius: 8px + font-size: 12px + font-weight: 600 + cursor: pointer + transition: background-color 0.2s ease + &:hover + background-color: $primary-hover + html.dark & + background-color: $dark-primary-color + color: $dark-bg-color + &:hover + background-color: $dark-primary-hover + +// Responsive adjustments +@media (max-width: 1024px) + .project-card + padding: 16px + + .project-header + .project-meta + font-size: 12px + +@media (max-width: 640px) + #projects_index_page + .projects-header + .header-actions + width: 100% + justify-content: flex-start + + .btn-browse-tags, + .btn-add-project + flex: 1 + min-width: 0 + justify-content: center + + .project-card + padding: 16px + + .project-header + .project-meta + font-size: 12px + + .project-content + .project-main + .project-icon-desc + .project-icon + width: 48px + height: 48px + font-size: 24px diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index 6164b1c78..f2b1faa6f 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -1,164 +1,515 @@ -#search-dingus - @include site-well-color - padding: 10px 15px - line-height: 29px - min-height: 50px - margin-bottom: 1em - border-radius: 8px - #value_select - display: inline !important - select - width: 400px - .col-md-5 - width: 50% - display: flex - align-items: stretch - padding: 0 - - .col-md-4 - width: 32% - #sort_by - float: right - padding-right: 10px - - label.paginate - font-size: 13px - - label.sr-only - position: absolute - width: 1px - height: 1px - padding: 0 - margin: -1px - overflow: hidden - clip: rect(0, 0, 0, 0) - white-space: nowrap - border-width: 0 - - .search-input-group - display: flex - align-items: stretch - width: 100% - max-width: 500px - - button.btn - height: auto - border: 1px solid #5A2A82 - line-height: 1 - padding: 8px 16px +// Modern Header Search Form +.header-search-form + display: flex + align-items: stretch + flex: 1 + max-width: 400px + height: 40px + + @media (max-width: 640px) + order: 1 + max-width: 100% + flex-basis: 100% + + .header-search-dropdown + position: relative display: flex - align-items: center - justify-content: center - - input[type="text"] + + .search-filter-btn + display: flex + align-items: center + gap: 6px + height: 40px + padding: 0 12px + background-color: #f9fafb + border: 1px solid #d1d5db + border-right: none + border-radius: 8px 0 0 8px + color: #1f2937 + font-size: 14px + font-weight: 500 + white-space: nowrap + transition: all 0.2s ease + cursor: pointer + box-sizing: border-box + line-height: normal + + &:hover + background-color: #f3f4f6 + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &:hover + background-color: rgba(29, 6, 49, 0.8) + + .selection + display: inline-block + + i + font-size: 12px + transition: transform 0.2s ease + + .search-dropdown-menu + width: 160px + margin-top: 4px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px + overflow: hidden + z-index: 1000 + // Material Design elevation shadow (level 3) + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + .dropdown-item + display: block + width: 100% + padding: 8px 16px + color: #111827 + font-size: 14px + font-weight: 400 + text-align: left + text-decoration: none + background: transparent + border: none + box-sizing: border-box + transition: background 0.15s ease, color 0.15s ease + cursor: pointer + + &:hover + background: #f3f4f6 + + &.active + background-color: #5A2A82 !important + color: #ffffff + font-weight: 400 + + &:hover + background-color: #5A2A82 + + html.dark & + color: #ffffff + + &:hover + background: #374151 + + &.active + background-color: #ffb91a !important + color: #5A2A82 + font-weight: 400 + + &:hover + background-color: #ffb91a + + .header-search-input flex: 1 - min-width: 250px - max-width: 100% - font-size: 14px - color: #1f2937 + height: 40px + padding: 0 12px background-color: #ffffff border: 1px solid #d1d5db - padding: 10px 14px - transition: all 0.15s ease-in-out - border-radius: 12px 0 0 12px - margin: 0 - height: auto - line-height: 1.5 - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) - &:hover - border-color: #9ca3af + color: #1f2937 + font-size: 14px + outline: none + border-right: none + box-shadow: none + min-width: 0 + transition: border-color 0.2s ease + box-sizing: border-box + line-height: normal + + &::placeholder + color: #6b7280 + &:focus outline: none border-color: #5A2A82 - box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) - background-color: #ffffff - &::placeholder - color: #9ca3af - font-size: 14px - - // Dark mode support - .dark & - background-color: rgba(29, 6, 49, 0.5) - input[type="text"] + box-shadow: none + + html.dark & + border-color: #ffb91a !important + + html.dark & background-color: #1D0631 + border-color: #5A2A82 !important color: #ffffff - border-color: #4b5563 - &:hover - border-color: #6b7280 - &:focus - border-color: #ffb91a - box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) - background-color: #1D0631 + &::placeholder + color: #d1d5db + + .header-search-btn + height: 40px + padding: 0 16px + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 0 8px 8px 0 + color: #ffffff + font-size: 14px + cursor: pointer + transition: background-color 0.2s ease + display: flex + align-items: center + justify-content: center + box-sizing: border-box + line-height: normal + + &:hover + background-color: rgba(90, 42, 130, 0.9) + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + + &:hover + background-color: rgba(255, 185, 26, 0.9) + + i + font-size: 14px + +// Modern Search/Filter Bar (for projects index and other listing pages) +.search-filter-bar + background-color: #ffffff + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.8) + padding: 16px + margin-bottom: 24px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: background-color 0.3s ease + + html.dark & + background-color: #2D1548 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + + .search-filter-content + display: flex + align-items: center + gap: 12px + flex-wrap: wrap + + .pagination-info + font-size: 14px + color: #6b7280 + white-space: nowrap + + html.dark & + color: #d1d5db + + .page-number + font-weight: 600 + color: #1f2937 + + html.dark & + color: #ffffff + + .search-section + flex: 1 + display: flex + align-items: center + gap: 8px + min-width: 300px + + label + font-size: 14px + color: #6b7280 + white-space: nowrap + line-height: 40px + margin: 0 + padding: 0 + + html.dark & + color: #d1d5db + + .search-input-wrapper + flex: 1 + position: relative + + input + width: 100% + height: 40px + padding: 0 40px 0 12px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px !important + color: #111827 + font-size: 14px + font-weight: 400 + line-height: normal + margin: 0 + box-sizing: border-box + transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease + + &::placeholder + color: #9ca3af + + &:hover + background-color: #f9fafb + border-color: #5A2A82 + + &:focus + outline: none + background-color: #ffffff + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &::placeholder + color: #9ca3af + + &:hover + background-color: rgba(29, 6, 49, 0.8) + border-color: #ffb91a + + &:focus + background-color: #1D0631 + border-color: #ffb91a + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) + + .search-cancel-btn + position: absolute + right: 8px + top: 50% + transform: translateY(-50%) + background: transparent + border: none + color: #9ca3af + font-size: 14px + cursor: pointer + padding: 4px 6px + line-height: 1 + transition: color 0.15s ease + + &:hover + color: #6b7280 + + html.dark & + color: #6b7280 + + &:hover + color: #d1d5db + + .search-refresh-btn + display: flex + align-items: center + justify-content: center + height: 40px + width: 40px + padding: 0 + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 8px + color: #ffffff + font-size: 14px + cursor: pointer + transition: background-color 0.2s ease, opacity 0.2s ease + box-sizing: border-box + flex-shrink: 0 + + &:hover + opacity: 0.9 + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + + &:hover + opacity: 0.9 + + i + font-size: 14px + + .sort-section + display: flex + align-items: center + gap: 8px + + label + font-size: 14px color: #6b7280 - - select - width: 150px + white-space: nowrap + + html.dark & + color: #d1d5db + + // Custom Sort Dropdown + .custom-sort-dropdown + position: relative + display: inline-block + + .sort-dropdown-btn + display: flex + align-items: center + gap: 8px + height: 40px + padding: 0 12px + background-color: #f9fafb + border: 1px solid #d1d5db + border-radius: 8px + color: #111827 + font-size: 14px + font-weight: 400 + min-width: 180px + cursor: pointer + transition: background 0.15s ease, border-color 0.15s ease + white-space: nowrap + box-sizing: border-box + + &:hover + background-color: #f3f4f6 + border-color: #5A2A82 + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &:hover + background-color: rgba(29, 6, 49, 0.8) + border-color: #ffb91a + + .selection + flex: 1 + text-align: left + + i + font-size: 12px + transition: transform 0.2s ease + + .sort-dropdown-menu + position: absolute + top: 100% + left: 0 + margin-top: 4px + min-width: 180px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px + overflow: hidden + z-index: 1000 + // Material Design elevation shadow (level 3) + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + .sort-dropdown-item + display: block + width: 100% + padding: 8px 16px + color: #111827 + font-size: 14px + font-weight: 400 + text-align: left + text-decoration: none + background: transparent + border: none + box-sizing: border-box + transition: background 0.15s ease, color 0.15s ease + cursor: pointer + + &:hover + background: #f3f4f6 + + &.active + background-color: #5A2A82 !important + color: #ffffff + font-weight: 400 + + &:hover + background-color: #5A2A82 + + html.dark & + color: #ffffff + + &:hover + background: #374151 + + &.active + background-color: #ffb91a !important + color: #5A2A82 + font-weight: 400 + + &:hover + background-color: #ffb91a + + .sort-value-input + display: none + + // Chosen select plugin support (for tag filtering) + #value_select + display: inline-block + margin-left: 8px + + select + min-width: 300px + + // Checkbox filters label.checkbox + display: inline-flex + align-items: center + gap: 6px + font-size: 14px + color: #6b7280 + margin: 0 12px 0 0 + cursor: pointer + font-weight: normal + + html.dark & + color: #d1d5db + input - margin-top: 4px - label.radio - input - width: 13px !important - margin-top: 4px - #human_edits - input - width: 14px + width: 16px + height: 16px + margin: 0 + cursor: pointer + + #human_edits, #enlistment_edits input - width: 14px - -#nav-top-bar .new_main_menu form#quicksearch - margin-top: -4px - button.submit - background: none - border: 0 - ul.dropdown-menu - border-radius: 0px - margin-top: -10px !important - li - display: inline - a - @include search-dropdown-link-color - font-size: 14px - text-transform: none - &:hover - @include search-dropdown-link-hover-colors - .btn-small - font-size: 10px - border-width: 2px !important - margin-right: 0px !important - .selection - font-weight: 100 - text-transform: none - &:hover - @include search-button-hover-colors - -@media (min-width: 320px) and (max-width: 360px) - form#quicksearch - margin-top: -20px !important - margin-right: -61px !important - .btn-small - font-size: 8px !important -@media (min-width: 361px) and (max-width: 480px) - form#quicksearch - margin-top: -6px !important - margin-right: -61px !important - .btn-small - font-size: 8px !important - -.global_top_search - height: 18px !important - @include global-top-search-colors - -.global_top_search_icon - @include global-top-search-icon-color - padding-bottom: 5px - font-size: 18px - -@media (min-width: 320px) and (max-width: 480px) - .global_top_search - height: 15px !important - font-size: 8px !important - .global_top_search_icon - font-size: 10px !important + width: 16px + height: 16px + + // Radio button filters + label.radio + display: inline-flex + align-items: center + gap: 6px + font-size: 14px + color: #6b7280 + margin: 0 12px 0 0 + cursor: pointer + font-weight: normal + + html.dark & + color: #d1d5db + + input + width: 16px + height: 16px + margin: 0 + cursor: pointer + + &.inline + display: inline-flex + +// Responsive adjustments for search-filter-bar +@media (max-width: 640px) + .search-filter-bar + .search-filter-content + .search-section, + .sort-section + width: 100% diff --git a/app/assets/stylesheets/session_projects.sass b/app/assets/stylesheets/session_projects.sass index b0f3d3e7f..8041df5cf 100644 --- a/app/assets/stylesheets/session_projects.sass +++ b/app/assets/stylesheets/session_projects.sass @@ -1,44 +1,250 @@ -.sp_label - margin-right: 10px - #sp_menu position: fixed - left: 50% - transform: translate(-50%, 0) - top: 0 - width: 960px - z-index: 1 - color: white - .inner - background-color: #0088cc - padding: 10px 20px 0 - box-shadow: 0 5px 0 #666 - -webkit-box-shadow: 0 5px 0 #666 - -ms-filter: "progid:DXImageTransform.Microsoft.DropShadow(color='#666666',offX='0', offY='5')" - ul.nav li - margin-right: 3em - &.last - margin-right: 0 - .sp_form - margin-bottom: 0 - .sp_label - color: #fff - margin-right: 0 - .sp_input - font-size: 17px - line-height: 17px - vertical-align: middle - margin-top: 2px + bottom: 0 + left: 0 + right: 0 + z-index: 1000 + display: none + transition: all 0.3s ease + + // Toggle Tab Button + .compare-toggle-tab + display: flex + justify-content: center + + .compare-toggle-button + display: flex + align-items: center + gap: 8px + padding: 8px 20px + background-color: #5A2A82 + color: #ffffff + border: none + border-radius: 12px 12px 0 0 + font-size: 14px + font-weight: 600 + cursor: pointer + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15) + transition: all 0.2s ease + + &:hover + opacity: 0.9 + + html.dark & + background-color: #ffb91a + color: #1D0631 + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3) + + .toggle-icon + transition: transform 0.2s ease + font-size: 12px + + &.rotate + transform: rotate(180deg) + + // Expandable Tray Content + .compare-tray-content + max-height: 0 + overflow: hidden + background-color: #ffffff + border-top: 1px solid #e5e7eb + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.12) + transition: max-height 0.3s ease + + &.expanded + max-height: 280px + + html.dark & + background-color: #2D1548 + border-top-color: #5A2A82 + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.4) + + .compare-tray-inner + max-width: 1200px + margin: 0 auto + padding: 16px 24px + + @media (max-width: 768px) + padding: 12px 16px + + // Projects Grid + .compare-projects-grid + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 12px + margin-bottom: 16px + + @media (min-width: 640px) + grid-template-columns: repeat(3, 1fr) + + .compare-project-slot + position: relative + background-color: #f9fafb + border: 1px solid #e5e7eb + border-radius: 12px + padding: 12px + display: flex + align-items: start + gap: 12px + min-height: 80px + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + + &.empty-slot + border: 2px dashed #d1d5db + flex-direction: column + align-items: center + justify-content: center + color: #9ca3af + + html.dark & + border-color: #5A2A82 + color: #6b7280 + + i + font-size: 20px + margin-bottom: 4px + + span + font-size: 12px + + .remove-project + position: absolute + top: 8px + right: 8px + width: 20px + height: 20px + padding: 0 + background-color: #e5e7eb + border: none + border-radius: 50% + color: #6b7280 + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.15s ease + + &:hover + background-color: #fee2e2 + color: #ef4444 + + html.dark & + background-color: rgba(255, 255, 255, 0.1) + color: #9ca3af + + &:hover + background-color: rgba(239, 68, 68, 0.2) + color: #fca5a5 + + i + font-size: 10px + + .project-slot-icon + flex-shrink: 0 + width: 40px + height: 40px + background-color: #e5e7eb + border: 1px solid #d1d5db + border-radius: 8px + display: flex + align-items: center + justify-content: center + overflow: hidden + + html.dark & + background-color: rgba(255, 255, 255, 0.1) + border-color: #6b7280 + + img + width: 100% + height: 100% + object-fit: cover + + i + font-size: 18px + color: #9ca3af + + .project-slot-info + flex: 1 + min-width: 0 + padding-right: 20px + + .project-slot-name + font-size: 14px + font-weight: 600 + color: #111827 + margin: 0 0 4px 0 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + html.dark & + color: #ffffff + + .project-slot-meta + font-size: 12px + color: #6b7280 + margin: 2px 0 + + html.dark & + color: #9ca3af + + // Compare Action + .compare-action + display: flex + justify-content: center + + form + width: auto + + .btn-compare-submit + padding: 10px 32px + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 8px + color: #ffffff + font-size: 14px + font-weight: 600 cursor: pointer - .compare - float: right - font-weight: bold - .limit - display: inline - margin-left: 0.5em - -#compare-submit-button - margin: -2px 0px 10px - padding: 0px 12px 1px - height: 29px - width: 75px + transition: all 0.2s ease + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + + &:hover:not(:disabled) + opacity: 0.9 + + &:disabled + background-color: #d1d5db + border-color: #d1d5db + color: #9ca3af + cursor: not-allowed + box-shadow: none + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + + &:disabled + background-color: #4b5563 + border-color: #4b5563 + color: #6b7280 + +// Add bottom padding when compare tray is visible +#projects_index_page.has-compare-tray + .projects-container + padding-bottom: 120px + +// Compare checkbox disabled state +.compare-checkbox.disabled + opacity: 0.5 + cursor: not-allowed !important + + &:hover + color: #9ca3af !important + + html.dark & + color: #6b7280 !important diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 6541fb0c5..3094a2c25 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -10,8 +10,8 @@ def initialize(analysis) def data chart_data = series_and_range_data(@defaults) - .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) - .deep_merge(chart_watermark) + .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) + .deep_merge(chart_watermark) apply_commits_palette(chart_data) end diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index 7334835b6..ad7ff65b1 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -1,104 +1,135 @@ - project = project_index -.well.searchable{ id: "project_#{project.id}" } - %h2.title.pull-left= link_to h(project.name.truncate(70)), project_path(project), title: project.name - .pull-right - .compare - - project_compare_button(project, t('.compare')) if defined?(compare) && compare - - if params[:action].to_s != 'check_forge' - .i_use_this - - project_iusethis_button(project) if defined?(i_use_this) && i_use_this - .clear +.project-card.searchable{ id: "project_#{project.id}" } + / Project Header with Compare Option + .project-header + .project-title-section + %h2 + = link_to h(project.name.truncate(70)), project_path(project), title: project.name - .pull-left#inner_content - %a.pull-left.logo{ href: project_path(project), style: 'max-width: 64px;' } - - project_icon(project, :med) + - if defined?(compare) && compare + .compare-section + .compare-checkbox{ data: { project_id: project.to_param } } + %span Compare + %i.fa.fa-square-o - .info.pull-left - %p - - if project.organization + / Project Content + .project-content + .project-meta + - if project.organization + .claimed-by = t('.claimed_by') - = link_to h(truncate(project.organization.name, length: 30)), - organization_path(project.organization), class: 'claimed-by-org', title: project.organization.name - - else -   - + %span + = link_to h(truncate(project.organization.name, length: 30)), + organization_path(project.organization), title: project.organization.name + .analyzed-date - if !project.best_analysis.nil? || @analysis - analysis = (project.best_analysis || @analysis) %i %abbr.date{ title: analysis.updated_on } = t('.analyzed_ago', time: time_ago_in_words(analysis.updated_on)) - else - %span.soft.pull-right - %i= t('.no_analysis_available') + %span.soft + %i= t('.no_analysis_available') - .desc - - if project.description && project.description.strip.size > 340 - = expander(project.description, 300, 340) - - elsif project.description - %span.proj_desc_toggle{ style: 'display: inline', id: "proj_desc_#{project.id}_lg" } + / Main Content - Left Side + .project-main + / Icon and Description + .project-icon-desc + .project-icon + - if project.logo.present? + = image_tag project.logo.url(:med), alt: project.name + - else + %i.fa.fa-cube + + .project-description + - if project.description && project.description.strip.size > 220 + = project.description.truncate(220) + %span.read-more [More] + - elsif project.description = project.description + - else + %span.text-muted No description available - .stats.pull-left - %p - - if project.best_analysis.empty? - = link_to 0, '#' - - else - - code_total_str = project.best_analysis.code_total.to_human - = link_to code_total_str, languages_summary_project_analysis_path(project, 'latest') - %span= t('.loc') - %p - = link_to number_with_delimiter(project.active_committers), summary_project_contributors_path(project) - %span= t('.current_contributors') - %p - - if !project.best_analysis.nil? && project.best_analysis.last_commit_time - %a{ href: summary_project_commits_path(project) }= time_ago_in_words(project.best_analysis.last_commit_time) - - else - = link_to 0, '#' - %span= t('.since_last_commit') - %p - = link_to number_with_delimiter(project.user_count), users_project_path(project) - %span= t('.users_on_open_hub') + / Language and License + .project-lang-license + .language + - if project.best_analysis.present? && project.best_analysis.main_language + - lang = project.best_analysis.main_language.nice_name + Mostly written in + = link_to lang, languages_summary_project_analysis_path(project, 'latest') + - else + %span Not available - .reviews-and-pai.pull-left - - project_activity_level_class(project, :twentyfive) - - project_activity_level_text(project, :twentyfive) - .stars!= rating_stars('average_rating_stars', project.rating_average.to_f || 0, mini: true) - .reviews - = link_to pluralize_with_delimiter(project.reviews.count, t('.review')), summary_project_reviews_path(project) - .use_this{ style: 'margin-top: 10px; margin-left: 10px' } - - if logged_in? - = render partial: 'projects/show/i_use_this', locals: { project: project } - - else - .btn.btn-mini.i_use_this_btn{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' }= t('.i_use_this') - .modal.fade#LoginModal{ 'aria-hidden' => 'true', 'aria-labelledby' => 'LoginModalLabel', :role => 'dialog' } - .modal-dialog.modal-sm - .modal-content{ style: 'width: 398px' } - .modal-header - %button.close{ 'aria-label' => 'Close', 'data-dismiss' => 'modal', :type => 'button' } - %span{ 'aria-hidden' => 'true' } × - %h4.modal-title#LoginModalLabel= t('.login_required') - .model-body - = render 'sessions/sign_in' + .licenses + %span.license-label Licenses: + - if project.licenses.any? + = project.licenses.first(3).collect { |l| l.short_name.truncate(12) }.join(', ') + - else + = t('.no_declared_licenses') + + / Tags + .project-tags + - unless project.tag_list.blank? + %span.tags-label Tags + - tags = project.tag_list.split(' ') + - tags.first(8).each do |tag| + - tag = tag.fix_encoding_if_invalid + = link_to html_escape(tag), tags_path(names: tag), class: 'tag' + - if tags.length > 8 + %span.more-tags= "#{tags.length - 8} more..." + + / Stats - Right Side + .project-stats + + .stats-grid + / Row 1: Lines of Code | Activity Indicator (spans 2 rows) + .stat-item + %span.stat-value + - if project.best_analysis.empty? + 0 + - else + = project.best_analysis.code_total.to_human + %span.stat-label lines of code + + .activity-indicator + .activity-icon + - project_activity_level_class(project, :twentyfive) + .activity-label + = project_activity_text(project, true) + + / Row 2: Contributors + .stat-item + %span.stat-value= number_with_delimiter(project.active_committers) + %span.stat-label current contributors + + / Row 3: Last Commit | Star Rating + .stat-item + %span.stat-value + - if !project.best_analysis.nil? && project.best_analysis.last_commit_time + = time_ago_in_words(project.best_analysis.last_commit_time) + - else + 0 + %span.stat-label since last commit + + .rating-section + .stars + - rating = project.rating_average.to_f || 0 + - 5.times do |i| + - if i < rating.floor + %i.fa.fa-star.star.filled + - else + %i.fa.fa-star.star.empty + .review-count + = link_to pluralize_with_delimiter(project.reviews.count, t('.review')), summary_project_reviews_path(project) - .add-info - .main_language.pull-left - - if project.best_analysis.present? && project.best_analysis.main_language - - lang = project.best_analysis.main_language.nice_name - = link_to t('.mostly_written_in', lang: lang), languages_summary_project_analysis_path(project, 'latest') - - else - %span= t('.mostly_written_lang_not_available') - .licenses.pull-right - = link_to t('.licenses'), project_licenses_path(project) - - if project.licenses.any? - = project.licenses.first(3).collect { |l| l.short_name.truncate(12) }.join(', ') - - else - %span= t('.no_declared_licenses') - .clear_both + / Row 4: Users on Open Hub | I Use This Button + .stat-item + %span.stat-value= number_with_delimiter(project.user_count) + %span.stat-label users on Open Hub - .tags - - unless project.tag_list.blank? - - tags = project.tag_list.split(' ') - = tag_icon_link(project) - = tag_links(tags, 12) - - if tags.length > 12 - = link_to t('.n_more', n: (tags.length - 12)), project_path(project) + .use-this-button + - if logged_in? + = render partial: 'projects/show/i_use_this', locals: { project: project } + - else + %button.btn-use-this{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' } + I Use This diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 93a0e6eab..34506ed4c 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -2,20 +2,39 @@ - content_for(:html_title) { @query ? t('.page_title_query', query: @query) : t('.page_title') } - page_context[:select_top_menu_nav] = 'select_projects' - content_for :session_projects_banner do - .navbar.container#sp_menu + #sp_menu = render 'session_projects/menu' -#projects_index_header - %h1.pull-left= t('.projects') - .pull-right.margin_top_20 - = link_to t('.browse_projects'), tags_path, class: 'btn btn-light btn-mini' - = link_to t('.add_new_project'), new_project_path, class: 'btn btn-light btn-mini' +#projects_index_page + .projects-container + / Header Section with Actions + .projects-header + %h1= t('.projects') + .header-actions + = link_to tags_path, class: 'btn-browse-tags' do + %i.fa.fa-tag + = t('.browse_projects') + = link_to new_project_path, class: 'btn-add-project' do + %i.fa.fa-plus + = t('.add_new_project') -.clearfix   + / Header Search with Dropdown + = render 'shared/search' -= render partial: 'shared/search_dingus', locals: { collection: @projects, sort_context: :projects } -.clear + / Search and Filter Bar + = render 'shared/search_dingus', collection: @projects, sort_context: :projects -#projects_index_list - = render partial: 'project_index', collection: @projects, locals: { compare: true, show_active_committers: true } - = will_paginate @projects + / Projects List + #projects_index_list + = render partial: 'project_index', collection: @projects, locals: { compare: true, show_active_committers: true } + + / Modern Pagination + - if @projects.total_pages > 1 + .modern-pagination + %button.page-btn{disabled: @projects.current_page == 1, onclick: "window.location.href='?page=#{@projects.current_page - 1}'"} + %i.fa.fa-chevron-left + - (1..@projects.total_pages).each do |page| + %button.page-btn{class: ('active' if page == @projects.current_page), onclick: "window.location.href='?page=#{page}'"} + = page + %button.page-btn{disabled: @projects.current_page == @projects.total_pages, onclick: "window.location.href='?page=#{@projects.current_page + 1}'"} + %i.fa.fa-chevron-right diff --git a/app/views/session_projects/_menu.html.haml b/app/views/session_projects/_menu.html.haml index a4255a8b1..51236a48c 100644 --- a/app/views/session_projects/_menu.html.haml +++ b/app/views/session_projects/_menu.html.haml @@ -1,20 +1,43 @@ - if @session_projects.any? - .inner - %p.col.pull-left{ style: 'margin-right: 3em' } - %strong= t('.compare_projects') - - if @session_projects.size == 3 - %span.limit - = surround '(', ')' do - = t('.limit_reached') - %ul.nav.pull-left.margin_top_0 - - @session_projects.each_with_index do |project, i| - %li{ class: ('last' if i == @session_projects.length - 1) } - = form_tag nil, class: 'sp_form form-inline', id: "sp_frm_#{project.to_param}" do - %span.sp_label{ title: project.name } - = project.name.truncate(20) - %i.sp_input.icon.icon-remove-sign{ project_id: project.to_param } - %form.sp_form.form_inline.pull-right{ action: compare_projects_path, method: :get } - - @session_projects.each_with_index do |p, i| - %input{ type: 'hidden', name: "project_#{i}", value: p.name } - %input.btn.btn-sm#compare-submit-button{ type: 'submit', value: t('compare') } - .clearfix + / Toggle Button + .compare-toggle-tab + %button#compare_toggle_btn.compare-toggle-button + %i.fa.fa-exchange + Compare Projects (#{@session_projects.size}/3) + %i.fa.fa-chevron-up.toggle-icon + + / Expandable Tray Content + .compare-tray-content + .compare-tray-inner + / Selected Projects Grid + .compare-projects-grid + - @session_projects.each do |project| + .compare-project-slot + %button.remove-project{ type: 'button', data: { project_id: project.to_param } } + %i.fa.fa-times + .project-slot-icon + - if project.logo.present? + = image_tag project.logo.url(:small), alt: project.name + - else + %i.fa.fa-cube + .project-slot-info + %p.project-slot-name{ title: project.name }= project.name.truncate(25) + - if project.best_analysis + %p.project-slot-meta= "#{project.best_analysis.code_total.to_human} lines" + %p.project-slot-meta= "#{project.active_committers} contributors" + + - (3 - @session_projects.size).times do + .compare-project-slot.empty-slot + %i.fa.fa-plus + %span Select Project + + / Compare Action Button + .compare-action + %form{ action: compare_projects_path, method: :get } + - @session_projects.each_with_index do |p, i| + %input{ type: 'hidden', name: "project_#{i}", value: p.name } + %button.btn-compare-submit{ type: 'submit', disabled: @session_projects.size < 2 } + - if @session_projects.size >= 2 + = "Compare #{@session_projects.size} Projects" + - else + Compare (Select at least 2) diff --git a/app/views/shared/_search.html.haml b/app/views/shared/_search.html.haml index 62493903a..283e26988 100644 --- a/app/views/shared/_search.html.haml +++ b/app/views/shared/_search.html.haml @@ -3,22 +3,17 @@ global_search_param = '' if @avoid_global_search - unless request.path == root_path - %form.pull-right#quicksearch{ action: projects_path } - .dropdown - .btn-group.ux-dropdown - %a.btn.btn-small.dropdown-toggle{ 'data-toggle' => 'dropdown' } - %span.selection= t('projects_menu') - %span.caret - %ul.dropdown-menu - %li - %a{ val: 'people' }= t('people_menu') - %li - %a.default{ val: 'p' }= t('projects_menu') - %li - %a{ val: 'orgs' }= t('organizations_menu') - %input.search.text.global_top_search{ type: :text, name: 'query', placeholder: t('shared.search.search_text'), - value: global_search_param, autocomplete: 'off' } - %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type', value: 'projects' } - %button.submit.no_padding{ type: 'submit' } - .icon-search.global_top_search_icon + %form#header-search-form.header-search-form{ action: projects_path } + .btn-group.ux-dropdown.header-search-dropdown + %button.search-filter-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ val: 'p' }= t('projects_menu') + %i.fa.fa-chevron-down + .dropdown-menu.search-dropdown-menu + %button.dropdown-item.active{ type: 'button', val: 'p' }= t('projects_menu') + %button.dropdown-item{ type: 'button', val: 'people' }= t('people_menu') + %button.dropdown-item{ type: 'button', val: 'orgs' }= t('organizations_menu') + %input.header-search-input{ type: 'text', name: 'query', placeholder: t('shared.search.search_text'), value: global_search_param, autocomplete: 'off' } + %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type', value: 'projects' } + %button.header-search-btn{ type: 'submit' } + %i.fa.fa-search diff --git a/app/views/shared/_search_dingus.html.haml b/app/views/shared/_search_dingus.html.haml index 496109b75..61893b1f8 100644 --- a/app/views/shared/_search_dingus.html.haml +++ b/app/views/shared/_search_dingus.html.haml @@ -6,9 +6,9 @@ sort_context ||= nil no_match_found_type ||= :message -.margin_top_20#search-dingus - %form.form-inline - .row +.search-filter-bar + %form + .search-filter-content = render 'shared/search_dingus/page_entries_info', collection: collection, total_count: total_count = render 'shared/search_dingus/search_bar', search_type: search_type, tags: tags diff --git a/app/views/shared/search_dingus/_page_entries_info.html.haml b/app/views/shared/search_dingus/_page_entries_info.html.haml index f0bb5827a..eb3f78ec3 100644 --- a/app/views/shared/search_dingus/_page_entries_info.html.haml +++ b/app/views/shared/search_dingus/_page_entries_info.html.haml @@ -1,7 +1,8 @@ -.col-md-3.col-sm-3.col-xs-3.padding_left_25 +.pagination-info - if collection.respond_to?(:current_page) && !collection.length.zero? - %label.paginate= t('.pagination_status', current_page: number_with_delimiter(collection.current_page), - total_pages: number_with_delimiter(collection.total_pages)) + Showing page + %span.page-number= collection.current_page + of + %span.page-number= collection.total_pages - elsif !total_count.to_i.zero? - %label.paginate= t('.count_status', current_count: collection.length, - total_count: number_with_delimiter(total_count)) + = t('.count_status', current_count: collection.length, total_count: number_with_delimiter(total_count)) diff --git a/app/views/shared/search_dingus/_search_bar.html.haml b/app/views/shared/search_dingus/_search_bar.html.haml index ad48ad78a..da45c3607 100644 --- a/app/views/shared/search_dingus/_search_bar.html.haml +++ b/app/views/shared/search_dingus/_search_bar.html.haml @@ -1,5 +1,5 @@ - if search_type == :tags - .search_tags + .search-section %label= t('.filter_by_tags') .chosen#value_select = select_tag :names, options_for_select(tags.map { |ary| ary.map(&:fix_encoding_if_invalid) }, params[:names]), @@ -7,11 +7,11 @@ multiple: true, onchange: 'this.form.submit()' - else - .col-md-5.col-sm-5.col-xs-5.no_padding - %label.sr-only= t('.search_text') - .search-input-group - = text_field_tag :query, params[:query], - placeholder: t('.search_text') - - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-search + .search-section + %label{for: 'query'}= t('.search_text') + .search-input-wrapper + %input#query{type: 'text', name: 'query', placeholder: t('.search_text'), value: params[:query]} + %button.search-cancel-btn{ type: 'button', style: (params[:query].blank? ? 'display: none;' : '') } + %i.fa.fa-times + %button.search-refresh-btn{ type: 'submit' } + %i.fa.fa-refresh diff --git a/app/views/shared/search_dingus/_sort.html.haml b/app/views/shared/search_dingus/_sort.html.haml index b3e99c0a7..792727a04 100644 --- a/app/views/shared/search_dingus/_sort.html.haml +++ b/app/views/shared/search_dingus/_sort.html.haml @@ -1,28 +1,45 @@ -.col-md-4.col-sm-4.col-xs-4 - - if filter_type == :checkbox - %label.checkbox.pull-right.padding_right_20#human_edits - %input{ type: 'checkbox', checked: params[:human] == 'true' } - = t('.show_only_humans') - %label.checkbox.pull-right.padding_right_15#enlistment_edits - %input{ type: 'checkbox', id: 'enlistment_checkbox', checked: params[:enlistment] == 'true'} - = t('.filter_only_enlistment') - #sort_by - %label= t('.sort_by') - = select_tag :sort, options_for_select([["updated_at", "updated_at"], ["created_at", "created_at"]], - params[:sort]), - class: 'chzn-select', onchange: 'this.form.submit()' +- if filter_type == :checkbox + %label.checkbox#human_edits + %input{ type: 'checkbox', checked: params[:human] == 'true' } + = t('.show_only_humans') + %label.checkbox#enlistment_edits + %input{ type: 'checkbox', id: 'enlistment_checkbox', checked: params[:enlistment] == 'true'} + = t('.filter_only_enlistment') + .sort-section + %label= t('.sort_by') + :ruby + sort_options = [["updated_at", "updated_at"], ["created_at", "created_at"]] + current_sort = params[:sort] || "updated_at" + selected_label = sort_options.find { |opt| opt[1] == current_sort }&.first || sort_options.first.first + .custom-sort-dropdown + %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ data: { value: current_sort } }= selected_label + %i.fa.fa-chevron-down + .dropdown-menu.sort-dropdown-menu + - sort_options.each do |label, value| + %button.sort-dropdown-item{ type: 'button', data: { value: value }, class: (value == current_sort ? 'active' : '') }= label + %input{ type: 'hidden', name: 'sort', value: current_sort, class: 'sort-value-input' } - - elsif filter_type == :radiobutton - %label.radio.inline.margin_left_40 +- elsif filter_type == :radiobutton + .search-section + %label.radio.inline = radio_button_tag('find_by', 'name', params[:find_by] != 'email') = t('.by_name') %label.radio.inline = radio_button_tag('find_by', 'email', params[:find_by] == 'email') = t('.by_email') - - elsif sort_context.present? - #sort_by - %label= t('.sort_by') - = select_tag :sort, options_for_select(SORT_OPTIONS[sort_context][:options], - params[:sort] || SORT_OPTIONS[sort_context][:default]), - class: 'chzn-select', onchange: 'this.form.submit()' +- elsif sort_context.present? + .sort-section + %label= t('.sort_by') + :ruby + current_sort = params[:sort] || SORT_OPTIONS[sort_context][:default] + selected_label = SORT_OPTIONS[sort_context][:options].find { |opt| opt[1] == current_sort }&.first || SORT_OPTIONS[sort_context][:options].first.first + .custom-sort-dropdown + %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ data: { value: current_sort } }= selected_label + %i.fa.fa-chevron-down + .dropdown-menu.sort-dropdown-menu + - SORT_OPTIONS[sort_context][:options].each do |label, value| + %button.sort-dropdown-item{ type: 'button', data: { value: value }, class: (value == current_sort ? 'active' : '') }= label + %input{ type: 'hidden', name: 'sort', value: current_sort, class: 'sort-value-input' } From e367890b217e8da96251fb4d7a4ee3ec60f5a486 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 10/53] OTWO-7546 Added layout for home page --- app/assets/javascripts/dropdown_handler.js | 71 + app/assets/javascripts/mobile_menu.js | 86 + app/assets/javascripts/project_swimlanes.js | 30 + app/assets/javascripts/rotating_stats.js | 29 + app/assets/javascripts/theme_toggle.js | 75 + app/assets/stylesheets/base.sass | 11 +- app/assets/stylesheets/buttons.sass | 45 +- app/assets/stylesheets/dark_theme.sass | 150 ++ app/assets/stylesheets/footer.sass | 213 ++- app/assets/stylesheets/home.sass | 1655 +++++++++++++++-- app/assets/stylesheets/home_search.sass | 145 ++ app/assets/stylesheets/oh-styles.sass | 37 +- app/assets/stylesheets/page.sass | 893 +++++++-- app/assets/stylesheets/search-dingus.sass | 83 +- .../home/_compact_project_card.html.haml | 37 + app/views/home/_join_now_home.html.haml | 44 +- app/views/home/_top_contributors.html.haml | 40 + app/views/home/_top_lists.html.haml | 141 +- app/views/home/_user_journeys.html.haml | 272 +++ app/views/home/_whats_new.html.haml | 11 +- app/views/home/index.html.haml | 112 +- app/views/layouts/partials/_footer.html.haml | 81 +- app/views/layouts/partials/_mast.html.haml | 131 +- .../search_dingus/_search_bar.html.haml | 10 +- config/initializers/assets.rb | 3 +- config/locales/home.en.yml | 79 +- test/controllers/home_controller_test.rb | 8 +- test/controllers/licenses_controller_test.rb | 2 +- .../organizations_controller_test.rb | 2 +- test/controllers/projects_controller_test.rb | 2 +- 30 files changed, 3913 insertions(+), 585 deletions(-) create mode 100644 app/assets/javascripts/dropdown_handler.js create mode 100644 app/assets/javascripts/mobile_menu.js create mode 100644 app/assets/javascripts/project_swimlanes.js create mode 100644 app/assets/javascripts/rotating_stats.js create mode 100644 app/assets/javascripts/theme_toggle.js create mode 100644 app/assets/stylesheets/dark_theme.sass create mode 100644 app/assets/stylesheets/home_search.sass create mode 100644 app/views/home/_compact_project_card.html.haml create mode 100644 app/views/home/_top_contributors.html.haml create mode 100644 app/views/home/_user_journeys.html.haml diff --git a/app/assets/javascripts/dropdown_handler.js b/app/assets/javascripts/dropdown_handler.js new file mode 100644 index 000000000..f9aea4967 --- /dev/null +++ b/app/assets/javascripts/dropdown_handler.js @@ -0,0 +1,71 @@ +// Dropdown menu handler for logged user menu +(function($) { + 'use strict'; + + function initDropdown() { + var $dropdown = $('#logged_user_menu'); + + if ($dropdown.length === 0) { + return; // Dropdown not found (user not logged in) + } + + var $toggle = $dropdown.find('.dropdown-toggle'); + var $menu = $dropdown.find('.dropdown-menu'); + + // Remove any existing event handlers + $toggle.off('click.userDropdown'); + $(document).off('click.userDropdown keydown.userDropdown'); + $menu.find('a').off('click.userDropdown'); + + // Toggle dropdown on click + $toggle.on('click.userDropdown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var wasOpen = $dropdown.hasClass('open'); + + // Close all dropdowns first + $('.dropdown').removeClass('open'); + + // Toggle this dropdown + if (!wasOpen) { + $dropdown.addClass('open'); + } + + return false; + }); + + // Close dropdown when clicking outside + $(document).on('click.userDropdown', function(e) { + var $target = $(e.target); + if (!$dropdown.is($target) && $dropdown.has($target).length === 0) { + $dropdown.removeClass('open'); + } + }); + + // Close dropdown when clicking a menu item + $menu.find('a').on('click.userDropdown', function() { + setTimeout(function() { + $dropdown.removeClass('open'); + }, 150); + }); + + // Close dropdown on ESC key + $(document).on('keydown.userDropdown', function(e) { + if (e.keyCode === 27) { + $dropdown.removeClass('open'); + } + }); + } + + // Initialize when DOM is ready + $(document).ready(function() { + initDropdown(); + }); + + // Re-initialize on Turbolinks page change + $(document).on('page:change', function() { + initDropdown(); + }); + +})(jQuery); diff --git a/app/assets/javascripts/mobile_menu.js b/app/assets/javascripts/mobile_menu.js new file mode 100644 index 000000000..d57090864 --- /dev/null +++ b/app/assets/javascripts/mobile_menu.js @@ -0,0 +1,86 @@ +// Mobile Menu Toggle Functionality +var MobileMenu = { + init: function() { + console.log('MobileMenu: Initializing...'); + this.bindEvents(); + }, + + toggleMenu: function() { + console.log('MobileMenu: Toggle clicked!'); + var mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenu) { + if (mobileMenu.classList.contains('show')) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } else { + mobileMenu.classList.add('show'); + console.log('MobileMenu: Menu opened'); + } + } else { + console.log('MobileMenu: WARNING - Mobile menu element not found!'); + } + }, + + closeMenu: function() { + var mobileMenu = document.getElementById('mobile-menu'); + if (mobileMenu) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } + }, + + bindEvents: function() { + var self = this; + var toggleBtn = document.getElementById('mobile-menu-toggle'); + + console.log('MobileMenu: Toggle button found?', !!toggleBtn); + + if (toggleBtn) { + toggleBtn.onclick = function(e) { + e.preventDefault(); + self.toggleMenu(); + return false; + }; + console.log('MobileMenu: Click handler attached'); + } else { + console.log('MobileMenu: WARNING - Mobile menu toggle button not found!'); + } + + // Close menu when clicking on a link + var mobileMenuLinks = document.querySelectorAll('.mobile-menu-items a, .mobile-menu-user a, .mobile-menu-signin a'); + console.log('MobileMenu: Found', mobileMenuLinks.length, 'menu links'); + + for (var i = 0; i < mobileMenuLinks.length; i++) { + mobileMenuLinks[i].onclick = function() { + self.closeMenu(); + }; + } + + // Close menu when window is resized to desktop size + window.addEventListener('resize', function() { + if (window.innerWidth >= 1024) { + self.closeMenu(); + } + }); + } +}; + +// Initialize on DOM ready +if (typeof $ !== 'undefined') { + $(document).on('page:change', function() { + console.log('MobileMenu: page:change event fired'); + MobileMenu.init(); + }); + + $(document).ready(function() { + console.log('MobileMenu: document.ready event fired'); + MobileMenu.init(); + }); +} else { + // Fallback if jQuery is not available + document.addEventListener('DOMContentLoaded', function() { + console.log('MobileMenu: DOMContentLoaded event fired'); + MobileMenu.init(); + }); +} diff --git a/app/assets/javascripts/project_swimlanes.js b/app/assets/javascripts/project_swimlanes.js new file mode 100644 index 000000000..270f4e96f --- /dev/null +++ b/app/assets/javascripts/project_swimlanes.js @@ -0,0 +1,30 @@ +// Project Swimlanes - Show More/Less functionality +document.addEventListener('DOMContentLoaded', function() { + var buttons = document.querySelectorAll('.show_more_btn'); + + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener('click', function() { + var swimlane = this.getAttribute('data-swimlane'); + var swimlaneContent = document.querySelector('.swimlane_content[data-swimlane="' + swimlane + '"]'); + var hiddenCards = swimlaneContent.querySelectorAll('.hidden_card'); + var isExpanded = this.textContent.trim() === 'Show Less'; + + // Toggle visibility of hidden cards + for (var j = 0; j < hiddenCards.length; j++) { + if (isExpanded) { + hiddenCards[j].style.display = 'none'; + } else { + hiddenCards[j].style.display = 'block'; + } + console.log('Card', j, 'classes:', hiddenCards[j].className, 'display:', window.getComputedStyle(hiddenCards[j]).display); + } + + // Toggle button text + if (isExpanded) { + this.textContent = 'Show More'; + } else { + this.textContent = 'Show Less'; + } + }); + } +}); diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js new file mode 100644 index 000000000..39a368b7b --- /dev/null +++ b/app/assets/javascripts/rotating_stats.js @@ -0,0 +1,29 @@ +// Rotating Stats Animation for Homepage +document.addEventListener('DOMContentLoaded', function() { + var globalStats = document.getElementById('global_statistics'); + + if (!globalStats) return; + + var statElements = globalStats.querySelectorAll('p'); + + if (statElements.length === 0) return; + + var currentIndex = 0; + + // Show first stat initially + statElements[0].classList.remove('hide'); + + // Rotate stats every 2 seconds + setInterval(function() { + // Hide current stat + statElements[currentIndex].classList.add('hide'); + + // Move to next stat + currentIndex = (currentIndex + 1) % statElements.length; + + // Show next stat after a brief delay + setTimeout(function() { + statElements[currentIndex].classList.remove('hide'); + }, 300); + }, 2000); +}); diff --git a/app/assets/javascripts/theme_toggle.js b/app/assets/javascripts/theme_toggle.js new file mode 100644 index 000000000..44c98daaf --- /dev/null +++ b/app/assets/javascripts/theme_toggle.js @@ -0,0 +1,75 @@ +// Theme Toggle Functionality +var ThemeToggle = { + init: function() { + console.log('ThemeToggle: Initializing...'); + var savedTheme = this.getSavedTheme(); + console.log('ThemeToggle: Saved theme is', savedTheme); + this.applyTheme(savedTheme); + this.bindEvents(); + }, + + getSavedTheme: function() { + try { + return localStorage.getItem('theme') || 'light'; + } catch (e) { + return 'light'; + } + }, + + applyTheme: function(theme) { + var html = document.documentElement; + var moonIcon = document.getElementById('theme-icon-moon'); + var sunIcon = document.getElementById('theme-icon-sun'); + + if (theme === 'dark') { + html.classList.add('dark'); + if (moonIcon) moonIcon.classList.add('hidden'); + if (sunIcon) sunIcon.classList.remove('hidden'); + } else { + html.classList.remove('dark'); + if (moonIcon) moonIcon.classList.remove('hidden'); + if (sunIcon) sunIcon.classList.add('hidden'); + } + + try { + localStorage.setItem('theme', theme); + } catch (e) { + console.log('Could not save theme preference'); + } + }, + + toggleTheme: function() { + var currentTheme = this.getSavedTheme(); + var newTheme = currentTheme === 'light' ? 'dark' : 'light'; + this.applyTheme(newTheme); + }, + + bindEvents: function() { + var self = this; + var themeToggleBtn = document.getElementById('theme-toggle'); + + console.log('ThemeToggle: Theme toggle button found?', !!themeToggleBtn); + + if (themeToggleBtn) { + themeToggleBtn.onclick = function(e) { + e.preventDefault(); + console.log('ThemeToggle: Toggle clicked!'); + self.toggleTheme(); + return false; + }; + console.log('ThemeToggle: Click handler attached'); + } else { + console.log('ThemeToggle: WARNING - Theme toggle button not found!'); + } + } +}; + +// Initialize on DOM ready +$(document).on('page:change', function() { + ThemeToggle.init(); +}); + +// Also initialize immediately if DOM is already loaded +$(document).ready(function() { + ThemeToggle.init(); +}); diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 2e50269cc..43c4cbed2 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -2,13 +2,13 @@ // base mixins =sans_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =serif_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =arial_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =lucida_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =small_font font-size: .91667em =tiny_font @@ -21,10 +21,11 @@ #commits_summary_page, #mini_account_row [class^="icon-"], [class*=" icon-"] - font-family: Roboto !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important body text-rendering: optimizeLegibility + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" a @include site-link-color diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 276577616..8baec979b 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -90,14 +90,49 @@ button.btn:active border-color: #005481 .btn-refresh - @include secondary-button-colors + color: white !important + background-color: #5A2A82 !important + border-color: #5A2A82 !important + border-radius: 0 12px 12px 0 + margin-left: -1px + padding: 10px 18px + min-width: 48px + transition: all 0.15s ease-in-out + &:hover + background-color: rgba(90, 42, 130, 0.9) !important + border-color: #5A2A82 !important + transform: none + &:active + background-color: #4A1A72 !important + border-color: #4A1A72 !important &.no-hover:hover - background-color: #0082C6 !important + background-color: #5A2A82 !important &.active, &.no-border.active - background-color: #66B7E8 !important - border-color: #66B7E8 + background-color: #4A1A72 !important + border-color: #4A1A72 &.disabled, &[disabled] - background-color: #66CEF6 !important + background-color: #8A5AAA !important + border-color: #8A5AAA !important + opacity: 0.6 + cursor: not-allowed + i + font-size: 16px + line-height: 1 + + // Dark mode support + .dark & + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: #ffb91a !important + &:active + background-color: #e6a617 !important + border-color: #e6a617 !important + &.active, &.no-border.active + background-color: #e6a617 !important + border-color: #e6a617 .btn-success @include join-now-button-colors diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass new file mode 100644 index 000000000..9a7f3d844 --- /dev/null +++ b/app/assets/stylesheets/dark_theme.sass @@ -0,0 +1,150 @@ +// Dark Theme Styles +// Applied when .dark class is added to html element + +html.dark + // Body and background colors - matches home page sections + body + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Page container + #page, .container#page + background-color: #1D0631 !important + + // Header stays with purple gradient (no change needed) + // Footer stays with purple gradient (no change needed) + + // Content areas + #page-contents, #page_contents + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Cards and wells + .well + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Tables + table + background-color: #1D0631 + color: #e2e8f0 + td, th + border-color: #4b5563 !important + color: #e2e8f0 !important + + // Links + a + color: #60a5fa !important + &:hover + color: #93c5fd !important + + // Buttons (keep primary yellow button, update others) + .btn + &:not(.btn-primary):not(.btn-success) + background-color: #334155 !important + color: #e2e8f0 !important + &:hover + background-color: #475569 !important + + // Forms + input, textarea, select + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + &::placeholder + color: #94a3b8 !important + + // Billboard/Showcase + .billboard + background-color: #1D0631 !important + color: #e2e8f0 !important + + .showcase + background-color: #1D0631 !important + + // Project container + #project_container + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Headings + h1, h2, h3, h4, h5, h6 + color: #f1f5f9 !important + + // Alerts + .alert + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Dropdown menus + .dropdown-menu + background-color: #1D0631 !important + border-color: #4b5563 !important + li a + color: #e2e8f0 !important + &:hover + background-color: rgba(90, 42, 130, 0.5) !important + + // Home page sections + .top_ten, .top_ten.middle, .top_ten.last + background-color: #1D0631 !important + color: #e2e8f0 !important + + .home_page_row .col-md-4 + background-color: #1D0631 !important + + // Navigation menu - keep white text on purple gradient + .navbar, #navbar-inner + .new_main_menu li a + color: white !important + + // Search inputs + .for_search_all_code + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Badges + .badge + background-color: rgba(90, 42, 130, 0.5) !important + color: #e2e8f0 !important + + // Panels + .panel + background-color: #1D0631 !important + border-color: #4b5563 !important + .panel-heading + background-color: rgba(90, 42, 130, 0.5) !important + color: #e2e8f0 !important + .panel-body + background-color: #1D0631 !important + color: #e2e8f0 !important + + // Code blocks + pre, code + background-color: #1D0631 !important + color: #e2e8f0 !important + border-color: #4b5563 !important + + // Borders + .mezzo + border-top-color: #4b5563 !important + + .right_border + border-right-color: #4b5563 !important + + // Text colors + .signature_color + color: #60a5fa !important + + // Keep error colors visible + .error + color: #ef4444 !important + + .bad + color: #f87171 !important + + .good + color: #4ade80 !important diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index dccca4602..61f45a71a 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -1,81 +1,154 @@ footer - @include footer-colors + // Figma design: purple gradient background + position: relative + overflow: hidden + background: linear-gradient(to right, #1D0631, #5A2A82) !important + color: white !important + margin: 0 + padding: 0 + width: 100% + max-width: 100% + // Matches Figma: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 + .footer-container + position: relative + max-width: 1280px + margin: 0 auto + padding: 24px 16px + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 48px 32px - margin: 20px 0 0 0 - min-height: 250px - padding-top: 10px - width: 960px - - @media (min-width: 320px) and (max-width: 480px) - width: auto - min-height: 194px !important - padding-top: 0px !important - margin: 0px 0 0 0 !important - .footer-left - margin-left: 5px !important - margin-top: 20px !important - min-height: 150px !important - .footer-mid - line-height: 15px - margin-left: 30px !important - font-size: 8px !important - width: 32% !important - .footer-right - font-size: 8px !important - padding-right: 2px !important - width: 31% !important - .footer-bottom - margin-left: 0px !important - margin-top: 0px !important - @media (min-width: 540px) and (max-width: 1024px) - width: auto + // Matches Figma: grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8 md:gap-12 + .footer-grid + display: grid + grid-template-columns: 1fr + gap: 24px + margin-bottom: 24px + @media (min-width: 640px) + gap: 32px + margin-bottom: 32px + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 48px + margin-bottom: 48px + // Matches Figma: h-8 sm:h-10 .footer-left - float: left - margin-top: 20px - margin-left: 30px - width: 15% + display: flex + align-items: flex-start + justify-content: center + @media (min-width: 768px) + justify-content: flex-start a .logo_img - width: 100% - height: 100% + width: auto + height: 32px + object-fit: contain + @media (min-width: 640px) + height: 40px - .footer-mid - float: left - margin-top: 20px - margin-left: 120px - margin-right: 40px - width: 20% - font-size: 12px - a - @include footer-colors - padding: 4px 4px - - .footer-right - float: left - margin-top: 20px - margin-bottom: 20px - width: 30% - font-size: 12px - padding-right: 60px - text-align: left + // Matches Figma: text-xs sm:text-sm for headers and links + .footer-mid, .footer-right + h3 + font-size: 12px + font-weight: 600 + color: white !important + margin-bottom: 8px + text-transform: uppercase + letter-spacing: 0.05em + @media (min-width: 640px) + font-size: 14px + margin-bottom: 12px + @media (min-width: 768px) + margin-bottom: 16px + ul + list-style: none + margin: 0 + padding: 0 + li + margin-bottom: 6px + @media (min-width: 640px) + margin-bottom: 8px + @media (min-width: 768px) + margin-bottom: 10px + p + margin: 0 0 6px 0 + @media (min-width: 640px) + margin: 0 0 8px 0 + @media (min-width: 768px) + margin: 0 0 10px 0 a - @include footer-colors - padding: 4px 6px + color: #d1d5db !important + text-decoration: none + font-size: 12px + font-weight: 400 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px + &:hover + color: white !important + // Matches Figma: py-3 sm:py-4 md:py-6 .footer-bottom - @include footer-copyright-colors - - float: left - margin-top: 10px - margin-left: 30px - margin-bottom: 2em - bottom: 0 - a - @include footer-copyright-colors + border-top: 1px solid rgba(255, 255, 255, 0.1) + padding: 12px 0 + @media (min-width: 640px) + padding: 16px 0 + @media (min-width: 768px) + padding: 24px 0 + .footer-bottom-content + display: flex + flex-direction: column + align-items: center + justify-content: space-between + gap: 12px + @media (min-width: 640px) + flex-direction: row + // Matches Figma: text-[10px] sm:text-xs + .copyright + color: #9ca3af !important + font-size: 10px + order: 2 + @media (min-width: 640px) + font-size: 12px + order: 1 + .follow-us + display: flex + align-items: center + gap: 8px + order: 1 + @media (min-width: 640px) + order: 2 + // Matches Figma: text-xs sm:text-sm + span + color: #d1d5db !important + font-size: 12px + font-weight: 500 + @media (min-width: 640px) + font-size: 14px + // Matches Figma: w-8 h-8 + a + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + transition: background-color 0.3s ease + &:hover + background-color: rgba(255, 255, 255, 0.2) + i + color: #d1d5db + font-size: 16px + transition: color 0.3s ease + &:hover i + color: white footer.fluid_footer width: auto + background: linear-gradient(to right, #1D0631, #5A2A82) !important .footer-left width: 12% ul @@ -83,16 +156,22 @@ footer.fluid_footer a padding: 1rem 2rem font-weight: bold + color: rgba(209, 213, 219, 1) !important + &:hover + color: white !important .footer-mid width: 25% text-align: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-right width: 40% float: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-bottom float: left margin-botton: 2em !important + color: rgba(156, 163, 175, 1) !important diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4c18ddd8c..4978e69e3 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,159 +1,138 @@ -.showcase - padding-top: 20px - padding-bottom: 30px - width: 980px - margin-left: -15px - padding-left: 5px - padding-right: 5px -@media only all and (min-width: 320px) and (max-width: 480px) - .showcase - width: auto - margin-left: 0px - .billboard - height: 185px !important - -.billboard - @include billboard-background-color - color: white - height: 160px - padding-top: 30px - padding-bottom: 30px - text-align: center +// ======================================== +// Hero Section - Figma Design System +// ======================================== +// Main container with consistent 1280px max-width throughout +// Isolated from global body font-size: 1.3rem override + +.hero_section + padding: 0 16px 40px + background: #fff !important + transition: background 0.3s ease + box-sizing: border-box + @media (min-width: 640px) + padding: 0 24px 40px + @media (min-width: 1024px) + padding: 0 32px 40px + // Dark mode support + html.dark & + background: #1D0631 !important + // Ensure all child elements use border-box + *, *::before, *::after + box-sizing: border-box + +// Hero Container - Controls all child widths consistently (1280px) +.hero_container + max-width: 1280px + margin: 0 auto width: 100% - z-index: 2 - .billboard_search - // margin-top: 80px - .discover - padding: 0 - .discover_msg - font-size: 2.1em !important - font-weight: 400!important - margin-top: -10px + box-sizing: border-box + +// Hero Header - Figma Specs +.hero_header + text-align: center + margin-bottom: 24px + padding: 0 + @media (min-width: 768px) margin-bottom: 24px - h1 - line-height: 1.2em !important -@media only all and (min-width: 320px) and (max-width: 480px) - .billboard - .discover_msg - font-size: 13px !important - #global_statistics - p - font-size: 10px !important -@media (min-width: 540px) and (max-width: 1024px) - .showcase - width: auto - margin-left: 0px -.global_stats - margin-top: 25px -#icon_text - font-size: 1.6em - color: #646E81 - margin-left: -27px - cursor: pointer + +.hero_title + font-size: 1.875rem + font-weight: 700 + color: #5A2A82 + margin-bottom: 8px + line-height: 2.25rem + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 2.25rem + line-height: 2.5rem + @media (min-width: 768px) + font-size: 48px + line-height: 1 + // Dark mode support + html.dark & + color: #ffffff + +.hero_subtitle + font-size: 0.875rem + color: #6b7280 + margin: 0 auto + max-width: 672px + line-height: 1.25rem + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 16px + line-height: 1.5rem + // Dark mode support + html.dark & + color: #9ca3af + +// Search Container - Narrower for focused search UX (768px) +.search_container + max-width: 768px + margin: 0 auto 20px + width: 100% + box-sizing: border-box + +// Rotating Stats - Figma Animation +.rotating_stats + text-align: center + margin-top: 12px + margin-bottom: 24px + height: 20px #global_statistics - padding-top: 1px - p - margin-top: -20px - text-align: center - font-size: 14px -.join_now - @include join-now-header-colors - padding-bottom: 5px - padding-left: 12px -.join_now_home - padding-top: 10px - font-size: 1.4em p - font-size: 14px !important - .join_now_color - @include join-now-section-colors - li - font-size: 0.5em !important - height: 30px - width: 30px -.home_page_row - .whats_new - margin-right: -27px !important - .col-md-8 - h2 - font-weight: 500 !important - margin-right: -40px !important - margin-left: 30px !important - text-indent: -10px !important - .col-md-4 - @include site-well-color - padding-bottom: 20px - h2 - font-weight: 500 !important - margin-left: 5px !important - text-indent: -10px !important - -@media only all and (min-width: 320px) and (max-width: 480px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important - .for_search_all_code - width: auto !important - .join_now_home - padding-top: 5px font-size: 12px - li - height: 10px - width: 10px -@media (min-width: 540px) and (max-width: 1024px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important -.btn.btn_join_now - @include join-now-button-colors - font-family: 'Roboto' !important - font-size: 22px !important - font-weight: bold !important - margin-left: 60px !important - width: 161px !important -::-webkit-input-placeholder - @include site-placeholder-color -\:-moz-placeholder - @include site-placeholder-color - opacity: 1 -::-moz-placeholder - @include site-placeholder-color - opacity: 1 -\:-ms-input-placeholder - @include site-placeholder-color - -input:focus::-webkit-input-placeholder - color: transparent -input:focus::-moz-placeholder - color: transparent -input:focus::-ms-input-placeholder - color: transparent -@media only all and (min-width: 320px) and (max-width: 480px) - .btn.btn_join_now - margin-left: 0px !important - width: 135px !important - font-size: 15px !important + font-weight: 500 + background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) + -webkit-background-clip: text + -webkit-text-fill-color: transparent + background-clip: text + margin: 0 + line-height: 1.5 + transition: opacity 0.3s ease + &.hide + opacity: 0 + display: block + &:not(.hide) + opacity: 1 + display: block + // Dark mode support + html.dark & + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) !important + -webkit-background-clip: text !important + -webkit-text-fill-color: transparent !important + background-clip: text !important +// Old join_now and home_page_row styles removed - using new card-based design +// Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px - margin-left: -14px + margin-left: 0 + padding: 32px 16px + width: 100% + background: #f9fafb !important + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + // Dark mode support + html.dark & + background: #1D0631 !important .top_ten_main padding-left: 40px !important padding-bottom: 6px !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" .top_ten_row padding-left: 9px !important padding-bottom: 15px + transition: all 0.3s ease + border-radius: 8px + margin-bottom: 12px + &:hover + background: rgba(90, 42, 130, 0.05) + transform: translateX(4px) .top_ten_bar line-height: 0.6em width: 30% @@ -162,11 +141,31 @@ input:focus::-ms-input-placeholder .top_ten_label line-height: 1.5em padding-left: 8px - color: #555 + color: #6b7280 + font-size: 12px + font-weight: 500 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + @media (min-width: 768px) + font-size: 14px + // Dark mode support + html.dark & + color: #9ca3af .top_ten h2 - font-weight: 500 !important + font-weight: 600 !important font-size: 16px !important + color: #111827 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode support + html.dark & + color: #ffffff .top_ten.middle @include site-well-color .top_ten.last @@ -174,8 +173,8 @@ input:focus::-ms-input-placeholder margin-right: -25px !important .top_ten_icon img - background-color: #EEE - color: #000 + background-color: #f3f4f6 + color: #111827 font-size: 4px line-height: 32px width: 32px @@ -184,37 +183,46 @@ input:focus::-ms-input-placeholder float: left margin-bottom: 0 margin-top: 0 - margin-right: 2px + margin-right: 8px + border-radius: 8px + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) -webkit-filter:grayscale(1) filter: grayscale(1) - opacity: .75 + opacity: .85 + transition: all 0.3s ease + // Dark mode support + html.dark & + background-color: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + &:hover img + opacity: 1 + transform: scale(1.05) a.top_ten_icon p opacity: .75 background-color: #999 -.for_search_all_code - @include site-placeholder-color - height: 23px !important - // margin-top: -108px - width: 413px +// Old .for_search_all_code removed - using new .search_input instead .top_ten_link a border: 0 -.icon_search - font-size: 14px - font-weight: 300 - margin-top: -37px - margin-left: -71px - text-align: center -.wh_new - @include whats-new-header-colors - width: 100% - padding-bottom: 5px -.common - padding-left: 6px - padding-bottom: 3px - font-size: 1.4em - font-weight: normal - color: #000000 + color: #5A2A82 + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 15px + @media (min-width: 768px) + font-size: 16px + &:hover + color: #1D0631 + text-decoration: none + // Dark mode support + html.dark & + color: #ffffff + &:hover + color: #ffb91a +// Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors .most_active_projects @@ -239,4 +247,1315 @@ button #no-background background-color: white !important +// Feature Cards Section - Figma: 1024px width +.features_section + max-width: 1024px + margin: 0 auto 24px + box-sizing: border-box + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 20px + +.feature_card + text-align: center + margin-bottom: 16px + @media (min-width: 768px) + margin-bottom: 0 + +.feature_icon + width: 40px + height: 40px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin: 0 auto 12px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.feature_icon_yellow + background: linear-gradient(135deg, #facc15 0%, #f97316 100%) + +.feature_icon_blue + background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) + +.feature_icon_green + background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%) + +.feature_title + font-size: 12px !important + font-weight: 600 + color: #111827 + margin-bottom: 4px + line-height: 1.3 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #ffffff + +.feature_description + font-size: 10px !important + color: #6b7280 + line-height: 1.625 + padding: 0 8px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af + +// Quick Stats Bar - Compact and centered (768px) +.quick_stats_bar + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + max-width: 768px + margin: 0 auto + padding: 16px + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) + backdrop-filter: blur(4px) + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.6) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + box-sizing: border-box + @media (min-width: 768px) + display: flex + align-items: center + justify-content: center + gap: 24px + padding: 12px 24px + // Dark mode support - Yellow background with purple text + html.dark & + background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) + border-color: #ffb91a + +.stat_item + text-align: center + +.stat_value + font-size: 16px + font-weight: 700 + color: #5A2A82 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px + @media (min-width: 768px) + font-size: 20px + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_label + font-size: 8px + color: #6b7280 + text-transform: uppercase + font-weight: 500 + letter-spacing: 0.05em + margin-top: 2px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 9px + @media (min-width: 768px) + font-size: 10px + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_divider + display: none + transition: background 0.3s ease + @media (min-width: 768px) + display: block + width: 1px + height: 32px + background: #9ca3af + opacity: 1 + // Dark mode - Purple divider on yellow background + html.dark & + @media (min-width: 768px) + background: #5A2A82 + opacity: 0.3 + +// Content Section +.content_section + padding: 32px 0 + background: white !important + transition: background 0.3s ease + // Dark mode support + html.dark & + background: #1D0631 !important + + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + +// Content Grid - matches Figma design: grid md:grid-cols-[35%_65%] gap-6 +.content_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: 35% 65% + +.contributors_column + width: 100% + +.whats_new_column + width: 100% + display: block + +.whats_new_join_stack + display: flex + flex-direction: column + gap: 24px + +// Top Contributors Card - Matches ContributorJourney.tsx +.top_contributors_card + position: relative + padding: 3px + border-radius: 16px + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 24px + overflow: hidden + transition: box-shadow 0.3s + &:hover + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + html.dark & + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + + > * + background: white + border-radius: 16px + overflow: hidden + html.dark & + background: #1D0631 + +// Card Header +.top_contributors_card .card_header + display: flex + align-items: flex-start + gap: 16px + padding: 24px 24px 16px 24px + background: white + html.dark & + background: #1D0631 + +.top_contributors_card .header_icon + display: flex + align-items: center + justify-content: center + width: 40px + height: 40px + border-radius: 12px + background: rgba(90, 42, 130, 0.1) + flex-shrink: 0 + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + @media (min-width: 640px) + width: 48px + height: 48px + html.dark & + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + + i + color: #5A2A82 + font-size: 20px + @media (min-width: 640px) + font-size: 24px + html.dark & + color: white + +.top_contributors_card .header_content + flex: 1 + +.top_contributors_card .card_title + font-size: 16px + font-weight: 700 + color: #5A2A82 + margin: 0 0 6px 0 + line-height: 1.4 + @media (min-width: 640px) + font-size: 18px + html.dark & + color: white + +.top_contributors_card .card_subtitle + font-size: 10px + color: rgba(90, 42, 130, 0.7) + margin: 0 + line-height: 1.5 + @media (min-width: 640px) + font-size: 12px + html.dark & + color: #9CA3AF + +// Contributors List +.top_contributors_card .contributors_list + padding: 16px + display: flex + flex-direction: column + gap: 8px + +.top_contributors_card .contributor_item + display: flex + align-items: center + gap: 12px + padding: 12px + background: linear-gradient(to right, #f9fafb 0%, #f1f5f9 100%) + border: 1px solid #e5e7eb + border-radius: 12px + transition: all 0.2s + text-decoration: none + cursor: pointer + + &:hover + background: linear-gradient(to right, #f5f3ff 0%, #ede9fe 100%) + border-color: #5A2A82 + + html.dark & + background: linear-gradient(to right, rgba(55, 65, 81, 0.5) 0%, rgba(71, 85, 105, 0.5) 100%) + border-color: #4B5563 + &:hover + background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) + border-color: #5A2A82 + +// Show only top 4 contributors on mobile +.top_contributors_card .contributor_item:nth-child(n+5) + @media (max-width: 767px) + display: none + +// Avatar +.top_contributors_card .contributor_avatar + flex-shrink: 0 + width: 40px + height: 40px + +.top_contributors_card .avatar_img, +.top_contributors_card .avatar_placeholder + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + display: block + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + border: 2px solid #9CA3AF + +// Contributor Info +.top_contributors_card .contributor_info + flex: 1 + min-width: 0 + display: flex + flex-direction: column + gap: 2px + +.top_contributors_card .contributor_name_row + display: flex + align-items: center + gap: 8px + margin-bottom: 2px + +.top_contributors_card .contributor_name + font-size: 14px + font-weight: 600 + color: #5A2A82 + line-height: 1.3 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + html.dark & + color: white + +.top_contributors_card .contributor_rank + font-size: 9px + font-weight: 400 + color: rgba(90, 42, 130, 0.6) + text-transform: uppercase + letter-spacing: 0.05em + white-space: nowrap + flex-shrink: 0 + html.dark & + color: #9CA3AF + +.top_contributors_card .contributor_stats + display: flex + align-items: center + gap: 12px + font-size: 10px + color: rgba(90, 42, 130, 0.7) + + html.dark & + color: #9CA3AF + + span + display: flex + align-items: center + gap: 4px + + i + flex-shrink: 0 + display: inline-flex + align-items: center + justify-content: center + font-size: 9px + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_commits, +.top_contributors_card .stat_projects + font-weight: 400 + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_separator + display: none + +// Arrow +.top_contributors_card .contributor_arrow + flex-shrink: 0 + color: rgba(90, 42, 130, 0.4) + display: flex + align-items: center + transition: color 0.2s + + i + font-size: 16px + + html.dark & + color: rgba(255, 255, 255, 0.3) + +.top_contributors_card .contributor_item:hover .contributor_arrow + color: #5A2A82 + html.dark & + color: white + +// View All Footer +.top_contributors_card .view_all_footer + padding: 16px + +.top_contributors_card .view_all_link + display: flex + width: 100% + align-items: center + justify-content: center + gap: 8px + padding: 12px + font-size: 14px + font-weight: 600 + color: #5A2A82 + text-decoration: none + border-top: 1px solid rgba(90, 42, 130, 0.2) + transition: color 0.2s + cursor: pointer + + span + border-bottom: 1px solid #5A2A82 + + &:hover + color: #1D0631 + span + border-bottom-color: #1D0631 + + i + font-size: 16px + + html.dark & + color: white !important + border-top-color: #ffb91a + border-top-width: 2px + border-radius: 12px + margin-top: 8px + span + border-bottom-color: white + &:hover + color: white !important + +// What's New Card - Figma Design +.whats_new_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + // Dark mode support + html.dark & + background: #1D0631 + +.card_header_section + padding: 16px 16px 12px 16px !important + background: white + @media (min-width: 640px) + padding: 24px 24px 16px 24px !important + // Dark mode support + html.dark & + background: #1D0631 + +.whats_new_title + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important + +.whats_new_subtitle + font-size: 12px !important + color: rgba(90, 42, 130, 0.7) !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.featured_image_section + padding: 0 16px 16px !important + display: block + @media (min-width: 640px) + padding: 0 24px 24px !important + +.featured_image_link + display: block + position: relative + border-radius: 12px + overflow: hidden + transition: transform 0.3s + &:hover + transform: scale(1.02) + .featured_image + transform: scale(1.05) + +.featured_image + width: 100% + height: auto + display: block + max-width: 100% + transition: transform 0.3s + +// Join Now Card - Figma Design (p-6 wrapper) +.join_now_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + padding: 24px !important + // Dark mode support + html.dark & + background: #1D0631 + +.join_header + display: flex + align-items: center + gap: 12px !important + margin-bottom: 16px !important + padding: 0 !important + +.join_icon + width: 40px + height: 40px + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.join_header_text + flex: 1 + +.join_title + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important + +.join_subtitle + font-size: 12px !important + color: #6b7280 !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.benefits_list + padding: 0 !important + margin-bottom: 24px !important + // Figma: space-y-3 (12px gap) + > * + * + margin-top: 12px !important + +.benefit_item + display: flex + align-items: flex-start + gap: 12px +.benefit_icon + width: 28px + height: 28px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + margin-top: 2px + @media (min-width: 640px) + width: 32px + height: 32px + +.benefit_icon_award + background: rgba(90, 42, 130, 0.1) + i + font-size: 14px + color: #5A2A82 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #5A2A82 + +.benefit_icon_trending + background: rgba(29, 6, 49, 0.1) + i + font-size: 14px + color: #1D0631 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #1D0631 + +.benefit_content + flex: 1 + +.benefit_title + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: white !important + +.benefit_description + font-size: 10px !important + color: #6b7280 !important + line-height: 1.625 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af !important + +.join_cta + padding: 0 !important + +.btn_join_cta + width: 100% + padding: 12px 24px !important + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) !important + color: white !important + border-radius: 8px !important + font-weight: 600 !important + transition: all 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + display: flex + align-items: center + justify-content: center + gap: 8px + font-size: 14px !important + text-decoration: none !important + border: none + cursor: pointer + @media (min-width: 640px) + font-size: 16px !important + &:hover + background: linear-gradient(90deg, rgba(29, 6, 49, 0.9) 0%, rgba(90, 42, 130, 0.9) 100%) !important + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important + color: white !important + text-decoration: none !important + i + font-size: 14px + @media (min-width: 640px) + font-size: 16px + +// User Journeys Section +.user_journeys_section + background: #1D0631 + padding: 64px 0 + @media (max-width: 768px) + padding: 48px 0 + + // Matches Figma: max-w-7xl mx-auto + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + +.journeys_header + text-align: center + margin-bottom: 48px + +// Matches Figma: text-2xl sm:text-3xl +.journeys_title + font-size: 24px + font-weight: 700 !important + color: white + margin-bottom: 12px + @media (min-width: 640px) + font-size: 30px + +// Matches Figma: text-base sm:text-lg max-w-2xl +.journeys_subtitle + font-size: 16px + color: #d1d5db + max-width: 672px + margin: 0 auto + @media (min-width: 640px) + font-size: 18px + +// Mobile View - Collapsible +// Matches Figma: lg:hidden (shows below 768px, then desktop grid takes over) +.journeys_mobile + display: block + @media (min-width: 768px) + display: none + +.journey_card + position: relative + padding: 3px + border-radius: 12px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 16px + transition: all 0.3s + +// Matches Figma: rounded-xl p-4 +.journey_card_header + width: 100% + display: flex + align-items: center + gap: 16px + padding: 16px + text-align: left + transition: background 0.3s + background: #1D0631 + border: none + cursor: pointer + border-radius: 12px + &:hover + background: rgba(90, 42, 130, 0.2) + +.journey_header_icon + width: 40px + height: 40px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 20px + color: white + +.journey_header_title + flex: 1 + font-size: 14px + font-weight: 600 !important + color: white + overflow-wrap: break-word + +.journey_toggle_icon + flex-shrink: 0 + i + font-size: 20px + color: white + +// Matches Figma: px-4 pb-4 +.journey_card_content + display: none + padding: 0 16px 16px + background: #1D0631 + border-radius: 0 0 12px 12px + +.journey_card.expanded .journey_card_content + display: block + animation: slideDown 0.3s ease-out + +@keyframes slideDown + from + opacity: 0 + transform: translateY(-10px) + to + opacity: 1 + transform: translateY(0) + +.journey_description + font-size: 14px + color: #d1d5db + line-height: 1.5 + margin-bottom: 16px + overflow-wrap: break-word + +.journey_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +.steps_title + font-size: 12px + font-weight: 600 + color: #e5e7eb + text-transform: uppercase + letter-spacing: 0.05em + margin-bottom: 8px + +.steps_list + list-style: none + padding: 0 + margin: 0 + li + display: flex + align-items: flex-start + gap: 8px + font-size: 14px + color: #d1d5db + margin-bottom: 6px + &:last-child + margin-bottom: 0 + +.step_bullet + color: #ffb91a + font-weight: 700 + +// Desktop View - Grid +// Matches Figma: hidden lg:grid md:grid-cols-2 lg:grid-cols-3 gap-6 +.journeys_desktop + display: none + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 24px + min-width: 0 + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + +// Matches Figma: rounded-xl p-6 +.journey_grid_card + border-radius: 12px + padding: 24px + border: 1px solid rgba(90, 42, 130, 0.4) + background: #1D0631 + transition: all 0.3s + min-width: 0 + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) + border-color: #ffb91a + +.grid_card_icon + width: 56px + height: 56px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin-bottom: 16px + transition: transform 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 28px + color: white + +.journey_grid_card:hover .grid_card_icon + transform: scale(1.1) + +.grid_card_title + font-size: 17px + font-weight: 700 !important + color: white + margin-bottom: 8px + line-height: 1.2 + overflow-wrap: break-word + +.grid_card_description + font-size: 14px + color: #d1d5db + margin-bottom: 16px + line-height: 1.5 + overflow-wrap: break-word + +.grid_card_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +// Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 +.project_swimlanes_section + padding: 32px 16px + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + html.dark & + background: #1D0631 + +// Matches Figma: max-w-7xl mx-auto +.swimlanes_container + max-width: 1280px + margin: 0 auto + +// Matches Figma: grid lg:grid-cols-3 md:grid-cols-2 gap-6 +.swimlanes_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + +.swimlane_column + display: flex + flex-direction: column + +// Matches Figma: mb-4 +.swimlane_header + margin-bottom: 16px + +// Matches Figma: text-base sm:text-lg md:text-xl font-bold +.swimlane_title + font-size: 16px + font-weight: 700 !important + color: #111827 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px + @media (min-width: 768px) + font-size: 20px + html.dark & + color: #ffffff + +// Matches Figma: space-y-3 +.swimlane_content + display: flex + flex-direction: column + gap: 12px + +// Compact Project Card Styles +.compact_project_card + position: relative + padding: 3px + border-radius: 8px + overflow: hidden + transition: all 0.3s ease + background: transparent + cursor: pointer + &:hover + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + .compact_card_inner + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + +.compact_project_card .compact_card_inner + background: white + border-radius: 8px + padding: 12px + transition: all 0.3s ease + height: 100% + border: 1px solid #e5e7eb + @media (min-width: 640px) + padding: 16px + html.dark & + background: #1D0631 + border-color: #4b5563 + +.compact_project_card .compact_card_header + display: flex + align-items: flex-start + gap: 8px + margin-bottom: 8px + @media (min-width: 640px) + gap: 12px + +.compact_project_card .project_logo + flex-shrink: 0 + width: 32px + height: 32px + @media (min-width: 640px) + width: 40px + height: 40px + img, p + width: 100% !important + height: 100% !important + border-radius: 8px !important + object-fit: cover + border: 1px solid #e5e7eb !important + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important + html.dark & + border-color: #4b5563 !important + p + display: flex !important + align-items: center !important + justify-content: center !important + margin: 0 !important + font-size: 16px !important + font-weight: 600 !important + +.compact_project_card .project-icon-placeholder + width: 100% + height: 100% + display: flex + align-items: center + justify-content: center + background: #f3f4f6 + border-radius: 8px + font-size: 16px + font-weight: 600 + color: #6b7280 + border: 1px solid #e5e7eb + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: #9ca3af + +.compact_project_card .account-avatar + width: 100% + height: 100% + border-radius: 8px + object-fit: cover + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + html.dark & + border-color: #4b5563 + +.compact_project_card .project_info + flex: 1 + min-width: 0 + +.compact_project_card .project_name_row + display: flex + align-items: center + gap: 8px + +.compact_project_card .project_name + color: #111827 + font-size: 14px + font-weight: 600 + text-decoration: none + transition: color 0.3s ease + display: inline-block + line-height: 1.4 + @media (min-width: 640px) + font-size: 16px + &:hover + color: #5A2A82 + html.dark & + color: #ffffff !important + &:hover + color: #ffb91a !important + span + display: inline + +.compact_project_card .trending_icon + flex-shrink: 0 + i + color: #ff6b35 + font-size: 12px + @media (min-width: 640px) + font-size: 14px + +.compact_project_card .compact_card_description + margin-bottom: 12px + p + font-size: 12px + color: #6b7280 + line-height: 1.5 + margin: 0 + overflow: hidden + text-overflow: ellipsis + display: -webkit-box + -webkit-line-clamp: 2 + -webkit-box-orient: vertical + @media (min-width: 640px) + font-size: 14px + html.dark & + color: #9ca3af + +.compact_project_card .compact_card_footer + display: flex + align-items: center + justify-content: space-between + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_left + display: flex + align-items: center + gap: 12px + +.compact_project_card .contributor_info + display: flex + align-items: center + gap: 4px + color: #6b7280 + html.dark & + color: #9ca3af + i + font-size: 10px + @media (min-width: 640px) + font-size: 12px + span + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_right + display: flex + align-items: center + +.compact_project_card .language_badge + display: inline-flex + align-items: center + justify-content: center + padding: 2px 8px + border-radius: 9999px + font-size: 10px + font-weight: 500 + line-height: 1.5 + white-space: nowrap + text-align: center + min-width: 40px + background: rgba(255, 185, 26, 0.3) + color: #1D0631 + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 12px + html.dark & + background: #ffb91a + color: #000000 + +// Responsive visibility helpers +.desktop_only + display: none !important + @media (min-width: 768px) + display: block !important + +.mobile_only + display: block + @media (min-width: 768px) + display: none !important + +.mobile_card + width: 100% + +.hidden_card + width: 100% + margin-bottom: 16px + +// Show More Button +.show_more_btn + width: 100% + font-size: 14px + background: white + color: #5A2A82 + border: 2px solid #5A2A82 + padding: 12px 16px + border-radius: 8px + font-weight: 600 + cursor: pointer + transition: all 0.3s ease + margin-top: 8px + display: block + &:hover + background: #5A2A82 + color: white + html.dark & + background: transparent + color: white + border-color: white + &:hover + background: white + color: #5A2A82 + +// Pagination Controls +.pagination_controls + display: flex + align-items: center + justify-content: center + gap: 16px + margin-top: 16px + padding: 12px + +.pagination_btn + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background: white + border: 1px solid #e5e7eb + border-radius: 8px + cursor: pointer + transition: all 0.3s ease + color: #5A2A82 + &:hover:not([disabled]) + background: #5A2A82 + color: white + border-color: #5A2A82 + &[disabled] + opacity: 0.4 + cursor: not-allowed + i + font-size: 14px + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: white + &:hover:not([disabled]) + background: #5A2A82 + border-color: #5A2A82 + +.pagination_info + font-size: 14px + color: #6b7280 + font-weight: 500 + html.dark & + color: #9ca3af + .current_page + color: #5A2A82 + font-weight: 700 + html.dark & + color: #ffb91a + +.swimlane_project_item + display: none + &[data-page="1"] + display: block + +// View All Projects Button - Matches Figma: pt-4 pb-8 +.view_all_container + padding-top: 16px + padding-bottom: 32px + padding-left: 16px + padding-right: 16px + display: flex + justify-content: center + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-left: 32px + padding-right: 32px + html.dark & + background: #1D0631 + +// Matches Figma: flex items-center gap-2 px-6 py-3 bg-[#ffb91a] rounded-lg +.btn_view_all_projects + display: inline-flex + align-items: center + gap: 8px + padding: 12px 24px + background: #ffb91a + color: #000000 + border-radius: 8px + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + font-size: 16px + &:hover + background: rgba(255, 185, 26, 0.9) + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) + color: #000000 + html.dark & + color: #ffffff !important + &:hover + color: #ffffff !important + i + font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass new file mode 100644 index 000000000..b1c0d45e8 --- /dev/null +++ b/app/assets/stylesheets/home_search.sass @@ -0,0 +1,145 @@ +// ======================================== +// Home Page Search Bar - Figma Design +// ======================================== +// Dedicated styles for search input with higher specificity +// to override Bootstrap and other base styles + +// Hero Section Search Container - Nested under .hero_section +.hero_section + .search_form#search_form + margin-bottom: 0 + position: relative + width: 100% + + .search_input_wrapper + position: relative + width: 100% + display: block + + // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) + // Positioned to align with input text baseline + .search_icon + position: absolute + top: 50% + left: 8px + transform: translateY(-60%) + display: block + width: 14px + height: 14px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 10 + transition: color 0.3s ease + text-align: center + font-style: normal + @media (min-width: 640px) + left: 24px + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Ensure Font Awesome icon centers properly + &::before + display: block + + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 + input.search_input + width: 100% + padding: 10px 12px 10px 32px + font-size: 11px + font-weight: 400 + line-height: 1.5 + height: auto + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #ffffff + background-clip: padding-box + color: #111827 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + appearance: none + -webkit-appearance: none + -moz-appearance: none + vertical-align: middle + box-sizing: border-box + + // Placeholder styling - Figma specs + &::placeholder + color: #9ca3af + opacity: 1 + + // Hover state - Figma specs + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) + border-color: #d1d5db + + // Focus state - Figma specs + &:focus + border-color: #0E4B7A + outline: none + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + background: #ffffff + &::placeholder + color: #d1d5db + + // Desktop - Figma: sm:pl-14 sm:pr-4 sm:py-4 + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px + border-radius: 16px + + // Dark mode support + html.dark & + background: #1D0631 + color: #ffffff + border-color: #4b5563 + &::placeholder + color: #6b7280 + opacity: 1 + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1) + border-color: #6b7280 + &:focus + border-color: #2E8B9E + background: #1D0631 + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// ======================================== +// Bootstrap Override - Global Search Form +// ======================================== +// High specificity overrides for Bootstrap and other frameworks +// Ensures Figma padding is applied: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 +form#search_form + input[type="text"].search_input, + input.search_input + width: 100% !important + padding: 10px 12px 10px 32px !important + font-size: 11px !important + height: auto !important + border: 2px solid #e5e7eb !important + border-radius: 16px !important + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important + background: #ffffff !important + color: #111827 !important + box-sizing: border-box !important + @media (min-width: 640px) + padding: 16px 16px 16px 56px !important + font-size: 16px !important + &:focus + border-color: #0E4B7A !important + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important + outline: none !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff !important + border-color: #4b5563 !important + &:focus + border-color: #2E8B9E !important + background: #1D0631 !important + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index bda95eea6..db0833582 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -55,13 +55,16 @@ @mixin top-navbar-colors color: white - background-color: #211e1e !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important @mixin top-navbar-menu-colors - color: white + color: white !important + font-weight: 500 + transition: color 0.3s ease &:hover padding-bottom: 0px - color: color-gradient($SECONDARY_YELLOW, 60) + color: rgba(255, 255, 255, 0.7) !important + text-decoration: none @mixin twitter-background-color background-color: #5A2A82 @@ -119,15 +122,15 @@ border-left: 3px solid color-gradient($PRIMARY_DARK_GRAY, 100) @mixin footer-colors - color: color-gradient($PRIMARY_PURPLE, 120) !important - background-color: color-gradient($FOOTER_BACKGROUND, 0) !important + color: white !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important a &:hover - color: color-gradient($FOOTER_BACKGROUND, 0) + color: white !important border-radius: 0 @mixin footer-copyright-colors - color: color-gradient($LIGHT_GRAY, 100) !important + color: rgba(156, 163, 175, 1) !important //////////////////////////////////////////////////////////////////// // @@ -264,6 +267,26 @@ &.no-border:hover border-color: color-gradient($LIGHT_GRAY, 80) !important +@mixin purple-theme-button-colors + color: white + background-color: #5A2A82 !important + border-color: #5A2A82 !important + &:hover + background-color: #4A1A72 !important + border-color: #4A1A72 !important + &.no-border:hover + border-color: #4A1A72 !important + +@mixin search-input-colors + background-color: #ffffff + color: #202020 + border-color: #d1d5db + &:focus + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + &::placeholder + color: #9ca3af + @mixin join-now-button-colors color: white background-color: color-gradient($SECONDARY_FOREST_GREEN, 100) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cc813f4f1..cb5fd0334 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -1,21 +1,42 @@ @import "base" +// Mobile menu slide down animation +@keyframes slideDown + 0% + opacity: 0 + transform: translateY(-10px) + 100% + opacity: 1 + transform: translateY(0) + body - background-color: #211e1e !important - font-family: Roboto + background-color: white !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" font-size: 1.3rem !important margin: 0 + padding: 0 color: #000 + width: 100% + overflow-x: hidden -#page - width: 980px +.container#page, #page + width: 100% !important + max-width: 100% !important background-color: white - margin: 0 auto - padding: 10px 10px 0 10px + margin: 0 !important + padding: 0 !important header - background-color: white - margin: -10px -10px 0px -10px + // Figma design: gradient background for entire header + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-size: 16px + margin: 0 + padding: 0 + width: 100% + position: sticky + top: 0 + z-index: 1000 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -27,60 +48,94 @@ header overflow-x: hidden !important @media (min-width: 540px) and (max-width: 1024px) - #page - margin: 0 auto - padding: 10px 10px 0 10px - width: auto + #page, .container#page + margin: 0 !important + padding: 0 !important + width: 100% !important + max-width: 100% !important #nav-top-bar - margin: 11px 11px 0 11px + width: 100% + max-width: 1280px + margin: 0 auto + padding: 0 32px + display: flex + align-items: center .new_main_menu - list-style: none - margin: 0 - padding: 0 + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + gap: 18px !important li - display: inline-block - margin-right: 32px + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important a - @include top-navbar-menu-colors - font-size: 11pt - text-decoration: none + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: color 0.3s ease !important + white-space: nowrap !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + padding: 6px 0 !important + margin: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important #navbar-inner - @include top-navbar-colors - padding-top: 1px - margin-left: 4px - margin-right: 5px - height: 42px + // Navigation moved to main navbar - hide this section + display: none -@media (min-width: 320px) and (max-width: 340px) +@media (min-width: 320px) and (max-width: 480px) #nav-top-bar - margin: 11px 4px 0 0px !important + padding: 0 16px .new_main_menu + gap: 16px + .mobile-menu + .mobile-menu-content + padding: 0 8px + .mobile-menu-items li - margin-right: 2px !important a - font-size: 10px !important - -@media (min-width: 341px) and (max-width: 480px) + padding: 10px 12px + font-size: 13px + +@media (min-width: 481px) and (max-width: 768px) #nav-top-bar - margin: 11px 10px 0 11px !important - .new_main_menu - li - margin-right: 6px !important - a - font-size: 10px !important -#nav-top-bar .new_main_menu li a:hover - color: #fff - text-decoration: underline + padding: 0 24px + .new_main_menu + gap: 24px #page_contents + max-width: 100% + padding: 0 20px .separator-div @include site-separator-color #project_container background-color: white margin: 0 auto - padding: 10px 10px 0 10px + padding: 10px 20px 0 20px + width: 100% + max-width: 100% #page-contents width: auto #project_masthead @@ -99,132 +154,667 @@ header .quick_reference_heading, .nutshell_heading padding-left: 2rem -@media (min-width: 320px) and (max-width: 480px) +// sm: 640px+ +@media (min-width: 640px) .navbar - margin-right: 0px !important - .btn - font-size: 10px !important - .company-div - float: none !important - .navbar_large_text - font-size: 18px !important + .navbar-container + padding: 0 16px + gap: 12px + .logo-and-brand + gap: 6px + .logo-div + .logo_img + height: 28px + .company-div + .navbar_large_text + font-size: 16px + .separator-div + display: block + height: 24px + .actions-div + gap: 8px + .theme-toggle-btn + padding: 8px + .theme-icon + font-size: 20px + .mobile-menu-toggle + padding: 8px + i + font-size: 20px + .mobile-menu + .mobile-menu-content + padding: 0 12px + .mobile-menu-items + li + a + padding: 12px 16px + +// md: 768px+ +@media (min-width: 768px) + .navbar + .navbar-container + padding: 0 24px + gap: 16px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 32px + .separator-div + display: block + height: 26px + .logo-and-brand + gap: 10px + .mobile-menu + .mobile-menu-content + padding: 0 24px + .mobile-menu-items + li + a + padding: 14px 20px + font-size: 15px + +// lg: 1024px+ (Desktop) +@media (min-width: 1024px) + .navbar + min-height: 72px + .navbar-container + padding: 0 32px + min-height: 72px + .logo-and-brand + gap: 12px + .logo-div + .logo_img + height: 32px !important + .company-div + .navbar_large_text + font-size: 20px + .separator-div + height: 28px + .actions-div + gap: 16px + .nav-menu + display: flex !important + .new_main_menu + gap: 18px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 14px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + .desktop-actions + display: flex !important + .mobile-menu-toggle + display: none !important + .mobile-menu + display: none + +// xl: 1280px+ +@media (min-width: 1280px) + .navbar + min-height: 80px + .navbar-container + min-height: 80px + .logo-div + .logo_img + height: 48px + .company-div + .navbar_large_text + font-size: 24px + .nav-menu + .new_main_menu + gap: 28px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// 2xl: 1536px+ +@media (min-width: 1536px) + .navbar + min-height: 88px + .navbar-container + min-height: 88px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 30px + .nav-menu + .new_main_menu + gap: 36px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// Mobile/Tablet: < 1024px (show hamburger menu) +@media (max-width: 1023px) + .navbar + .nav-menu + display: none !important + .desktop-actions + display: none !important + .mobile-menu-toggle + display: flex !important .navbar + // Figma design: gradient background with purple theme + position: relative + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) border-width: 0 - margin-top: 15px - margin-right: 15px - margin-bottom: 5px !important - margin-left: 15px - padding: 0 0 5px 0 - min-height: 30px + border-bottom: 1px solid rgba(31, 41, 55, 0.8) + margin: 0 + padding: 0 + min-height: 64px + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2) + width: 100% + display: flex + align-items: flex-start + flex-wrap: wrap + overflow: visible + z-index: 1001 + // Override global button styles for navbar + .btn + border: none !important + border-width: 0 !important + box-shadow: none !important + &:active + top: 0 !important + left: 0 !important + .navbar-container + max-width: 100% + margin: 0 auto + padding: 0 16px + width: 100% + min-height: 64px + display: flex !important + flex-direction: row !important + flex-wrap: nowrap !important + align-items: center + justify-content: space-between + gap: 12px + position: relative + .hidden + display: none !important + position: absolute !important + width: 0 !important + height: 0 !important + overflow: hidden !important + .logo-and-brand + display: flex + align-items: center + gap: 8px + flex-shrink: 0 + min-width: 0 .logo-div - height: 35px - float: left + display: flex + align-items: center + flex-shrink: 0 .logo_link text-decoration: none - margin-top: 3px + display: flex + align-items: center .logo_img - height: 100% + height: 20px + width: auto + display: block + object-fit: contain .company-div - @include top-navigation-colors - float: left + color: white !important + display: flex + align-items: center + min-width: 0 .navbar_small_text font-size: 10px !important .navbar_large_text - font-size: 24px + color: white !important + font-weight: 700 + line-height: 1.25 + letter-spacing: 0 + white-space: nowrap a - @include top-navigation-colors + color: white !important text-decoration: none - .spacing-div - float: left - width: 2px - height: 30px - background-color: white - margin: 0 auto + &:hover + color: #d1d5db !important + transition: color 0.3s ease .separator-div - @include site-separator-color - @include site-separator-background-color - float: left + background-color: rgba(255, 255, 255, 0.2) width: 1px - height: 30px - margin: 0 2px 0 2px + height: 20px + margin: 0 + display: none + .nav-menu + display: none + position: absolute + left: 50% + transform: translateX(-50%) + .new_main_menu + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + li + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important + a + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: all 0.3s ease !important + white-space: nowrap !important + position: relative !important + padding: 6px 0 !important + margin: 0 !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + &::after + content: '' !important + position: absolute !important + bottom: 0 !important + left: 0 !important + right: 0 !important + height: 2px !important + background-color: #FFB91A !important + border-radius: 2px !important .actions-div - float: right - .twitter-text - @include twitter-background-color - color: white - font-weight: bold - font-size: 0.80em + display: flex + align-items: center + gap: 12px + flex-shrink: 0 + flex-grow: 0 + .theme-toggle-btn + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + .theme-icon + font-size: 18px + color: white + transition: all 0.3s ease + .hidden + display: none + .mobile-menu-toggle + display: none + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + i + font-size: 18px + color: white .btn border: 0 - display: inline-block - font-size: 15px - line-height: 25px + display: inline-flex + align-items: center + justify-content: center + font-size: 14px + line-height: 1.5 margin: 0 - padding: 1px 6px 0 7px - .follow_btn - @include follow-button-colors - display: inline-block - padding: 0 0 0 4px - height: 24px - i, p, img - color: white - display: inline-block - vertical-align: top - i - color: #555 - vertical-align: middle - &:before - padding-right: 0 - p - font-weight: normal - line-height: 24px - text-transform: none - margin: 0 - border-radius: 0 4px 4px 0 - img - margin-right: -1px + padding: 8px 16px + border-radius: 8px + font-weight: 600 + transition: all 0.3s ease + cursor: pointer + text-decoration: none + white-space: nowrap ul list-style: none - margin: 3px 0 0 0 + margin: 0 + padding: 0 + display: flex + align-items: center + gap: 12px li - float: left - padding: 0 0 0 8px + padding: 0 + list-style: none #top_nav_actions - float: right - margin-right: 5px + display: flex + align-items: center + gap: 12px + margin: 0 + padding: 0 li - padding-top: 5px + padding: 0 + list-style: none + .btn-primary, .btn-success + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-width: 0 !important + font-weight: 600 !important + padding: 10px 20px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: transparent !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) !important + i + font-size: 14px + margin: 0 + .btn-outline + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + font-weight: 600 !important + padding: 8px 18px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + border-color: #FFB91A !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important @media (min-width: 320px) and (max-width: 340px) #top_nav_actions margin-right: 0px !important -#logged_user_menu - a - color: black !important +// Mobile Menu +.mobile-menu + display: none + width: 100% + flex-basis: 100% + background: linear-gradient(to right, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + border-top: 1px solid rgba(255, 255, 255, 0.15) + padding: 20px 0 + position: relative + z-index: 1002 + overflow: visible + order: 2 + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) + &.show + display: block !important + visibility: visible !important + opacity: 1 !important + animation: slideDown 0.3s ease-out + .mobile-menu-content + max-width: 1280px + margin: 0 auto + padding: 0 16px + width: 100% + display: block + visibility: visible + .mobile-menu-items + list-style: none + margin: 0 + padding: 0 + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-weight: 500 + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + &.select + color: #FFB91A !important + font-weight: 600 + background-color: rgba(255, 185, 26, 0.15) + .mobile-menu-user + list-style: none + margin: 16px 0 0 0 + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.15) + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + .mobile-menu-signin + margin-top: 16px + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.1) + display: block + visibility: visible + .btn-mobile-signin + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + margin-bottom: 12px + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) + &:active + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) + .btn-mobile-join-now + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) + &:active + transform: translateY(0) + box-shadow: none + +.dropdown#logged_user_menu + position: relative + z-index: 1000 + .dropdown-toggle + color: white !important text-transform: none - text-decoration: none - margin: 0 0px 15px - padding-right: 10px - padding-top: 3px - ul - margin-top: 40px - border-radius: 0 + text-decoration: none !important + margin: 0 + padding: 10px 16px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + display: inline-flex !important + align-items: center + gap: 8px + font-size: 14px + font-weight: 500 + transition: all 0.3s ease + border: 1px solid transparent + cursor: pointer !important + position: relative + z-index: 1001 + pointer-events: auto !important + &:hover + background-color: rgba(255, 255, 255, 0.15) + border-color: rgba(255, 255, 255, 0.2) + text-decoration: none !important + transform: translateY(-1px) + &:active + transform: translateY(0) + i + font-size: 14px + pointer-events: none + ul.dropdown-menu + display: none + position: absolute + right: 0 + top: calc(100% + 24px) + margin-top: 8px + border-radius: 12px + border: 1px solid rgba(255, 255, 255, 0.15) + background-color: rgba(29, 6, 49, 0.97) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 4px 10px rgba(0, 0, 0, 0.2) + z-index: 1050 + min-width: 200px + padding: 8px 0 li - width: 150px + width: 100% a - color: black + color: white !important margin: 0 + padding: 12px 16px + transition: all 0.3s ease + border-radius: 8px + display: block + cursor: pointer &:hover - background-color: #0082C6 !important - color: #fff !important + background-color: rgba(255, 185, 26, 0.2) !important + color: #FFB91A !important + transform: translateX(4px) .divider width: 100% height: 1px - margin: 13px 0 + margin: 8px 0 overflow: hidden - border-bottom: 1px solid #e5e5e5 + border-bottom: 1px solid rgba(255, 255, 255, 0.15) + &.open + ul.dropdown-menu + display: block + +// Override Bootstrap's default dropdown styling +.navbar .dropdown.open .dropdown-menu + display: block !important + +#top_nav_actions + position: relative + z-index: 1000 #flash-msg .alert @@ -277,7 +867,6 @@ header @media (min-width: 320px) and (max-width: 480px) h2 - font-size: 12px !important margin-top: 5px !important .navbar .follow_btn @@ -533,3 +1122,47 @@ fieldset width: 14rem .language_percentage_indicator width: 3rem + +// Full-width layout styles +.page-content-wrapper + width: 100% + max-width: 100% + margin: 0 auto + padding: 20px + +// Ensure home page content uses full width +.home-content, .explore-content, .billboard + width: 100% + max-width: 100% + margin: 0 auto + padding: 0 20px + +// Override Bootstrap navigation styles +.navbar .nav-menu .new_main_menu li.menu_item a, +.navbar ul.new_main_menu li.menu_item a, +ul.new_main_menu li.menu_item a + color: white !important + font-size: 14px !important + font-weight: 500 !important + text-decoration: none !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + padding: 6px 0 !important + margin: 0 !important + background: none !important + border: none !important + box-shadow: none !important + letter-spacing: 0 !important + &:hover, &:focus, &:active, &:visited + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + +@media (min-width: 1280px) + .navbar .nav-menu .new_main_menu li.menu_item a, + .navbar ul.new_main_menu li.menu_item a, + ul.new_main_menu li.menu_item a + font-size: 16px !important diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index f75db9c1d..6164b1c78 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -1,15 +1,19 @@ #search-dingus @include site-well-color - padding: 7px 0px + padding: 10px 15px line-height: 29px - min-height: 33px + min-height: 50px margin-bottom: 1em + border-radius: 8px #value_select display: inline !important select width: 400px .col-md-5 - width: 43% + width: 50% + display: flex + align-items: stretch + padding: 0 .col-md-4 width: 32% @@ -19,12 +23,75 @@ label.paginate font-size: 13px + + label.sr-only + position: absolute + width: 1px + height: 1px + padding: 0 + margin: -1px + overflow: hidden + clip: rect(0, 0, 0, 0) + white-space: nowrap + border-width: 0 + + .search-input-group + display: flex + align-items: stretch + width: 100% + max-width: 500px + button.btn - height: 34px - border-top: 10px - input - width: 180px - color: #000 + height: auto + border: 1px solid #5A2A82 + line-height: 1 + padding: 8px 16px + display: flex + align-items: center + justify-content: center + + input[type="text"] + flex: 1 + min-width: 250px + max-width: 100% + font-size: 14px + color: #1f2937 + background-color: #ffffff + border: 1px solid #d1d5db + padding: 10px 14px + transition: all 0.15s ease-in-out + border-radius: 12px 0 0 12px + margin: 0 + height: auto + line-height: 1.5 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + &:hover + border-color: #9ca3af + &:focus + outline: none + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + background-color: #ffffff + &::placeholder + color: #9ca3af + font-size: 14px + + // Dark mode support + .dark & + background-color: rgba(29, 6, 49, 0.5) + input[type="text"] + background-color: #1D0631 + color: #ffffff + border-color: #4b5563 + &:hover + border-color: #6b7280 + &:focus + border-color: #ffb91a + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) + background-color: #1D0631 + &::placeholder + color: #6b7280 + select width: 150px label.checkbox diff --git a/app/views/home/_compact_project_card.html.haml b/app/views/home/_compact_project_card.html.haml new file mode 100644 index 000000000..d67b36c4e --- /dev/null +++ b/app/views/home/_compact_project_card.html.haml @@ -0,0 +1,37 @@ +- is_account = project.is_a?(Account) +- project_url = is_account ? account_path(project) : project_path(project) + +.compact_project_card{ data: { project_id: project.id } } + .compact_card_inner + .compact_card_header + .project_logo + = link_to project_url do + - if is_account + = image_tag(avatar_img_path(project), class: 'account-avatar') + - elsif project.logo.present? + = image_tag(project.logo.attachment.url(:small), alt: project.name) + - else + .project-icon-placeholder + = project.name[0].upcase + .project_info + .project_name_row + = link_to project_url, class: 'project_name' do + %span= h(truncate(project.name, length: 30)) + - if !is_account && project.updated_at > 7.days.ago + %span.trending_icon + %i.fa.fa-arrow-trend-up + .compact_card_description + - description = is_account ? (project.try(:about) || '') : (project.description || '') + %p= h(truncate(description, length: 80)) + .compact_card_footer + .footer_left + .contributor_info + %i.fa.fa-users + - count = is_account ? (project.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0) : (project.best_analysis&.headcount || 0) + %span= count + .footer_right + - if is_account + - language_name = project.best_account_analysis&.account_analysis_fact&.primary_language&.nice_name || 'N/A' + - else + - language_name = project.best_analysis&.main_language&.nice_name || 'NA' + %span.language_badge= language_name diff --git a/app/views/home/_join_now_home.html.haml b/app/views/home/_join_now_home.html.haml index 2fe5d5a3c..b2a24f233 100644 --- a/app/views/home/_join_now_home.html.haml +++ b/app/views/home/_join_now_home.html.haml @@ -1,16 +1,28 @@ -.join_now_home - %ul - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.claim') - %span.join_now_color= t('.contributions') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.manage') - %span.join_now_color= t('.projects_data') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.highlight') - %span.join_now_color= t('.use') - %br - %p - = link_to t('.join_now'), h(new_account_path(invite: params[:invite], ref: 'homepage')), class: 'btn btn_join_now' +.join_now_card + .card_inner + .join_header + .join_icon + %i.fa.fa-user-plus + .join_header_text + %h3.join_title= t('.join_now') + %p.join_subtitle= t('.become_part_community') + + .benefits_list + .benefit_item + .benefit_icon.benefit_icon_award + %i.fa.fa-trophy + .benefit_content + %h4.benefit_title= t('.track_contributions_title') + %p.benefit_description= t('.track_contributions_desc') + + .benefit_item + .benefit_icon.benefit_icon_trending + %i.fa.fa-line-chart + .benefit_content + %h4.benefit_title= t('.gain_recognition_title') + %p.benefit_description= t('.gain_recognition_desc') + + .join_cta + = link_to new_account_path(invite: params[:invite], ref: 'homepage'), class: 'btn btn_join_cta' do + %i.fa.fa-user-plus + = t('.create_account') diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml new file mode 100644 index 000000000..d87f62121 --- /dev/null +++ b/app/views/home/_top_contributors.html.haml @@ -0,0 +1,40 @@ +.top_contributors_card + .card_header + .header_icon + %i.fa.fa-users + .header_content + %h3.card_title= t('.top_contributors') + %p.card_subtitle= t('.most_active_developers') + + .contributors_list + - @home.most_active_contributors&.each_with_index do |contributor, index| + = link_to account_path(contributor), class: 'contributor_item' do + .contributor_avatar + - begin + = image_tag avatar_img_path(contributor, 40), class: 'avatar_img', alt: contributor.name, onerror: "this.onerror=null; this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2240%22 height=%2240%22 viewBox=%220 0 40 40%22%3E%3Crect width=%2240%22 height=%2240%22 rx=%226%22 fill=%22%239CA3AF%22/%3E%3Ctext x=%2250%25%22 y=%2250%25%22 dominant-baseline=%22middle%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-size=%2218%22 font-weight=%22600%22 fill=%22white%22%3E#{h(contributor.name[0].upcase)}%3C/text%3E%3C/svg%3E';" + - rescue + %div.avatar_placeholder{style: "width: 40px; height: 40px; border-radius: 12px; background: #9CA3AF; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 18px;"} + = contributor.name[0].upcase + + .contributor_info + .contributor_name_row + %span.contributor_name= h(contributor.name) + %span.contributor_rank= "Rank ##{index + 1}" + .contributor_stats + - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 + - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project + - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 + %span.stat_commits + %i.fa.fa-dot-circle-o + = number_with_delimiter(commits) + %span.stat_projects + %i.fa.fa-code + = "#{project_count} projects" + + .contributor_arrow + %i.fa.fa-chevron-right + + .view_all_footer + = link_to accounts_path, class: 'view_all_link' do + %i.fa.fa-users + %span= t('.view_all_contributors') diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index 6630636ee..e0d18cc2b 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -1,56 +1,85 @@ -.col-md-3.top_ten - - if device? - %h2.most_popular_projects - %button.collapsed#no-background{"aria-controls" => "collapseOne", "aria-expanded" => "false", "data-target" => "#collapseOne", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase1 - =t('.popular_projects') - .collapse#collapseOne - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } - - else - %h2.most_popular_projects= t('.popular_projects') - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_projects - %button.collapsed{"aria-controls" => "collapseTwo", "aria-expanded" => "false", "data-target" => "#collapseTwo", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase2 - =t('.active_projects') - .collapse#collapseTwo - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } - - else - %h2.most_active_projects= t('.active_projects') - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } -.col-md-3.top_ten.last - - if device? - %h2.most_recent_projects - %button.collapsed#no-background{"aria-controls" => "collapseThree", "aria-expanded" => "false", "data-target" => "#collapseThree", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase3 - =t('.recent_projects') - .collapse#collapseThree - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } - - else - %h2.most_recent_projects=t('.recent_projects') - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_contributors - %button.collapsed{"aria-controls" => "collapseFour", "aria-expanded" => "false", "data-target" => "#collapseFour", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase4 - =t('.active_contributors') - .collapse#collapseFour - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } - - else - %h2.most_active_contributors=t('.active_contributors') - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } \ No newline at end of file +/ Project Swimlanes Section - New Design +.project_swimlanes_section + .swimlanes_container + .swimlanes_grid + / Most Active Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.active_projects') + .swimlane_content{ data: { swimlane: 'active' } } + / Desktop: Show all projects + - @home.most_active_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_active_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_active_projects.size > 2 + - @home.most_active_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'active', target: 'active', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Most Popular Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.popular_projects') + .swimlane_content{ data: { swimlane: 'popular' } } + / Desktop: Show all projects + - @home.most_popular_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_popular_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_popular_projects.size > 2 + - @home.most_popular_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'popular', target: 'popular', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Recently Added Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.recent_projects') + .swimlane_content{ data: { swimlane: 'recent' } } + / Desktop: Show all projects + - @home.most_recent_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_recent_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_recent_projects.size > 2 + - @home.most_recent_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'recent', target: 'recent', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + + / View All Projects Button + .view_all_container + = link_to projects_path, class: 'btn_view_all_projects' do + = t('.view_all_projects') + %i.fa.fa-arrow-right diff --git a/app/views/home/_user_journeys.html.haml b/app/views/home/_user_journeys.html.haml new file mode 100644 index 000000000..a5d6cc56e --- /dev/null +++ b/app/views/home/_user_journeys.html.haml @@ -0,0 +1,272 @@ +.user_journeys_section + .container + .journeys_header + %h2.journeys_title= t('.your_journey_with_openhub') + %p.journeys_subtitle= t('.explore_ways_description') + + / Mobile View - Collapsible Cards + .journeys_mobile + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '0'} + .journey_header_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.journey_header_title= t('.discover_compare_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.discover_compare_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '1'} + .journey_header_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.journey_header_title= t('.assess_health_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.assess_health_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '2'} + .journey_header_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.journey_header_title= t('.manage_portfolio_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.manage_portfolio_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '3'} + .journey_header_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.journey_header_title= t('.analyze_code_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.analyze_code_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '4'} + .journey_header_icon.journey_icon_security + %i.fa.fa-shield + %h3.journey_header_title= t('.check_security_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.check_security_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '5'} + .journey_header_icon.journey_icon_contribute + %i.fa.fa-search + %h3.journey_header_title= t('.identify_opportunities_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.identify_opportunities_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + + / Desktop View - Grid Cards + .journeys_desktop + .journey_grid_card + .grid_card_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.grid_card_title= t('.discover_compare_title') + %p.grid_card_description= t('.discover_compare_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.grid_card_title= t('.assess_health_title') + %p.grid_card_description= t('.assess_health_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.grid_card_title= t('.manage_portfolio_title') + %p.grid_card_description= t('.manage_portfolio_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.grid_card_title= t('.analyze_code_title') + %p.grid_card_description= t('.analyze_code_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_security + %i.fa.fa-shield + %h3.grid_card_title= t('.check_security_title') + %p.grid_card_description= t('.check_security_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_contribute + %i.fa.fa-search + %h3.grid_card_title= t('.identify_opportunities_title') + %p.grid_card_description= t('.identify_opportunities_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + +:javascript + document.addEventListener('DOMContentLoaded', function() { + const toggleButtons = document.querySelectorAll('.journey_toggle'); + + toggleButtons.forEach(function(button) { + button.addEventListener('click', function() { + const journeyCard = this.closest('.journey_card'); + const content = journeyCard.querySelector('.journey_card_content'); + const icon = this.querySelector('.journey_toggle_icon i'); + + journeyCard.classList.toggle('expanded'); + + if (journeyCard.classList.contains('expanded')) { + icon.classList.remove('fa-plus'); + icon.classList.add('fa-minus'); + } else { + icon.classList.remove('fa-minus'); + icon.classList.add('fa-plus'); + } + }); + }); + }); diff --git a/app/views/home/_whats_new.html.haml b/app/views/home/_whats_new.html.haml index 130643baf..c045b66e9 100644 --- a/app/views/home/_whats_new.html.haml +++ b/app/views/home/_whats_new.html.haml @@ -1,2 +1,9 @@ -%a{ href: 'https://www.blackduck.com/resources/analyst-reports/open-source-security-risk-analysis.html', target: '_blank' } - %img{src: image_path('home/OSSRA-OH-banner.png'), style: 'margin-left: 25px; margin-top: 15px'} +.whats_new_card + .card_inner + .card_header_section + %h3.whats_new_title= t('.whats_new') + %p.whats_new_subtitle= t('.latest_updates_reports') + + .featured_image_section + %a{ href: 'https://www.blackduck.com/resources/analyst-reports/ossra-report.html', target: '_blank', class: 'featured_image_link' } + %img.featured_image{src: image_path('home/OSSRA-OH-banner.png'), alt: t('.ossra_report_alt')} diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index c47b3002b..a9cfb02b6 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,31 +1,83 @@ - content_for(:html_title) { t('.page_title') } -.showcase - .billboard - .row - .col-md-12 - %h2.discover_msg - = t('.discover_message') - .row - .col-md-12.billboard_search - %form.search#search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'homepage' } - %input.for_search_all_code{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text' } - %i#icon_text.fa.fa-search - - .row - .col-md-12 - .global_stats - #global_statistics - %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) - %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) - %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) - %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) -.home_page_row - .col-md-4.col-xs-7.col-sm-6 - %h2.join_now= t('.join_now') - = render partial: '/home/join_now_home' - .col-md-8.col-xs-5.col-sm-6 - %h2.wh_new= t('.whats_new') - = render partial: '/home/whats_new' -.landing.col-md-12.col-sm-12.col-xs-12.last_section - = home_top_lists +- content_for(:javascript) do + = javascript_include_tag 'rotating_stats' + = javascript_include_tag 'project_swimlanes' + +/ Hero Section +.hero_section + .hero_container + / Hero Header - Centered + .hero_header + %h1.hero_title= t('.discover_message') + %p.hero_subtitle= t('.worlds_largest_directory') + + / Search Bar - Centered & Compact + .search_container + %form.search_form#search_form{ action: projects_path } + %input{ type: :hidden, name: 'ref', value: 'homepage' } + .search_input_wrapper + %i.fa.fa-search.search_icon + %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } + + / Rotating Stats + .rotating_stats + #global_statistics + %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) + %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) + %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) + %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) + + / Feature Cards - Moved to Top + .features_section + .feature_card + .feature_icon.feature_icon_yellow + %i.fa.fa-database + %h3.feature_title= t('.feature_directory_title') + %p.feature_description= t('.feature_directory_desc') + .feature_card + .feature_icon.feature_icon_blue + %i.fa.fa-heartbeat + %h3.feature_title= t('.feature_health_title') + %p.feature_description= t('.feature_health_desc') + .feature_card + .feature_icon.feature_icon_green + %i.fa.fa-user-circle + %h3.feature_title= t('.feature_portfolio_title') + %p.feature_description= t('.feature_portfolio_desc') + + / Quick Stats Bar - Ultra Compact & Elegant + .quick_stats_bar + .stat_item + .stat_value= "2.5M+" + .stat_label= t('.projects_label') + .stat_divider + .stat_item + .stat_value= number_with_delimiter(@home.person_count / 1000) + "K" + .stat_label= t('.contributors_label') + .stat_divider + .stat_item + .stat_value= "450+" + .stat_label= t('.languages_label') + .stat_divider + .stat_item + .stat_value= "12K" + .stat_label= t('.organizations_label') +/ Top Contributors and What's New Section +.content_section + .container + .content_grid + / Top Contributors - 35% + .contributors_column + = render partial: '/home/top_contributors' + + / What's New and Join Now - 65% + .whats_new_column + .whats_new_join_stack + = render partial: '/home/whats_new' + = render partial: '/home/join_now_home' + +/ User Journeys Section += render partial: '/home/user_journeys' + +/ Project Swimlanes Section += home_top_lists diff --git a/app/views/layouts/partials/_footer.html.haml b/app/views/layouts/partials/_footer.html.haml index 53b3e40d7..d16f5375d 100644 --- a/app/views/layouts/partials/_footer.html.haml +++ b/app/views/layouts/partials/_footer.html.haml @@ -1,40 +1,51 @@ - raw_email = '%69%6Efo%40O%70en%48u%62.%6E%65t' -.footer-left - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } +.footer-container + .footer-grid + .footer-left + %a.logo_link{ href: 'https://www.blackduck.com' } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck' } -.footer-mid - = "ABOUT BLACK DUCK" - %p - %p - %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' - %p - %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' - %p - %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' - %p - %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' + .footer-mid + %h3 ABOUT BLACK DUCK + %ul + %li + %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' + %li + %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' + %li + %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' + %li + %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' -.footer-right - = "ABOUT OPEN HUB" - %p - %p - %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog - %p - %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') - %p - %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us + .footer-right + %h3 ABOUT OPEN HUB + %ul + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog + %li + %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') + %li + %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us - -.footer-bottom - %sup © - = Time.current.year - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span{ itemprop: 'publisher' } - %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." - = t('copyright.rights_text') + .footer-bottom + .footer-bottom-content + .copyright + %sup © + = Time.current.year + %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span{ itemprop: 'publisher' } + %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." + = t('copyright.rights_text') + .follow-us + %span Follow us: + :ruby + twitter_url = 'https://x.com/intent/follow?original_referer=' + twitter_url += CGI.escape request.url + twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' + %a{ href: twitter_url, target: '_blank', 'aria-label' => 'Follow us on Twitter' } + %i.icon-twitter diff --git a/app/views/layouts/partials/_mast.html.haml b/app/views/layouts/partials/_mast.html.haml index f28f75c58..e67c7f71c 100644 --- a/app/views/layouts/partials/_mast.html.haml +++ b/app/views/layouts/partials/_mast.html.haml @@ -1,52 +1,103 @@ +:ruby + explore_projects_class = params[:action] == 'explore/projects' ? 'select' : '' + explore_orgs_class = params[:action] == 'explore/orgs' ? 'select' : '' + tools_select = params[:action] == 'tools' ? 'select' : '' + .navbar - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span.hidden{ itemprop: 'author' } openhub.net - %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. - %span + .navbar-container + %span.hidden{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span.hidden{ itemprop: 'author' } openhub.net + %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. + .logo-and-brand %div.logo-div - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } - %div.spacing-div + %a.logo_link{ href: root_path } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck Open Hub' } %div.separator-div - %div.spacing-div %div.company-div %a{ href: root_path } %span.navbar_large_text Open Hub - %div.actions-div - %ul#top_nav_actions - %li.twitter_follow - :ruby - twitter_url = 'https://twitter.com/intent/follow?original_referer=' - twitter_url += CGI.escape request.url - twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' - %a.btn.follow_btn{ href: twitter_url, target: '_blank' } - %i.icon-twitter - %p.follow! #{t(:follow)} @ - %p.twitter-text  OH  - - if logged_in? - %li - .dropdown#logged_user_menu - %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } - %i.icon-user - = h(current_user.name) - %i.icon-caret-down - %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer - %li - = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %nav.nav-menu + %ul.new_main_menu{ class: page_context[:select_top_menu_nav] } + %li.menu_item.projects + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li.menu_item.people + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li.menu_item.organizations + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li.menu_item.tools + = link_to t(:tools_menu), tools_path, class: tools_select + %li.menu_item.blog + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li.menu_item.bdsa + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + %div.actions-div + %button#theme-toggle.theme-toggle-btn{ 'aria-label' => 'Toggle theme' } + %i#theme-icon-moon.icon-moon.theme-icon + %i#theme-icon-sun.icon-sun.theme-icon.hidden + %ul#top_nav_actions.desktop-actions + - if logged_in? + %li + .dropdown#logged_user_menu + %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } + %i.icon-user + = h(current_user.name) + %i.icon-caret-down + %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) - - if current_user_is_admin? - %li - = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) - %li.divider - %li - = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) - - else - %li - %a.btn.btn-mini.btn-primary.btn-header{ href: new_session_path }= t :sign_in + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li.divider + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + %li + %a.btn.btn-primary.btn-header{ href: new_session_path } + %i.icon-user + = t :sign_in + %li + %a.btn.btn-outline.btn-join-now{ href: new_account_path } + Join Now + %button#mobile-menu-toggle.mobile-menu-toggle{ 'aria-label' => 'Menu' } + %i#mobile-menu-icon.icon-reorder + #mobile-menu.mobile-menu + .mobile-menu-content + %ul.mobile-menu-items + %li + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li + = link_to t(:tools_menu), tools_path, class: tools_select + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + = render 'shared/search.html.haml' + - if logged_in? + %ul.mobile-menu-user + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - %a.btn.btn-mini.btn-success.btn-header{ href: new_account_path }= t :join_now + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + .mobile-menu-signin + %a.btn.btn-primary.btn-mobile-signin{ href: new_session_path } + %i.icon-user + = t :sign_in + %a.btn.btn-outline.btn-mobile-join-now{ href: new_account_path } + Join Now - if read_only_mode? %div diff --git a/app/views/shared/search_dingus/_search_bar.html.haml b/app/views/shared/search_dingus/_search_bar.html.haml index acc25067e..ad48ad78a 100644 --- a/app/views/shared/search_dingus/_search_bar.html.haml +++ b/app/views/shared/search_dingus/_search_bar.html.haml @@ -8,8 +8,10 @@ - else .col-md-5.col-sm-5.col-xs-5.no_padding - %label #{t('.search_text')}   - = text_field_tag :query, params[:query] + %label.sr-only= t('.search_text') + .search-input-group + = text_field_tag :query, params[:query], + placeholder: t('.search_text') - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-refresh + %button.btn.btn-refresh{ type: 'Submit' } + %i.icon-search diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e3160c2b9..b35d3c3ee 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,6 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js] + api/vulnerability.sass api/vulnerability.js + rotating_stats.js project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 9e5497ce0..3607a7749 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -3,24 +3,89 @@ en: index: page_title: 'Open Hub, the open source network' discover_message: 'Discover, Track and Compare Open Source' + worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' - whats_new: "What's New" counting: 'Counting %{count} lines of code' indexing: 'Indexing %{count} open source projects' connecting: 'Connecting %{count} open source contributors' tracking: 'Tracking %{count} source control repositories' search_projects: 'Search Projects...' + feature_directory_title: 'Massive Directory' + feature_directory_desc: 'Indexing over 2.5 million open source projects.' + feature_health_title: 'Assess Health' + feature_health_desc: 'Check activity levels and risk before you adopt.' + feature_portfolio_title: 'Manage Portfolio' + feature_portfolio_desc: 'Claim your commits and track your developer rankings.' + projects_label: 'Projects' + contributors_label: 'Contributors' + languages_label: 'Languages' + organizations_label: 'Organizations' top_lists: popular_projects: 'Most Popular Projects' active_projects: 'Most Active Projects' active_contributors: 'Most Active Contributors' recent_projects: 'Recently Added Projects' + show_more: 'Show More' + show_less: 'Show Less' + view_all_projects: 'View All Projects' join_now_home: - claim: 'Claim' - contributions: 'your contributions' - manage: 'Manage' - projects_data: "your project's data" - highlight: 'Highlight' - use: 'your use of FOSS' join_now: 'Join Now' + become_part_community: 'Become part of the community' + track_contributions_title: 'Track Your Contributions' + track_contributions_desc: 'Showcase your open source portfolio and contributions' + gain_recognition_title: 'Gain Recognition' + gain_recognition_desc: 'Build your developer profile with verified achievements' + create_account: 'Create Your Account' + + whats_new: + whats_new: "What's New" + latest_updates_reports: 'Latest updates and reports' + ossra_report_alt: 'Open Source Security and Risk Analysis Report' + + top_contributors: + top_contributors: 'Top Contributors' + most_active_developers: 'Most active contributors' + view_all_contributors: 'View All Contributors' + view_all: 'View all' + + user_journeys: + your_journey_with_openhub: 'Your Journey with Open Hub' + explore_ways_description: 'Explore the many ways you can discover, track, and contribute to open source projects' + key_steps: 'Key Steps' + + discover_compare_title: 'Discover & Compare Solutions' + discover_compare_desc: 'Find the perfect open source solution by comparing features, project activity, and community engagement side-by-side.' + discover_compare_step1: 'Compare projects side-by-side' + discover_compare_step2: 'Review activity metrics' + discover_compare_step3: 'Analyze community interest' + + assess_health_title: 'Assess Project Health' + assess_health_desc: 'Determine if a project is safe to adopt by analyzing commit frequency, number of contributors, and growth trends.' + assess_health_step1: 'Analyze commit history trends' + assess_health_step2: 'Review year-over-year growth' + assess_health_step3: 'Track number of contributors' + + manage_portfolio_title: 'Manage Your Developer Portfolio' + manage_portfolio_desc: 'Build a unified portfolio of your open source work by claiming your contributions across all repositories.' + manage_portfolio_step1: 'Claim your commits' + manage_portfolio_step2: 'Earn "Kudos" and rankings' + manage_portfolio_step3: 'Track your global rank' + + analyze_code_title: 'Analyze Code Composition' + analyze_code_desc: 'Get deep insights into the codebase architecture, including language breakdown, lines of code, and comment ratios.' + analyze_code_step1: 'View language distribution' + analyze_code_step2: 'Check comment-to-code ratios' + analyze_code_step3: 'Estimate effort (COCOMO model)' + + check_security_title: 'Check Security Vulnerabilities' + check_security_desc: 'Verify the security track record of a project by checking known vulnerabilities and confidence scores before you adopt.' + check_security_step1: 'View Security Confidence Index' + check_security_step2: 'Check reported vulnerabilities (CVEs)' + check_security_step3: 'Assess risk level' + + identify_opportunities_title: 'Identify Contribution Opportunities' + identify_opportunities_desc: 'Find projects that need your skills and identify the best repositories to start your open source journey.' + identify_opportunities_step1: 'Find beginner-friendly projects' + identify_opportunities_step2: 'Locate source repositories (GitHub/GitLab)' + identify_opportunities_step3: 'Track your contribution impact' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb index 04d2523cc..3e27b7716 100644 --- a/test/controllers/home_controller_test.rb +++ b/test/controllers/home_controller_test.rb @@ -9,7 +9,13 @@ class HomeControllerTest < ActionController::TestCase best_account_analysis.account.update(best_vita_id: best_account_analysis.id, created_at: 4.days.ago) account_analysis_fact = best_account_analysis.account_analysis_fact account_analysis_fact.update(last_checkin: Time.current) - Rails.cache.stubs(:fetch).returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-recently_active_accounts-cache').returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-person_count-cache').returns(1_000_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-active_project_count-cache').returns(2_500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-repository_count-cache').returns(500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-vita_count-cache').returns([]) + Rails.cache.stubs(:fetch).with('HomeDecorator-most_active_projects-cache').returns([]) + Rails.cache.stubs(:fetch).with('homepage_top_lists').returns([]) get :index assert_response :success diff --git a/test/controllers/licenses_controller_test.rb b/test/controllers/licenses_controller_test.rb index b2ac3ce27..e04bba48b 100644 --- a/test/controllers/licenses_controller_test.rb +++ b/test/controllers/licenses_controller_test.rb @@ -52,7 +52,7 @@ class LicensesControllerTest < ActionController::TestCase get :show, params: { id: @license.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e59b18bb3..fc1e35bab 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -83,7 +83,7 @@ class OrganizationsControllerTest < ActionController::TestCase get :show, params: { id: @organization.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'should support show page via xml api' do diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index def7e2c23..0c4a958ea 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -292,7 +292,7 @@ class ProjectsControllerTest < ActionController::TestCase get :show, params: { id: project.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'show accepts being called via api' do From 9000b8fb33f71318b965159fca5dc9ba2fbd2a81 Mon Sep 17 00:00:00 2001 From: bd-vaibhav Date: Fri, 6 Mar 2026 14:03:26 +0530 Subject: [PATCH 11/53] OTWO-7574 - Figma redesign for homepage (#1870) * OTWO-7574 - Figma redesign for homepage * OTWO-7574 Fixed card layout issues in homepage * OTWO-7574 Fixed card layout issue --- app/assets/javascripts/home.js.coffee | 2 +- app/assets/javascripts/rotating_stats.js | 29 --- app/assets/stylesheets/home.sass | 260 ++++++++++++--------- app/assets/stylesheets/home_search.sass | 64 ++--- app/assets/stylesheets/page.sass | 28 ++- app/views/home/_top_contributors.html.haml | 7 +- app/views/home/_top_lists.html.haml | 6 +- app/views/home/index.html.haml | 10 +- app/views/layouts/application.html.haml | 2 +- config/initializers/assets.rb | 2 +- 10 files changed, 211 insertions(+), 199 deletions(-) delete mode 100644 app/assets/javascripts/rotating_stats.js diff --git a/app/assets/javascripts/home.js.coffee b/app/assets/javascripts/home.js.coffee index 0e23b0b5b..560e1b882 100644 --- a/app/assets/javascripts/home.js.coffee +++ b/app/assets/javascripts/home.js.coffee @@ -2,7 +2,7 @@ $(document).on 'page:change', -> divs = $('p[id^="content-"]') i = 0 do -> - divs.eq(i).removeClass('hide').fadeIn(400).delay(6000).fadeOut 400, arguments.callee + divs.eq(i).removeClass('hide').fadeIn(200).delay(2500).fadeOut 200, arguments.callee i = ++i % divs.length $('#icon_text').click -> diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js deleted file mode 100644 index 39a368b7b..000000000 --- a/app/assets/javascripts/rotating_stats.js +++ /dev/null @@ -1,29 +0,0 @@ -// Rotating Stats Animation for Homepage -document.addEventListener('DOMContentLoaded', function() { - var globalStats = document.getElementById('global_statistics'); - - if (!globalStats) return; - - var statElements = globalStats.querySelectorAll('p'); - - if (statElements.length === 0) return; - - var currentIndex = 0; - - // Show first stat initially - statElements[0].classList.remove('hide'); - - // Rotate stats every 2 seconds - setInterval(function() { - // Hide current stat - statElements[currentIndex].classList.add('hide'); - - // Move to next stat - currentIndex = (currentIndex + 1) % statElements.length; - - // Show next stat after a brief delay - setTimeout(function() { - statElements[currentIndex].classList.remove('hide'); - }, 300); - }, 2000); -}); diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4978e69e3..8e46c6e06 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -4,15 +4,21 @@ // Main container with consistent 1280px max-width throughout // Isolated from global body font-size: 1.3rem override -.hero_section - padding: 0 16px 40px - background: #fff !important +// Common section background +.section_bg + background: #f9fafb !important transition: background 0.3s ease + html.dark & + background: #1D0631 !important + +.hero_section + @extend .section_bg + padding: 0 16px 4px box-sizing: border-box @media (min-width: 640px) - padding: 0 24px 40px + padding: 0 24px 16px @media (min-width: 1024px) - padding: 0 32px 40px + padding: 0 32px 16px // Dark mode support html.dark & background: #1D0631 !important @@ -107,12 +113,11 @@ // Old placeholder and btn_join_now styles removed - handled in new components .landing + @extend .section_bg margin-top: 20px margin-left: 0 padding: 32px 16px width: 100% - background: #f9fafb !important - transition: background 0.3s ease @media (min-width: 640px) padding: 32px 24px @media (min-width: 1024px) @@ -338,8 +343,9 @@ button padding: 12px 24px // Dark mode support - Yellow background with purple text html.dark & - background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) - border-color: #ffb91a + background: linear-gradient(90deg, #2D1548 0%, #2D1548 50%, #2D1548 100%) + border-color: rgba(255, 255, 255, 0.1) + .stat_item text-align: center @@ -354,9 +360,10 @@ button font-size: 18px @media (min-width: 768px) font-size: 20px - // Dark mode - Purple text on yellow background + // Dark mode - Yellow text on purple background html.dark & - color: #5A2A82 + color: #ffb91a + .stat_label font-size: 8px @@ -370,9 +377,10 @@ button font-size: 9px @media (min-width: 768px) font-size: 10px - // Dark mode - Purple text on yellow background + // Dark mode - Gray text on purple background html.dark & - color: #5A2A82 + color: #9ca3af + .stat_divider display: none @@ -383,17 +391,17 @@ button height: 32px background: #9ca3af opacity: 1 - // Dark mode - Purple divider on yellow background + // Dark mode - White divider with low opacity on purple background html.dark & @media (min-width: 768px) - background: #5A2A82 - opacity: 0.3 + background: #ffffff + opacity: 0.2 + // Content Section .content_section + @extend .section_bg padding: 32px 0 - background: white !important - transition: background 0.3s ease // Dark mode support html.dark & background: #1D0631 !important @@ -429,24 +437,22 @@ button // Top Contributors Card - Matches ContributorJourney.tsx .top_contributors_card - position: relative - padding: 3px + background: white border-radius: 16px - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - margin-bottom: 24px overflow: hidden - transition: box-shadow 0.3s + margin-bottom: 4px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s + display: flex + flex-direction: column &:hover - box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) html.dark & - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) - > * - background: white - border-radius: 16px - overflow: hidden - html.dark & - background: #1D0631 // Card Header .top_contributors_card .card_header @@ -456,7 +462,7 @@ button padding: 24px 24px 16px 24px background: white html.dark & - background: #1D0631 + background: #2D1548 .top_contributors_card .header_icon display: flex @@ -512,6 +518,30 @@ button display: flex flex-direction: column gap: 8px + max-height: 300px + overflow-y: scroll + scrollbar-width: thin + scrollbar-color: rgba(90, 42, 130, 0.4) rgba(90, 42, 130, 0.1) + @media (min-width: 768px) + max-height: 500px + &::-webkit-scrollbar + width: 8px + &::-webkit-scrollbar-track + background: rgba(90, 42, 130, 0.1) + border-radius: 3px + &::-webkit-scrollbar-thumb + background: rgba(90, 42, 130, 0.4) + border-radius: 3px + &:hover + background: rgba(90, 42, 130, 0.6) + html.dark & + scrollbar-color: rgba(255, 185, 26, 0.4) rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-track + background: rgba(255, 185, 26, 0.1) + &::-webkit-scrollbar-thumb + background: rgba(255, 185, 26, 0.4) + &:hover + background: rgba(255, 185, 26, 0.6) .top_contributors_card .contributor_item display: flex @@ -536,10 +566,8 @@ button background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) border-color: #5A2A82 -// Show only top 4 contributors on mobile -.top_contributors_card .contributor_item:nth-child(n+5) - @media (max-width: 767px) - display: none +// All contributors visible on mobile to enable scrolling +// Mobile users can scroll to see all contributors // Avatar .top_contributors_card .contributor_avatar @@ -662,51 +690,45 @@ button font-weight: 600 color: #5A2A82 text-decoration: none - border-top: 1px solid rgba(90, 42, 130, 0.2) - transition: color 0.2s + border: 2px solid rgba(90, 42, 130, 0.2) + border-radius: 12px + margin-top: 8px + transition: all 0.2s cursor: pointer - - span - border-bottom: 1px solid #5A2A82 + background: transparent &:hover color: #1D0631 - span - border-bottom-color: #1D0631 + border-color: #5A2A82 + background: rgba(90, 42, 130, 0.05) i font-size: 16px html.dark & - color: white !important - border-top-color: #ffb91a - border-top-width: 2px - border-radius: 12px - margin-top: 8px - span - border-bottom-color: white + color: #ffb91a !important + border-color: #ffb91a &:hover - color: white !important + background: #ffb91a + color: #1D0631 !important + // What's New Card - Figma Design .whats_new_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .card_header_section padding: 16px 16px 12px 16px !important @@ -715,7 +737,7 @@ button padding: 24px 24px 16px 24px !important // Dark mode support html.dark & - background: #1D0631 + background: #2D1548 .whats_new_title font-size: 18px !important @@ -768,24 +790,21 @@ button // Join Now Card - Figma Design (p-6 wrapper) .join_now_card - position: relative - padding: 3px + background: white border-radius: 16px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s + height: 100% + padding: 24px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: box-shadow 0.2s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - padding: 24px !important - // Dark mode support - html.dark & - background: #1D0631 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) + .join_header display: flex @@ -953,9 +972,12 @@ button // User Journeys Section .user_journeys_section background: #1D0631 - padding: 64px 0 + padding: 32px 0 !important + width: 100vw + margin-left: calc(-50vw + 50%) + margin-right: calc(-50vw + 50%) @media (max-width: 768px) - padding: 48px 0 + padding: 16px 0 !important // Matches Figma: max-w-7xl mx-auto .container @@ -998,10 +1020,10 @@ button .journey_card position: relative - padding: 3px border-radius: 12px overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + border: 0.8px solid #ffb91a + background: #1D0631 margin-bottom: 16px transition: all 0.3s @@ -1171,13 +1193,12 @@ button // Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 .project_swimlanes_section - padding: 32px 16px - background: #f9fafb - transition: background 0.3s ease + @extend .section_bg + padding: 8px 16px !important @media (min-width: 640px) - padding: 32px 24px + padding: 8px 24px !important @media (min-width: 1024px) - padding: 32px 32px + padding: 8px 32px !important html.dark & background: #1D0631 @@ -1226,29 +1247,36 @@ button // Compact Project Card Styles .compact_project_card position: relative - padding: 3px - border-radius: 8px + border-radius: 16px overflow: hidden transition: all 0.3s ease - background: transparent cursor: pointer + border: 1px solid rgba(229, 231, 235, 0.8) + background: white + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + display: flex + flex-direction: column + &:hover - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - .compact_card_inner - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + border-color: #5A2A82 + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) + + html.dark & + background: #2D1548 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + &:hover + border-color: #ffb91a + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) .compact_project_card .compact_card_inner - background: white - border-radius: 8px padding: 12px transition: all 0.3s ease height: 100% - border: 1px solid #e5e7eb + display: flex + flex-direction: column @media (min-width: 640px) padding: 16px - html.dark & - background: #1D0631 - border-color: #4b5563 .compact_project_card .compact_card_header display: flex @@ -1347,6 +1375,11 @@ button .compact_project_card .compact_card_description margin-bottom: 12px + height: 42px + display: flex + align-items: center + @media (min-width: 640px) + height: 44px p font-size: 12px color: #6b7280 @@ -1432,7 +1465,6 @@ button .hidden_card width: 100% - margin-bottom: 16px // Show More Button .show_more_btn @@ -1442,7 +1474,7 @@ button color: #5A2A82 border: 2px solid #5A2A82 padding: 12px 16px - border-radius: 8px + border-radius: 12px font-weight: 600 cursor: pointer transition: all 0.3s ease @@ -1453,11 +1485,11 @@ button color: white html.dark & background: transparent - color: white - border-color: white + color: #ffb91a + border-color: #ffb91a &:hover - background: white - color: #5A2A82 + background: #ffb91a + color: #1D0631 // Pagination Controls .pagination_controls @@ -1516,7 +1548,7 @@ button // View All Projects Button - Matches Figma: pt-4 pb-8 .view_all_container - padding-top: 16px + padding-top: 32px padding-bottom: 32px padding-left: 16px padding-right: 16px @@ -1540,8 +1572,8 @@ button gap: 8px padding: 12px 24px background: #ffb91a - color: #000000 - border-radius: 8px + color: #1D0631 + border-radius: 12px font-weight: 600 font-size: 14px text-decoration: none @@ -1552,10 +1584,10 @@ button &:hover background: rgba(255, 185, 26, 0.9) box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) - color: #000000 + color: #1D0631 html.dark & - color: #ffffff !important + color: #1D0631 !important &:hover - color: #ffffff !important + color: #1D0631 !important i font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass index b1c0d45e8..b873b26f5 100644 --- a/app/assets/stylesheets/home_search.sass +++ b/app/assets/stylesheets/home_search.sass @@ -16,34 +16,34 @@ width: 100% display: block - // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) - // Positioned to align with input text baseline - .search_icon - position: absolute - top: 50% - left: 8px - transform: translateY(-60%) - display: block - width: 14px - height: 14px - font-size: 14px - color: #9ca3af - pointer-events: none - z-index: 10 - transition: color 0.3s ease - text-align: center - font-style: normal - @media (min-width: 640px) - left: 24px - width: 20px - height: 20px - font-size: 20px - // Dark mode support - html.dark & - color: #6b7280 - // Ensure Font Awesome icon centers properly - &::before - display: block + // Search Icon Wrapper - Figma Design +.search_icon_wrapper + position: absolute + top: 0 + bottom: 0 + left: 8px + display: flex + align-items: center + pointer-events: none + @media (min-width: 640px) + left: 24px + +// Search Icon - Figma specs +.search_icon + width: 14px + height: 14px + font-size: 14px + line-height: 0.5 + color: #9ca3af + transition: color 0.3s ease + @media (min-width: 640px) + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 input.search_input @@ -131,15 +131,15 @@ form#search_form padding: 16px 16px 16px 56px !important font-size: 16px !important &:focus - border-color: #0E4B7A !important + border-color: #5A2A82 !important box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important outline: none !important // Dark mode support html.dark & background: #1D0631 !important color: #ffffff !important - border-color: #4b5563 !important + border-color: #5A2A82 !important &:focus - border-color: #2E8B9E !important + border-color: #ffb91a !important background: #1D0631 !important - box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important + box-shadow: 0 0 0 4px rgba(255, 185, 26, 0.1) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cb5fd0334..994ad5244 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -18,13 +18,17 @@ body color: #000 width: 100% overflow-x: hidden + background-color: #f9fafb !important .container#page, #page width: 100% !important max-width: 100% !important - background-color: white + background-color: #f9fafb margin: 0 !important padding: 0 !important + transition: background-color 0.3s ease + html.dark & + background-color: #1D0631 header // Figma design: gradient background for entire header @@ -313,7 +317,9 @@ header .navbar // Figma design: gradient background with purple theme - position: relative + position: sticky + top: 0 + z-index: 1000 background: linear-gradient(to right, #000000, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) @@ -603,16 +609,17 @@ header .mobile-menu display: none width: 100% - flex-basis: 100% background: linear-gradient(to right, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) border-top: 1px solid rgba(255, 255, 255, 0.15) padding: 20px 0 - position: relative + position: absolute + top: 64px + left: 0 + right: 0 z-index: 1002 overflow: visible - order: 2 box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) &.show display: block !important @@ -694,7 +701,7 @@ header background-color: #FFB91A !important color: #000 !important border: none !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -720,7 +727,7 @@ header background-color: transparent !important color: #FFB91A !important border: 2px solid #FFB91A !important - border-radius: 8px + border-radius: 12px font-weight: 600 font-size: 15px text-decoration: none @@ -736,6 +743,13 @@ header &:active transform: translateY(0) box-shadow: none + html.dark & + background-color: transparent !important + color: #FFB91A !important + border-color: #FFB91A !important + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + .dropdown#logged_user_menu position: relative diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml index d87f62121..1544ad3b9 100644 --- a/app/views/home/_top_contributors.html.haml +++ b/app/views/home/_top_contributors.html.haml @@ -22,14 +22,9 @@ %span.contributor_rank= "Rank ##{index + 1}" .contributor_stats - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 - - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project - - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 %span.stat_commits %i.fa.fa-dot-circle-o - = number_with_delimiter(commits) - %span.stat_projects - %i.fa.fa-code - = "#{project_count} projects" + = "#{number_with_delimiter(commits)} commits" .contributor_arrow %i.fa.fa-chevron-right diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index e0d18cc2b..02db80a5a 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -19,7 +19,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_active_projects.size > 2 - - @home.most_active_projects.drop(2).each do |project| + - @home.most_active_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -44,7 +44,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_popular_projects.size > 2 - - @home.most_popular_projects.drop(2).each do |project| + - @home.most_popular_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } @@ -69,7 +69,7 @@ / Mobile: Hidden cards (to be shown on click) - if @home.most_recent_projects.size > 2 - - @home.most_recent_projects.drop(2).each do |project| + - @home.most_recent_projects[2..9].each do |project| .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } = render partial: 'compact_project_card', locals: { project: project } diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index a9cfb02b6..82762ba3e 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,6 +1,5 @@ - content_for(:html_title) { t('.page_title') } - content_for(:javascript) do - = javascript_include_tag 'rotating_stats' = javascript_include_tag 'project_swimlanes' / Hero Section @@ -16,7 +15,8 @@ %form.search_form#search_form{ action: projects_path } %input{ type: :hidden, name: 'ref', value: 'homepage' } .search_input_wrapper - %i.fa.fa-search.search_icon + .search_icon_wrapper + %i.fa.fa-search.search_icon %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } / Rotating Stats @@ -76,8 +76,8 @@ = render partial: '/home/whats_new' = render partial: '/home/join_now_home' -/ User Journeys Section -= render partial: '/home/user_journeys' - / Project Swimlanes Section = home_top_lists + +/ User Journeys Section += render partial: '/home/user_journeys' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7c5f3dbe8..a8438e724 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -18,8 +18,8 @@ %body{ zoom: 1 } = yield :session_projects_banner + %header= render partial: 'layouts/partials/header' .container#page - %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear %footer= render partial: 'layouts/partials/footer' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index b35d3c3ee..052e2ce67 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -7,5 +7,5 @@ Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css api/vulnerability.sass api/vulnerability.js - rotating_stats.js project_swimlanes.js] + project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] From f24f3af018b12e577af37d449f9995a83521a2ab Mon Sep 17 00:00:00 2001 From: Niharika1117 <101638919+Niharika1117@users.noreply.github.com> Date: Fri, 6 Mar 2026 14:41:15 +0530 Subject: [PATCH 12/53] OTWO-7557 project show page design modification (#1871) * OTWO-7557 project show page design modification * OTWO-7557 fix on the css for project show page --- app/assets/stylesheets/charts.sass | 8 +- app/assets/stylesheets/dark_theme.sass | 20 + app/assets/stylesheets/page.sass | 40 +- .../stylesheets/project_show_redesign.sass | 3055 +++++++++++++++++ app/decorators/analysis/chart.rb | 2 +- .../analysis/commit_history_chart.rb | 13 +- .../analysis/contributor_history_chart.rb | 2 + app/decorators/vulnerability/version_chart.rb | 4 +- .../responsive_project_layout.html.haml | 4 +- app/views/projects/show.html.haml | 503 ++- .../show/_analysis_summary_redesign.html.haml | 104 + .../show/_community_rating_redesign.html.haml | 28 + ...unity_recent_committers_redesign.html.haml | 21 + .../projects/show/_header_redesign.html.haml | 128 + .../show/_languages_redesign.html.haml | 20 + .../show/_licenses_redesign.html.haml | 93 + app/views/projects/show/_security.html.haml | 100 +- .../analysis/options_based_on_type.yml | 12 +- config/locales/projects.en.yml | 90 +- 19 files changed, 4141 insertions(+), 106 deletions(-) create mode 100644 app/assets/stylesheets/project_show_redesign.sass create mode 100644 app/views/projects/show/_analysis_summary_redesign.html.haml create mode 100644 app/views/projects/show/_community_rating_redesign.html.haml create mode 100644 app/views/projects/show/_community_recent_committers_redesign.html.haml create mode 100644 app/views/projects/show/_header_redesign.html.haml create mode 100644 app/views/projects/show/_languages_redesign.html.haml create mode 100644 app/views/projects/show/_licenses_redesign.html.haml diff --git a/app/assets/stylesheets/charts.sass b/app/assets/stylesheets/charts.sass index 9de5968b6..3fd13b400 100644 --- a/app/assets/stylesheets/charts.sass +++ b/app/assets/stylesheets/charts.sass @@ -398,10 +398,16 @@ .highcharts-tooltip .highcharts-color-9 @include project-contributions-color-9-fill -#code_analysis_chart, #activity_chart, #community_chart +#code_analysis_chart, #community_chart .highcharts-graph @include code-analysis-chart-color-0-stroke +#activity_chart + .highcharts-graph, + .highcharts-point + stroke: #5A2A82 + fill: #5A2A82 + #committer_history_chart .highcharts-graph @include code-analysis-chart-color-0-stroke diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 9a7f3d844..5b8198f4f 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -148,3 +148,23 @@ html.dark .good color: #4ade80 !important + + // Project security: Did You Know block (legacy and redesign templates) + #project_container .project_row #did_you_know + background: #1D0631 !important + + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + &:hover + color: #93c5fd !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index 994ad5244..af7da55f2 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -130,7 +130,6 @@ header gap: 24px #page_contents max-width: 100% - padding: 0 20px .separator-div @include site-separator-color @@ -151,7 +150,6 @@ header padding-left: 0 margin-left: -1rem .thirtyfive_project_activity_text - margin-top: 5.5rem line-height: 1.3rem .thirtyfive_project_activity_level_na top: 1rem @@ -1137,6 +1135,44 @@ fieldset .language_percentage_indicator width: 3rem +// Dark mode guards for legacy project security/no-analysis blocks +html.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + + h4 + color: #f8fafc !important + + p, li, span, .indent + color: #cbd5e1 !important + + a + color: #60a5fa !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + +body.dark + #project_container + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + // Full-width layout styles .page-content-wrapper width: 100% diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass new file mode 100644 index 000000000..8509ed849 --- /dev/null +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -0,0 +1,3055 @@ +// Project Show Page Redesign - Clean Implementation Based on Figma Design +// ============================================================================ + +// Design System Variables +// ============================================================================ +$purple-dark: #1D0631 +$purple-primary: #5A2A82 +$purple-light: #9F7ABA +$yellow-accent: #FFB91A +$yellow-hover: #FFCC4D + +// Neutrals +$gray-50: #f9fafb +$gray-100: #f3f4f6 +$gray-200: #e5e7eb +$gray-300: #d1d5db +$gray-400: #9ca3af +$gray-500: #6b7280 +$gray-600: #4b5563 +$gray-700: #374151 +$gray-900: #111827 + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05) +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) + +// System Font Stack +$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + +// ============================================================================ +// Global Overrides - Remove Bootstrap padding/margins +// ============================================================================ + +// Remove navbar margin globally with maximum specificity +.navbar, +.navbar-default, +.navbar-static-top, +nav.navbar, +header .navbar, +.navbar-fixed-top, +.navbar-fixed-bottom + margin-bottom: 0 !important + margin-top: 0 !important + +// Additional Bootstrap navbar overrides +body .navbar + margin-bottom: 0 !important + +// Target specific header navbar +header + .navbar + margin-bottom: 0 !important + +// Remove container and page padding when showing redesigned project page +body:has(.project-show-redesign) + #project_container, + .container-fluid#project_container + padding: 0 !important + margin: 0 !important + max-width: 100% !important + + #page-contents, + .row#page-contents + margin: 0 !important + padding: 0 !important + + > div + padding: 0 !important + +// Direct child approach - more specific +#project_container + > .row#page-contents + > .col-xs-12, + > .col-sm-12, + > .col-md-12 + padding: 0 !important + + > .project-show-redesign + margin: 0 !important + +// Force remove padding from all Bootstrap column classes in project container +.project-show-redesign + ~ * + .container, + .container-fluid, + .row, + [class*="col-"] + padding-left: 0 !important + padding-right: 0 !important + +// ============================================================================ +// Main Container +// ============================================================================ +.project-show-redesign + width: 100% + min-height: 100vh + background-color: $gray-50 + font-family: $font-family + -webkit-font-smoothing: antialiased + -moz-osx-font-smoothing: grayscale + margin: 0 !important + + // Hide old project header elements + #project_masthead, + #project_header, + #project_icon, + #widgets, + #project_header_activity_indicator + display: none !important + + // Reset global styles that interfere + * + box-sizing: border-box + + // Override any legacy project container color styles + color: $gray-900 + + a + color: $purple-primary + + &:hover + color: $purple-primary + + // ============================================================================ + // Project Header Section + // ============================================================================ + .project-header-gradient + background: linear-gradient(135deg, $purple-dark 0%, $purple-primary 100%) + padding: 50px 16px 32px + position: relative + overflow: hidden + + // Decorative gradient circle + &::before + content: '' + position: absolute + top: -192px + right: -192px + width: 384px + height: 384px + background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%) + border-radius: 50% + + .header-content + margin: 0 auto + position: relative + z-index: 10 + + .header-main + display: flex + flex-direction: column + gap: 16px + + @media (min-width: 768px) + flex-direction: row + align-items: flex-start + justify-content: space-between + gap: 24px + + a, + button + font-family: $font-family + + // Desktop: Top-right user stats and button + .header-top-right + position: absolute + top: 49px + right: 16px + display: none + align-items: center + gap: 12px + z-index: 20 + + @media (min-width: 640px) + display: flex + right: 24px + + @media (min-width: 1024px) + right: 32px + + .stats-card + background: white + border-radius: 8px + padding: 12px 16px + box-shadow: $shadow-md + min-width: 140px + + .activity-indicator-wrapper + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 8px + min-width: 80px + + [class^='thirtyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + transform: scale(1.25) + transform-origin: center center + + .thirtyfive_project_activity_text + margin: 0 + width: auto + line-height: 1.2 + font-size: 12px + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card + .stats-inner + display: flex + align-items: center + gap: 12px + margin-bottom: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .user-count + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + + .i-use-this-btn + width: 100% + padding: 10px 16px + border-radius: 6px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + transition: all 0.2s + + &.using + background: $purple-primary + color: white + + &:hover + background: darken($purple-primary, 8%) + + &.not-using + background: $yellow-accent + color: $purple-dark + + &:hover + background: $yellow-hover + + svg + width: 16px + height: 16px + + // Project title and info section + .project-title-section + flex: 1 + min-width: 0 + max-width: 900px + + @media (min-width: 768px) + max-width: calc(100% - 280px) + + h1 + font-size: 32px + font-weight: 700 + color: white + line-height: 1.2 + letter-spacing: -0.02em + margin: 0 0 12px + + @media (min-width: 768px) + font-size: 48px + + @media (min-width: 1024px) + font-size: 48px + + .action-buttons + display: flex + flex-wrap: wrap + gap: 8px + margin-bottom: 20px + + .action-btn + display: inline-flex + align-items: center + gap: 6px + padding: 6px 12px + border-radius: 8px + font-size: 12px + font-weight: 500 + line-height: 1.25 + text-decoration: none !important + appearance: none + -webkit-appearance: none + background-image: none !important + box-shadow: none !important + border: 0 + cursor: pointer + transition: all 0.2s ease + + &:link, + &:visited, + &:hover, + &:active + color: white !important + text-decoration: none !important + + @media (min-width: 768px) + font-size: 14px + + svg + width: 14px + height: 14px + flex-shrink: 0 + stroke: currentColor + + // Outlined button style (for Settings and Report Duplicate) + &.action-btn-outline + background: rgba(255, 255, 255, 0.1) + border: 1px solid rgba(255, 255, 255, 0.2) + color: white !important + box-shadow: none + + &:hover + background: rgba(255, 255, 255, 0.2) + border-color: rgba(255, 255, 255, 0.2) + color: white !important + transform: none + + &:active + transform: none + background: rgba(255, 255, 255, 0.2) + + &:focus-visible + outline: 2px solid rgba(255, 255, 255, 0.55) + outline-offset: 2px + + .project-description + font-size: 16px + color: rgba(255, 255, 255, 0.9) + line-height: 1.6 + margin-bottom: 12px + + @media (min-width: 768px) + font-size: 17px + + .timestamp + font-size: 14px + color: rgba(255, 255, 255, 0.7) + line-height: 1.5 + + // Mobile: Bottom stats card + .header-mobile-stats + display: flex + gap: 12px + margin-top: 24px + + @media (min-width: 640px) + display: none + + .activity-card-mobile + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 4px + padding: 10px 12px + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + + [class^='twentyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + + .twentyfive_project_activity_text + margin: 0 + width: auto + min-height: 0 + font-size: 10px + line-height: 1.2 + font-weight: 600 + color: white + text-align: center + white-space: nowrap + + .stats-card-mobile + flex: 1 + display: flex + flex-direction: column + background: rgba(255, 255, 255, 0.1) + backdrop-filter: blur(10px) + border: 1px solid rgba(255, 255, 255, 0.2) + border-radius: 8px + padding: 0 + overflow: hidden + + .stats-top + display: flex + align-items: center + gap: 8px + margin-bottom: 0 + padding: 10px 12px 6px + + svg + width: 16px + height: 16px + color: $yellow-accent + + .user-count + font-size: 18px + font-weight: 700 + color: $yellow-accent + + .users-label + font-size: 12px + color: rgba(255, 255, 255, 0.8) + + .i-use-this-btn-mobile + width: calc(100% - 20px) + margin: 0 10px 10px + padding: 8px 12px + border-radius: 8px + font-weight: 600 + font-size: 14px + display: flex + align-items: center + justify-content: center + gap: 8px + border: none + cursor: pointer + line-height: 1.25 + + &.using + background: $purple-primary + color: white + + &.not-using + background: $yellow-accent + color: $purple-dark + + svg + width: 16px + height: 16px + + // ============================================================================ + // Main Content Area + // ============================================================================ + .project-content + margin: 0 auto + padding: 24px + + @media (min-width: 768px) + padding: 32px + + // ============================================================================ + // Stats Grid (3 columns) + // ============================================================================ + .stats-grid + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + + .stat-card + background: white + border-radius: 12px + padding: 20px 16px + text-align: center + box-shadow: $shadow-sm + transition: box-shadow 0.2s + + &:hover + box-shadow: $shadow-md + + .stat-value + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + margin-bottom: 8px + + @media (min-width: 768px) + font-size: 36px + + .stat-label + font-size: 14px + color: $gray-600 + font-weight: 500 + line-height: 1.2 + + // ============================================================================ + // Card Components + // ============================================================================ + .gradient-card + background: white + border-radius: 12px + // Enhanced shadow to match Figma design - more prominent + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + overflow: hidden + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + .card-content + padding: 20px + + @media (min-width: 768px) + padding: 24px + + h3 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + line-height: 1.3 + margin: 0 0 16px !important + padding-bottom: 12px + border-bottom: 1px solid $gray-200 + + h4 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + line-height: 1.4 + margin: 0 0 12px !important + + p + font-size: 14px !important + line-height: 1.5 + color: $gray-700 !important + margin: 0 0 16px !important + + &:last-child + margin-bottom: 0 + + &.note + font-size: 12px !important + color: $gray-500 !important + font-style: italic + + ul + list-style: none + padding: 0 + margin: 0 + + // ============================================================================ + // Desktop: 3-column grid + // ============================================================================ + .three-col-grid + display: none + + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + margin-bottom: 32px + + // ============================================================================ + // Mobile: Accordion cards + // ============================================================================ + .mobile-accordion + display: flex + flex-direction: column + gap: 16px + margin-bottom: 32px + + @media (min-width: 1024px) + display: none + + .accordion-card + .accordion-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 20px + background: none + border: none + cursor: pointer + text-align: left + + .header-left + display: flex + align-items: center + gap: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + + h3 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin: 0 !important + + .chevron + width: 20px + height: 20px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content + padding: 0 20px 20px + display: none + + &.expanded + display: block + + // ============================================================================ + // Tags + // ============================================================================ + #project_tags, + #project_tags_mobile + margin-top: 24px + + h4 + font-size: 16px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + color: $gray-900 !important + + .tags-container + display: flex + flex-wrap: wrap + gap: 8px + + .tag + padding: 4px 12px + background: $gray-100 + color: $gray-900 !important + font-size: 14px + border-radius: 6px + text-decoration: none + transition: all 0.15s + + &:hover + background: $purple-primary + color: white !important + + // ============================================================================ + // Nutshell List + // ============================================================================ + .nutshell-list + list-style: none + padding: 0 + margin: 0 + display: flex + flex-direction: column + gap: 12px + + li + display: flex + gap: 12px + padding-top: 12px + font-size: 14px !important + line-height: 1.5 + border-top: 1px solid $gray-100 + color: $gray-700 !important + + &:first-child + border-top: none + padding-top: 0 + + svg + width: 16px + height: 16px + color: $purple-primary !important + flex-shrink: 0 + margin-top: 2px + + .content + color: $gray-700 !important + flex: 1 + + a + color: $purple-primary !important + font-weight: 600 + text-decoration: none + + &:hover + text-decoration: underline + color: $purple-primary !important + + .font-semibold, + span.font-semibold + font-weight: 600 + color: $gray-700 !important + + // ============================================================================ + // Quick Reference List + // ============================================================================ + .quick-ref-list + list-style: none + padding: 0 + margin: 0 + display: flex + flex-direction: column + gap: 12px + + li + display: flex + justify-content: space-between + align-items: flex-start + gap: 16px + padding-bottom: 12px + font-size: 14px !important + border-bottom: 1px solid $gray-100 + + &:last-child + border-bottom: none + padding-bottom: 0 + + .label + color: $gray-600 !important + font-weight: 500 !important + font-size: 14px !important + flex-shrink: 0 + line-height: 1.5 + + .value + color: $purple-primary !important + font-weight: 600 !important + font-size: 14px !important + text-align: right + flex: 1 + line-height: 1.5 + + a + color: inherit !important + text-decoration: none + display: inline-flex + align-items: center + gap: 4px + font-size: 14px !important + font-weight: 600 !important + + &:hover + text-decoration: underline + color: inherit !important + + svg + width: 12px + height: 12px + flex-shrink: 0 + color: currentColor !important + + i + font-size: 14px !important + color: $purple-primary !important + font-weight: 600 !important + + // Maximum specificity overrides for Quick Reference to prevent legacy interference + #page #project_container .project-show-redesign + .gradient-card .card-content h3 + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + + .quick-ref-list li + font-size: 14px !important + + .label + font-size: 14px !important + font-weight: 500 !important + color: $gray-600 !important + + .value + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + i + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + + .mobile-accordion .accordion-card .accordion-header .header-left h3 + font-size: 16px !important + font-weight: 600 !important + + // Languages section override + .languages-content .language-legend .language-item + font-size: 12px !important + + .language-name + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + font-size: 12px !important + + .language-percent + color: $gray-500 !important + font-size: 12px !important + + // Summary sections override + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + + // ============================================================================ + // Even more specific overrides for Activity card summary sections + // ============================================================================ + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin-bottom: 4px !important + + p.summary-timespan + font-size: 12px !important + color: #9ca3af !important + margin-bottom: 8px !important + + p.summary-stat + font-size: 12px !important + color: #374151 !important + margin-bottom: 4px !important + + span.stat-number + font-weight: 600 !important + color: #111827 !important + + p.summary-change + font-size: 12px !important + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + + // ============================================================================ + // Licenses Section + // ============================================================================ + .licenses-section + margin-bottom: 32px + + @media (min-width: 768px) + margin-bottom: 48px + + .section-title + display: block + visibility: visible + opacity: 1 + font-size: 20px + font-weight: 600 + color: $gray-900 + margin-bottom: 12px + line-height: 1.3 + + @media (min-width: 768px) + font-size: 24px + margin-bottom: 16px + + .license-card + margin-bottom: 16px + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + &:last-child + margin-bottom: 0 + + .license-header + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 16px + background: none + border: none + cursor: pointer + text-align: left + + @media (min-width: 768px) + padding: 24px + + @media (min-width: 1024px) + cursor: default + + .header-left + display: flex + align-items: center + gap: 8px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + @media (min-width: 768px) + width: 20px + height: 20px + + h4 + font-size: 16px + font-weight: 600 + color: $purple-primary + margin: 0 + line-height: 1.3 + + @media (min-width: 768px) + font-size: 18px + + .chevron + width: 20px + height: 20px + color: $gray-500 + transition: transform 0.2s + flex-shrink: 0 + + @media (min-width: 1024px) + display: none + + &.expanded + transform: rotate(180deg) + + .license-content + display: none + border-top: 1px solid $gray-200 + padding: 16px + + &.expanded + display: block + + @media (min-width: 768px) + padding: 24px + + @media (min-width: 1024px) + display: block + + .license-details-grid + display: grid + gap: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 24px + + .license-detail-card + border-radius: 8px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + + @media (min-width: 768px) + padding: 20px + + &.permitted + background: linear-gradient(to bottom right, #faf5ff 0%, #f3e8ff 100%) + + h5 + color: $purple-primary + + svg + color: $purple-primary + + li + color: $gray-700 + + svg + color: $purple-primary + + &.forbidden + background: linear-gradient(to bottom right, #fff7ed 0%, #fed7aa 100%) + + h5 + color: #c2410c + + svg + color: #c2410c + + li + color: $gray-700 + + svg + color: #c2410c + + &.required + background: linear-gradient(to bottom right, #fefce8 0%, #fef3c7 100%) + + h5 + color: #a16207 + + svg + color: #a16207 + + li + color: $gray-700 + + svg + color: #a16207 + + h5 + font-size: 14px + font-weight: 600 + margin-bottom: 12px + display: flex + align-items: center + gap: 8px + + svg + width: 16px + height: 16px + flex-shrink: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + li + font-size: 14px + margin-bottom: 8px + display: flex + align-items: center + gap: 8px + line-height: 1.4 + + &:last-child + margin-bottom: 0 + + svg + width: 12px + height: 12px + flex-shrink: 0 + margin-top: 0 + + .license-disclaimer + margin-top: 20px + padding-top: 16px + border-top: 1px solid $gray-200 + text-align: center + + @media (min-width: 768px) + margin-top: 24px + + p + font-size: 12px + color: $gray-500 + font-style: italic + margin: 0 + line-height: 1.5 + + .view-all-link + margin-top: 16px + text-align: right + + a + font-size: 14px !important + font-weight: 500 !important + color: $purple-primary !important + text-decoration: none + + &:visited, + &:active + color: $purple-primary !important + + &:hover + text-decoration: underline + color: $purple-primary !important + + // ============================================================================ + // Project Security Section + // ============================================================================ + .security-section + margin-bottom: 32px + + @media (min-width: 768px) + margin-bottom: 48px + + .section-title + font-size: 20px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + + @media (min-width: 768px) + font-size: 24px !important + margin-bottom: 16px !important + + .security-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 16px + + @media (min-width: 768px) + gap: 24px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .card-subtitle + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 16px !important + line-height: 1.4 + + @media (min-width: 768px) + font-size: 16px !important + + .security-link + color: $gray-900 !important + text-decoration: none + + &:hover + color: $purple-primary !important + text-decoration: underline + + .subtitle-detail + visibility: visible !important + opacity: 1 !important + display: block !important + font-size: 12px !important + font-weight: 400 !important + color: #6b7280 !important + margin-top: 4px + + @media (min-width: 768px) + display: inline !important + margin-left: 8px + + #vulnerability_version_chart + .chart-container + min-height: 250px + background: $gray-50 + border-radius: 8px + padding: 16px + + .no-data + text-align: center + padding: 60px 20px + color: $gray-600 + font-size: 14px + + // Vulnerability Report Indices + .vulnerability-indices + display: flex + flex-direction: column + gap: 24px + margin-top: 16px + + .index-section + .index-title + display: block !important + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + font-weight: 500 !important + color: #4b5563 !important + margin-bottom: 8px !important + line-height: 1.4 + + .gradient-bar-container + position: relative + height: 32px + margin-bottom: 8px + + @media (min-width: 768px) + height: 40px + + .gradient-bar + width: 100% + height: 100% + background: linear-gradient(to right, #5A2A82 0%, #ffb91a 50%, #f59e0b 100%) + border-radius: 8px + + .indicator-pointer + position: absolute + top: 0 + width: 4px + height: 100% + background: #1D0631 + border-radius: 9999px + cursor: pointer + transition: all 0.2s + + &:hover + background: $gray-900 + + .index-labels + display: flex + justify-content: space-between + font-size: 12px + color: #6b7280 + + .label-left, + .label-right + max-width: 45% + + .report-link-container + margin-top: 16px + text-align: center + + .report-link + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + // Did You Know Card + .did-you-know-card + background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%) + border-radius: 12px + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.04) + transition: box-shadow 0.2s + + &:hover + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.06) + + &.single + max-width: 600px + margin: 0 auto + + .card-content + padding: 20px + + @media (min-width: 768px) + padding: 24px + + .did-you-know-title + display: flex !important + visibility: visible !important + opacity: 1 !important + font-size: 16px !important + font-weight: 600 !important + color: #78350f !important + margin-bottom: 12px !important + align-items: center + gap: 8px + + @media (min-width: 768px) + font-size: 18px !important + + svg + width: 20px !important + height: 20px !important + color: #78350f !important + + .feature-list + list-style: disc + padding-left: 24px + margin: 0 + display: flex + flex-direction: column + gap: 8px + + li + visibility: visible !important + opacity: 1 !important + font-size: 14px !important + color: #78350f !important + line-height: 1.5 + + .security-footer-link + margin-top: 16px + text-align: right + + a + font-size: 14px !important + font-weight: 600 !important + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px + + // ============================================================================ + // Footer Navigation + // ============================================================================ + .footer-nav-section + background: white + border-radius: 16px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + margin-bottom: 24px + + @media (min-width: 640px) + padding: 32px + margin-bottom: 32px + + a + &:link, + &:visited, + &:hover, + &:active + text-decoration: none + + // Desktop: 4-column grid + .footer-nav-grid + display: none + + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(4, 1fr) + gap: 32px + + .nav-column + .column-header + display: flex + align-items: center + gap: 8px + margin-bottom: 12px + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + svg + width: 16px + height: 16px + color: $purple-primary + + h4 + font-size: 14px + font-weight: 600 + color: $gray-900 + margin: 0 + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 8px + + &:last-child + margin-bottom: 0 + + a + font-size: 14px + color: #5A2A82 !important + text-decoration: none + transition: color 0.15s + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + + // Mobile: Accordion + .footer-nav-accordion + display: flex + flex-direction: column + + @media (min-width: 768px) + display: none + + .accordion-item + border-bottom: 1px solid $gray-200 + + &:last-child + border-bottom: none + + .accordion-header-btn + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 12px 0 + background: none + border: none + cursor: pointer + + .header-left + display: flex + align-items: center + gap: 10px + + svg + width: 16px + height: 16px + color: $purple-primary + + span + font-size: 14px + font-weight: 600 + color: $gray-900 + + .chevron + width: 16px + height: 16px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content-footer + padding-bottom: 12px + padding-left: 24px + display: none + + &.expanded + display: block + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 6px + + a + font-size: 14px + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + +// ============================================================================ +// Dark Theme Overrides (Project Show Redesign) +// ============================================================================ +html.dark + .project-show-redesign + background-color: $purple-dark + color: #e2e8f0 !important + + // Override any legacy color styles + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .project-content + background: transparent + + .stats-grid + .stat-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .stat-value + color: $yellow-accent + + .stat-label + color: #94a3b8 + + .gradient-card + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .card-content + h3 + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) + + h4 + color: white !important + + p + color: #cbd5e1 !important + + &.note + color: #94a3b8 !important + + a + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .mobile-accordion + .accordion-card + .accordion-header + .header-left + svg + color: $yellow-accent + + h3 + color: #f8fafc + + .chevron + color: #94a3b8 + + #project_tags, + #project_tags_mobile + h4 + color: white !important + + svg + color: white !important + + .tags-container + .tag + background: $purple-dark + color: $gray-300 !important + + &:hover + background: $yellow-accent + color: $purple-dark !important + + .nutshell-list + li + border-top-color: rgba(90, 42, 130, 0.2) + color: $gray-300 !important + + svg + color: $yellow-accent !important + + .content + color: $gray-300 !important + + a + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .font-semibold, + span.font-semibold + color: $gray-300 !important + + .quick-ref-list + li + border-bottom-color: rgba(90, 42, 130, 0.2) + + .label + color: $gray-400 !important + font-size: 14px !important + font-weight: 500 !important + + .value + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + a + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + i + color: $yellow-accent !important + font-size: 14px !important + font-weight: 600 !important + + // Maximum specificity dark mode overrides + html.dark #page #project_container .project-show-redesign + .gradient-card .card-content h3 + color: white !important + + .quick-ref-list li + .label + color: $gray-400 !important + font-size: 14px !important + + .value + color: $yellow-accent !important + font-size: 14px !important + + a, + i + color: $yellow-accent !important + font-size: 14px !important + + // Languages section dark mode override + .languages-content .language-legend .language-item + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + // Summary sections dark mode override + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Even more specific dark mode overrides for Activity card summary sections + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .summary-grid .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #6b7280 !important + + p.summary-stat + color: #d1d5db !important + + span.stat-number + color: white !important + + p.summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + // Maximum specificity for rating section + #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + font-size: 12px !important + color: #374151 !important + + .rating-link + color: #5A2A82 !important + + .rating-display + .rating-value + font-size: 12px !important + color: #111827 !important + font-weight: 600 !important + + // Maximum specificity dark mode for rating section + html.dark #page #project_container .project-show-redesign .analysis-grid .gradient-card.analysis-card .card-content + .ratings-section + .rating-content + .rating-info + color: #d1d5db !important + + .rating-link + color: #FFB91A !important + + .rating-display + .rating-value + color: white !important + + html.dark + .licenses-section + .section-title + color: white + + .license-card + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) + + .header-left + svg + color: $yellow-accent + + h4 + color: $yellow-accent + + .chevron + color: #94a3b8 + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) + + .license-details-grid + .license-detail-card + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) + + h5 + color: $yellow-accent + + svg + color: $yellow-accent + + li + color: $gray-300 + + svg + color: $yellow-accent + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) + + h5 + color: #fb923c + + svg + color: #fb923c + + li + color: $gray-300 + + svg + color: #fb923c + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) + + h5 + color: #facc15 + + svg + color: #facc15 + + li + color: $gray-300 + + svg + color: #facc15 + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) + + p + color: #94a3b8 + + .view-all-link + a + font-weight: 500 !important + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + &:hover + text-decoration: underline + color: $yellow-accent !important + + .security-section + .section-title + color: white !important + + .card-subtitle + color: white !important + + .security-link + color: white !important + + &:hover + color: $yellow-accent !important + + .subtitle-detail + color: #9ca3af !important + + #vulnerability_version_chart + .chart-container + background: rgba(90, 42, 130, 0.15) + + .no-data + color: #94a3b8 + + .index-section + .index-title + color: #9ca3af !important + + .gradient-bar-container + .indicator-pointer + background: white !important + + &:hover + background: $yellow-accent !important + + .report-link-container + .report-link + color: $yellow-accent !important + + .did-you-know-card + background: linear-gradient(135deg, #4a3a15 0%, #3a2a10 100%) !important + + .did-you-know-title + color: #fcd34d !important + + svg + color: #fcd34d !important + + .feature-list + li + color: #fef08a !important + + .security-footer-link + a + color: $yellow-accent !important + + .no-data + color: #94a3b8 + + .footer-nav-section + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) + + svg + color: $yellow-accent + + h4 + color: #f8fafc + + ul + li + a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) + + .accordion-header-btn + .header-left + svg + color: $yellow-accent + + span + color: #f8fafc + + .chevron + color: #94a3b8 + + .accordion-content-footer + ul + li + a + color: $yellow-accent + + &:visited, + &:active + color: $yellow-accent + + // Legacy sections rendered inside redesign page (security + analysis summary) + .project_row + color: #e2e8f0 + + h2, h3, h4, h5 + color: #f8fafc !important + + p, small, td, th, li, span + color: #cbd5e1 + + a + color: $yellow-accent + + .well, + .activity_well, + .community_well, + #vulnerability-report .well + background-color: #2D1548 !important + border-color: rgba(159, 122, 186, 0.3) !important + + .right_border + border-right-color: rgba(159, 122, 186, 0.3) !important + + .security_blog_link + color: $yellow-accent !important + + #did_you_know + background-color: transparent !important + + h4 + color: #f8fafc !important + + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + ul, + li, + .indent, + p, + span + color: #cbd5e1 !important + + a + color: $yellow-accent !important + + &:hover + color: $yellow-hover !important + text-decoration: underline + + .project_row + #did_you_know + .well + background-color: #1D0631 !important + border-color: #4b5563 !important + + .no_analysis_message + .alert.alert-info.alert-block + background-color: #2D1548 !important + border: 1px solid rgba(159, 122, 186, 0.35) !important + color: #cbd5e1 !important + + .title + h3 + color: #f8fafc !important + + i, + [class^='icon-'], + [class*=' icon-'] + color: $yellow-accent !important + + p, + .analysis_in_progress, + .indent, + span + color: #60a5fa !important + + a + color: $yellow-accent !important + + .btn.btn-primary, + .btn.btn-large.btn-primary + background-color: $yellow-accent !important + border-color: $yellow-accent !important + color: $purple-dark !important + + &:hover, + &:focus, + &:active + background-color: $yellow-hover !important + border-color: $yellow-hover !important + color: $purple-dark !important + +// Final cascade guard for legacy styles overriding Did You Know dark theme +html.dark #page #project_container .project-show-redesign .project_row #did_you_know + background: transparent !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .well + background-color: #1D0631 !important + border: 1px solid #4b5563 !important + box-shadow: none !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know h4 + color: #f8fafc !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know p, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know li, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know .indent, +html.dark #page #project_container .project-show-redesign .project_row #did_you_know span + color: #cbd5e1 !important + +html.dark #page #project_container .project-show-redesign .project_row #did_you_know a + color: $yellow-accent !important + +// ============================================================================ +// Final Override Layer - Ensure our styles take precedence +// ============================================================================ +#project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: $gray-900 !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: $gray-700 !important + + .nutshell-list li + color: $gray-700 !important + + .content + color: $gray-700 !important + + a + color: $purple-primary !important + + .quick-ref-list li + .label + color: $gray-600 !important + + .value + color: $purple-primary !important + + a + color: $purple-primary !important + +// Dark mode final overrides +html.dark #project_container .project-show-redesign + .gradient-card + .card-content + h3, + h4 + color: white !important + + p:not(.summary-timespan):not(.summary-stat):not(.summary-change) + color: #cbd5e1 !important + + .nutshell-list li + color: $gray-300 !important + + .content + color: $gray-300 !important + + a + color: $yellow-accent !important + + .quick-ref-list li + .label + color: $gray-400 !important + + .value + color: $yellow-accent !important + + a + color: $yellow-accent !important + +// ============================================================================ +// Analysis Grid (Code, Activity, Community) +// ============================================================================ +.project-show-redesign + .analysis-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-bottom: 32px + + @media (min-width: 768px) + gap: 24px + margin-bottom: 48px + + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + + .analysis-card + display: flex + flex-direction: column + + .card-content + display: flex + flex-direction: column + height: 100% + flex: 1 + + .section-header + font-size: 18px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 12px !important + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + @media (min-width: 768px) + font-size: 20px !important + + .chart-title + font-size: 14px !important + font-weight: 600 !important + margin-bottom: 12px + display: block + + a + color: $purple-primary !important + text-decoration: none + + &:hover + text-decoration: underline + color: $purple-primary !important + + .subsection-title + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + margin-top: 16px + margin-bottom: 8px + + .chart-container + min-height: 160px + background: $gray-50 + border-radius: 8px + padding: 8px + margin-bottom: 16px + overflow: hidden + + @media (min-width: 768px) + min-height: 180px + + &.activity-chart + background: #f3f3f5 + padding: 12px + + #code_history_chart.chart-container + background: transparent + border-radius: 0 + padding: 0 + min-height: 200px + + #community_chart + margin-bottom: 16px + + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .highcharts-series-1 + .highcharts-point + stroke: $purple-primary !important + fill: $purple-primary !important + + .no-data + text-align: center + padding: 40px 20px + color: $gray-600 + font-size: 14px + + // Chart header with zoom controls + .chart-header + display: flex + align-items: flex-start + justify-content: space-between + margin-bottom: 12px + gap: 8px + + .chart-title + margin-bottom: 0 + + .zoom-controls + display: flex + align-items: center + gap: 4px + flex-shrink: 0 + + .zoom-label + font-size: 12px + color: $gray-400 + margin-right: 4px + + .zoom-btn + padding: 2px 6px + border-radius: 4px + font-size: 12px + border: none + background: transparent + color: $gray-500 + cursor: pointer + transition: all 0.15s + + &:hover + color: $purple-primary + + &.active + background: $purple-primary + color: white + font-weight: 600 + + // Summary Grid (30 day, 12 month) + .summary-grid + display: grid + grid-template-columns: 1fr + gap: 16px + margin-top: 16px + + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + gap: 20px + + .summary-section + .summary-header + font-size: 12px !important + font-weight: 600 !important + color: $gray-900 !important + margin-bottom: 2px !important + + .summary-timespan + font-size: 12px !important + color: $gray-400 !important + margin-bottom: 6px !important + line-height: 1.25 + + .summary-stat + font-size: 12px !important + color: $gray-700 !important + margin-bottom: 2px !important + line-height: 1.3 + + &:last-of-type + margin-bottom: 0 !important + + &.thirty-day-summary + .summary-header + margin-bottom: 1px !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 1px !important + line-height: 1.25 + + .stat-number + font-weight: 600 !important + color: $gray-900 !important + + .summary-change + font-size: 12px !important + margin-bottom: 8px !important + line-height: 1.4 + + &.positive + color: #16a34a !important + + &.negative + color: #dc2626 !important + +// ============================================================================ +// Summary Cascade Guard (override generic gradient-card paragraph rules) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + margin-top: 0 !important + + .summary-timespan, + .summary-stat, + .summary-change + margin-top: 0 !important + + .summary-timespan + margin-bottom: 6px !important + + .summary-stat + margin-bottom: 2px !important + + .summary-change + margin-bottom: 8px !important + +html.dark #project_container .project-show-redesign .analysis-grid .summary-section + .summary-header + color: white !important + + .summary-timespan + color: #94a3b8 !important + + .summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Languages + Contributors Cascade Guard (Redesign only) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + display: flex !important + align-items: flex-start !important + gap: 16px !important + margin-top: 8px !important + + .language-pie-container + width: 90px !important + height: 90px !important + flex: 0 0 90px !important + + .language-pie-chart + width: 90px !important + height: 90px !important + display: block !important + + .language-legend + flex: 1 1 auto !important + min-width: 0 !important + display: flex !important + flex-direction: column !important + gap: 6px !important + + .language-item + display: flex !important + align-items: center !important + gap: 8px !important + min-width: 0 !important + + .language-name + flex: 1 1 auto !important + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $gray-700 !important + + a + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + + .language-percent + flex: 0 0 auto !important + margin-left: auto !important + color: $gray-500 !important + + .community-chart-wrap + .col-md-12.manage_padding + padding: 0 !important + + .contributors-grid + display: grid !important + grid-template-columns: repeat(2, minmax(0, 1fr)) !important + column-gap: 8px !important + row-gap: 6px !important + margin-top: 6px !important + + .contributor-item + display: flex !important + align-items: center !important + gap: 6px !important + min-width: 0 !important + + .contributor-avatar + width: 24px !important + height: 24px !important + border-radius: 50% !important + flex: 0 0 24px !important + + .contributor-name + min-width: 0 !important + white-space: nowrap !important + overflow: hidden !important + text-overflow: ellipsis !important + color: $purple-primary !important + text-decoration: none !important + + &:hover + text-decoration: underline !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .languages-content + .language-legend + .language-item + .language-name + color: $gray-300 !important + + a + color: $yellow-accent !important + + .language-percent + color: $gray-400 !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + // Languages Section + .languages-content + display: flex + align-items: flex-start + gap: 16px + margin-top: 8px + + .language-pie-container + flex-shrink: 0 + width: 90px + height: 90px + + .language-pie-chart + width: 100% + height: 100% + display: block + + .language-legend + flex: 1 + display: flex + flex-direction: column + gap: 6px + + .language-item + display: flex + align-items: center + gap: 8px + font-size: 12px !important + + .language-color-box + width: 10px + height: 10px + border-radius: 2px + flex-shrink: 0 + + .language-name + flex: 1 + color: $gray-700 !important + font-size: 12px !important + + a + color: $purple-primary !important + text-decoration: none + font-size: 12px !important + + &:hover + text-decoration: underline + color: $purple-primary !important + + .language-percent + color: $gray-500 !important + font-weight: 500 + margin-left: auto + font-size: 12px !important + + // Contributors Grid + .contributors-grid + display: grid + grid-template-columns: repeat(2, 1fr) + column-gap: 8px + row-gap: 6px + margin-top: 6px + + .contributor-item + display: flex + align-items: center + gap: 6px + + .contributor-avatar + width: 24px + height: 24px + border-radius: 50% + flex-shrink: 0 + + .contributor-name + font-size: 12px !important + line-height: 1.2 + color: $purple-primary + text-decoration: none + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + &:hover + text-decoration: underline + + // Ratings Section + .ratings-section + margin-top: 12px + padding-top: 12px + border-top: 1px solid $gray-200 + + .rating-content + .rating-info + font-size: 12px !important + line-height: 1.3 + color: $gray-700 + margin-bottom: 2px + + .rating-link + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + .rating-display + display: flex + align-items: center + gap: 6px + + .stars-container + display: inline-block + height: 23px + line-height: 23px + overflow: hidden + + .rating_stars, + [id$='_rating_stars'] + display: inline-block + height: 23px + line-height: 1 + + // Allow the rating stars helper's inline styles to work + span[style*='float: left'] + float: left !important + display: inline !important + height: 23px + + .rating-value + font-size: 12px !important + color: $gray-900 !important + font-weight: 600 !important + +// ============================================================================ +// Dark Mode - Analysis Grid +// ============================================================================ +html.dark .project-show-redesign + .section-header + color: white !important + border-bottom-color: rgba(90, 42, 130, 0.3) + + .chart-title + a + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .subsection-title + color: white !important + + .chart-container + background: rgba(90, 42, 130, 0.15) + + &.activity-chart + background: #1D0631 + + #code_history_chart.chart-container + background: transparent + + #community_chart + .highcharts-series-0 + .highcharts-graph, + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .highcharts-series-1 + .highcharts-point + stroke: $yellow-accent !important + fill: $yellow-accent !important + + .zoom-controls + .zoom-label + color: #94a3b8 + + .zoom-btn + color: #94a3b8 + + &:hover + color: $yellow-accent + + &.active + background: $yellow-accent + color: #1D0631 + + .summary-section + .summary-header + color: white !important + + .summary-timespan + color: $gray-500 !important + + .summary-stat + color: $gray-300 !important + + .stat-number + color: white !important + + .summary-change + &.positive + color: #4ade80 !important + + &.negative + color: #f87171 !important + + .languages-content + .language-legend + .language-item + font-size: 12px !important + + .language-name + color: $gray-300 !important + font-size: 12px !important + + a + color: $yellow-accent !important + font-size: 12px !important + + &:hover + color: $yellow-accent !important + text-decoration: underline + + .language-percent + color: $gray-400 !important + font-size: 12px !important + + .contributors-grid + .contributor-item + .contributor-name + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) + + .rating-content + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Ratings Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + margin-top: 12px !important + padding-top: 12px !important + border-top: 1px solid $gray-200 !important + + .subsection-title + margin-top: 0 !important + margin-bottom: 8px !important + font-size: 14px !important + font-weight: 600 !important + color: $gray-900 !important + + .rating-content + display: flex !important + flex-direction: column !important + gap: 2px !important + + .rating-top-row + display: flex !important + align-items: flex-start !important + justify-content: space-between !important + gap: 12px !important + + .rating-main + flex: 1 1 auto !important + min-width: 0 !important + + .rating-action + flex: 0 0 auto !important + text-align: right !important + white-space: nowrap !important + + .rating-link + display: inline-flex !important + align-items: center !important + justify-content: flex-end !important + gap: 6px !important + + .rating-action-icon + width: 12px !important + height: 12px !important + flex-shrink: 0 !important + + .rating-info + margin: 0 !important + font-size: 12px !important + line-height: 1.35 !important + color: $gray-700 !important + + &.rating-secondary + margin-top: 4px !important + + .rating-link + color: $purple-primary !important + text-decoration: none !important + font-weight: 500 !important + + &:hover + color: $purple-primary !important + text-decoration: underline !important + + .rating-display + display: flex !important + align-items: center !important + gap: 6px !important + margin-top: 2px !important + + .stars-container + display: inline-block !important + height: 23px !important + line-height: 23px !important + overflow: hidden !important + + .rating-value + font-size: 12px !important + line-height: 1.2 !important + font-weight: 600 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .ratings-section + border-top-color: rgba(90, 42, 130, 0.3) !important + + .subsection-title + color: white !important + + .rating-content + .rating-top-row + .rating-action + .rating-info + color: $gray-300 !important + + .rating-info + color: $gray-300 !important + + .rating-link + color: $yellow-accent !important + + &:hover + color: $yellow-accent !important + + .rating-display + .rating-value + color: white !important + +// ============================================================================ +// Dark Theme Final Guard (Licenses + Graphs) +// ============================================================================ +html.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + // Graph surfaces + chart typography + #activity_chart, + #community_chart, + #loc_chart, + #vulnerability_version_chart + .highcharts-background + fill: transparent !important + + .highcharts-plot-background, + .highcharts-plot-border, + .highcharts-label-box + fill: none !important + + .highcharts-axis-line, + .highcharts-tick, + .highcharts-grid-line + stroke: rgba(148, 163, 184, 0.35) !important + + .highcharts-axis-labels text, + .highcharts-axis-title, + .highcharts-legend-item text, + .highcharts-title, + .highcharts-subtitle + fill: #cbd5e1 !important + + .highcharts-tooltip-box + fill: #2D1548 !important + stroke: #5A2A82 !important + + .highcharts-tooltip text + fill: #f8fafc !important + +// ============================================================================ +// Summary Typography Final Guard (Figma parity) +// ============================================================================ +#project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + margin: 0 0 2px 0 !important + color: $gray-900 !important + + p.summary-timespan, + p.summary-stat, + p.summary-change + font-size: 12px !important + line-height: 1.35 !important + margin-top: 0 !important + + p.summary-timespan + color: $gray-400 !important + margin-bottom: 6px !important + + p.summary-stat + color: $gray-700 !important + margin-bottom: 2px !important + + p.summary-change + margin-bottom: 8px !important + + .stat-number + font-size: 12px !important + font-weight: 600 !important + line-height: 1.25 !important + color: $gray-900 !important + +html.dark #project_container .project-show-redesign .analysis-grid .analysis-card .card-content + .summary-grid + .summary-section + h5.summary-header + color: white !important + + p.summary-timespan + color: #94a3b8 !important + + p.summary-stat + color: #d1d5db !important + + .stat-number + color: white !important + +// ============================================================================ +// Dark Mode Compatibility Guard (html.dark + body.dark) +// ============================================================================ +html.dark #project_container .project-show-redesign, +body.dark #project_container .project-show-redesign, +.dark #project_container .project-show-redesign + .licenses-section + .section-title + color: white !important + + .license-card + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .license-header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .header-left + h4 + color: $yellow-accent !important + + svg + color: $yellow-accent !important + + .chevron + color: #94a3b8 !important + + .license-content + border-top-color: rgba(90, 42, 130, 0.3) !important + + .license-detail-card + &.permitted, + &.forbidden, + &.required + border: 1px solid rgba(159, 122, 186, 0.25) !important + + &.permitted + background: linear-gradient(to bottom right, #3a1d6e 0%, #4a2580 100%) !important + + h5, + h5 svg, + li svg + color: $yellow-accent !important + + li + color: $gray-300 !important + + &.forbidden + background: linear-gradient(to bottom right, #4a1f1f 0%, #3a1515 100%) !important + + h5, + h5 svg, + li svg + color: #fb923c !important + + li + color: $gray-300 !important + + &.required + background: linear-gradient(to bottom right, #4a3a15 0%, #3a2a10 100%) !important + + h5, + h5 svg, + li svg + color: #facc15 !important + + li + color: $gray-300 !important + + .license-disclaimer + border-top-color: rgba(90, 42, 130, 0.3) !important + + p + color: #94a3b8 !important + + .view-all-link a + color: $yellow-accent !important + + .footer-nav-section + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + svg + color: $yellow-accent !important + + h4 + color: #f8fafc !important + + ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) !important + + .accordion-header-btn + .header-left + svg + color: $yellow-accent !important + + span + color: #f8fafc !important + + .chevron + color: #94a3b8 !important + + .accordion-content-footer ul li a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important diff --git a/app/decorators/analysis/chart.rb b/app/decorators/analysis/chart.rb index 193134c21..1e3b7015b 100644 --- a/app/decorators/analysis/chart.rb +++ b/app/decorators/analysis/chart.rb @@ -18,7 +18,7 @@ def series_and_range_data(default_options) end def first_ticks - series.first.ticks + series.first&.ticks end def min_month_as_ticks diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 942eadb86..6541fb0c5 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -9,9 +9,11 @@ def initialize(analysis) end def data - series_and_range_data(@defaults) + chart_data = series_and_range_data(@defaults) .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) .deep_merge(chart_watermark) + + apply_commits_palette(chart_data) end private @@ -25,6 +27,15 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.commits }] end + + def apply_commits_palette(chart_data) + series_config = chart_data[:series] || [] + series_config[0][:color] = '#2f7ed8' if series_config[0] + series_config[1][:color] = '#2f7ed8' if series_config[1] + chart_data + end end diff --git a/app/decorators/analysis/contributor_history_chart.rb b/app/decorators/analysis/contributor_history_chart.rb index d93c7bb05..e97911ba3 100644 --- a/app/decorators/analysis/contributor_history_chart.rb +++ b/app/decorators/analysis/contributor_history_chart.rb @@ -27,6 +27,8 @@ def series_data_without_axis_data end def x_and_y_axis_data + return [] unless series.last + [{ 'x' => series.last.ticks, 'y' => series.last.contributors }] end end diff --git a/app/decorators/vulnerability/version_chart.rb b/app/decorators/vulnerability/version_chart.rb index dcaea58bd..aa2706ae3 100644 --- a/app/decorators/vulnerability/version_chart.rb +++ b/app/decorators/vulnerability/version_chart.rb @@ -27,7 +27,7 @@ def set_xaxis end def series - color_map = ['#0c1c33', '#183867', '#4b6b9a', '#a5b5cd', '#cce1e9'] + color_map = ['#4a148c', '#7b1fa2', '#9c27b0', '#ba68c8', '#ce93d8'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index] = { name: "CVE #{legend[0]}", @@ -41,7 +41,7 @@ def series end def bdsa_series - bdsa_color_map = ['rgb(90,16,12)', 'rgb(156,37,31)', 'rgb(231,140,135)', 'rgb(154,155,156)', '#D3D3D3'] + bdsa_color_map = ['#8b0000', '#c62828', '#f57c00', '#fbc02d', '#9e9e9e'] SEVERITY_LEGENDS.keys.each_with_index do |level, index| legend = SEVERITY_LEGENDS[level] @default_options[:series][index + 5] = { name: "BDSA #{legend[0]}", diff --git a/app/views/layouts/responsive_project_layout.html.haml b/app/views/layouts/responsive_project_layout.html.haml index 63f832a98..b4d49b9a6 100644 --- a/app/views/layouts/responsive_project_layout.html.haml +++ b/app/views/layouts/responsive_project_layout.html.haml @@ -20,6 +20,6 @@ %header= render partial: 'layouts/partials/header' = render partial: 'layouts/partials/page', locals: { responsive_layout: true } .clear - %footer.fluid_footer= render partial: 'layouts/partials/fluid_footer' - = render 'cookies/consent_banner' unless cookies[:cookie_consented] + %footer= render partial: 'layouts/partials/footer' + = render 'cookies/consent_banner' unless cookies[:cookie_consented] = render partial: 'layouts/partials/js_scripts' diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index f6a029249..879072ea9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,7 +1,9 @@ :ruby content_for(:html_title) { t('.page_title', name: @project.name) } page_context[:select_top_menu_nav] = 'select_projects' - page_context[:select_footer_nav] = :project_summary + page_context[:select_footer_nav] = nil # Disable old footer navigation + page_context[:footer_menu_list] = nil # Disable old footer navigation + page_context[:page_header] = nil # Disable default header, using redesigned header instead - content_for :twitter_card do %meta{ content: 'summary', name: 'twitter:card' } @@ -12,75 +14,434 @@ %meta{ content: @project.logo.attachment.url(:med), name: 'twitter:image' } %meta{ content: 'https://www.openhub.net', name: 'twitter:domain' } -%div.separator-div - = project_analysis_timestamp(@project) - -.clear -= render partial: 'projects/show/is_a_duplicate', locals: { project: @project } - -.row.row-eq-height.margin_top_two.project_row - .col-md-4.project_summary_container - .well - %h4.text-left= t('.project_summary') - - size_breach = project_description_size_breached?(@project) - %section#project_summary{ itemprop: 'description', class: ('vertical_scroll_bar' if size_breach) } - - if @project.description.present? - = simple_format @project.description.strip_tags - - else - = t('.no_description') - = link_to t('.add_description'), edit_project_path(@project) - - %section#project_tags{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %h4.title= tag_icon_link(@project) - %p.tags - %span - - tags = @project.tag_list.split(' ') - - if tags.any? - = tag_links(tags) +.project-show-redesign{ itemscope: '', itemtype: 'http://schema.org/SoftwareSourceCode' } + / Project Header with Gradient + = render partial: 'projects/show/header_redesign' + + .project-content + / Stats Grid - 3 columns (Lines of Code, Contributors, Total Commits) + .stats-grid + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.code_total, delimiter: ',') + - else + N/A + .stat-label= t('.lines_of_code') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.committers_all_time) + - else + 0 + .stat-label= t('.contributors') + + .stat-card + .stat-value + - if @analysis.present? + = number_with_delimiter(@analysis.commit_count) + - else + 0 + .stat-label= t('.total_commits') + + / Desktop: 3-column grid (Project Summary, In a Nutshell, Quick Reference) + .three-col-grid + / Project Summary + .gradient-card + .card-content + %h3= t('.project_summary') + - if @project.description.present? + %p= simple_format(@project.description.strip_tags) + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + - if @project.is_a_duplicate + %p.note + = t('.note') + There are two distinct + = link_to 'Mozilla Core', '#' + and + = link_to 'Mozilla Chrome', '#' + projects for complete coverage. + + %section#project_tags + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tags_path(names: tag), class: 'tag' + - else + %span.tag + = t('.no_tags') + - if @project.edit_authorized? + = link_to t('.add_tags'), project_tags_path(@project) + + / In a Nutshell + .gradient-card + .card-content + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + .content + representing + = link_to "#{number_with_delimiter @analysis.code_total} lines of code", languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.main_language + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + .content + is + = link_to "mostly written in #{@analysis.main_language.nice_name}", languages_summary_project_analysis_path(@project, id: 'latest') + with average comment density + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "23 6 13.5 15.5 8.5 10.5 1 18" } + %polyline{ points: "17 6 23 6 23 12" } + .content + has a + %span.font-semibold mature codebase + maintained by a + %span.font-semibold very large development team + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %polyline{ points: "12 6 12 12 16 14" } + .content + took an estimated + = link_to "#{number_with_delimiter @analysis.man_years.ceil} years of effort", estimated_cost_project_path(@project) + (COCOMO model) + + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" } + %line{ x1: "16", y1: "2", x2: "16", y2: "6" } + %line{ x1: "8", y1: "2", x2: "8", y2: "6" } + %line{ x1: "3", y1: "10", x2: "21", y2: "10" } + .content + - if @analysis.first_commit_time? + starting + %span.font-semibold= @analysis.first_commit_time.strftime('%B %Y') + - if @analysis.last_commit_time? + , ending + %span.font-semibold= "#{time_ago_in_words(@analysis.last_commit_time)} ago" + - else + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference + .gradient-card + .card-content + %h3= t('.quick_reference.quick_reference') + %ul.quick-ref-list + - if @project.organization + %li + %span.label= t('.organization') + %span.value + = link_to @project.organization.name, organization_path(@project.organization) + + %li + %span.label= t('.homepage') + %span.value + - homepage_link = @project.decorate.sorted_link_list['Homepage']&.first + - if homepage_link + = link_to homepage_link.url, itemprop: 'url' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + = truncate(homepage_link.url.gsub(/^https?:\/\//, ''), length: 30) + - else + N/A + + %li + %span.label= t('.documentation') + %span.value + - doc_link = @project.decorate.sorted_link_list['Documentation']&.first + - if doc_link + = link_to doc_link.url do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M4 19.5A2.5 2.5 0 0 1 6.5 17H20" } + %path{ d: "M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z" } + View Docs + - else + N/A + + %li + %span.label= t('.code_location') + %span.value + - if @project.enlistments.present? + = link_to truncate(@project.enlistments.first.code_location.url, length: 40), @project.enlistments.first.code_location.url + - else + = link_to t('.add_code_location'), project_enlistments_path(@project) + + %li + %span.label= t('.similar_projects') + %span.value + %div#similar_projects{ data: { project_id: @project.to_param } } + + %li + %span.label= t('.managers') + %span.value + - if @project.active_managers.present? + = link_to "#{@project.active_managers.count} managers", project_managers_path(@project) + - else + = link_to t('.become_first', what: @project.name), new_project_manager_path(@project) + + / Mobile: Collapsible accordion cards + .mobile-accordion + / Project Summary - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h3= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @project.description.present? + %p= simple_format(@project.description.strip_tags, {}, wrapper_tag: 'span') + - else + %p + = t('.no_description') + = link_to t('.add_description'), edit_project_path(@project) + + %section#project_tags_mobile + %h4 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", style: "width: 1rem; height: 1rem; display: inline-block; vertical-align: text-top;" } + %path{ d: "M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" } + %line{ x1: "7", y1: "7", x2: "7.01", y2: "7" } + = t('.tags') + .tags-container + - tags = @project.tag_list.split(' ') + - if tags.any? + - tags.each do |tag| + = link_to tag, tags_path(names: tag), class: 'tag' + - else + %span.tag= t('.no_tags') + + / In a Nutshell - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "12", y1: "20", x2: "12", y2: "10" } + %line{ x1: "18", y1: "20", x2: "18", y2: "4" } + %line{ x1: "6", y1: "20", x2: "6", y2: "16" } + %h3= t('.in_a_nutshell', name: truncate_project_name(@project.name, 40)) + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + - if @analysis.present? + %ul.nutshell-list + %li + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %line{ x1: "22", y1: "2", x2: "11", y2: "13" } + %polygon{ points: "22 2 15 22 11 13 2 9 22 2" } + .content + has had + = link_to "#{number_with_delimiter @analysis.commit_count} commits", summary_project_commits_path(@project) + made by + = link_to "#{number_with_delimiter @analysis.committers_all_time} contributors", summary_project_contributors_path(@project) + / Add remaining factoids as in desktop version - else - = t('.no_tags') - - if @project.edit_authorized? - = link_to t('.add_tags'), project_tags_path(@project) - - if @project.project_badges.active.present? - %section#project_badges - %h4.title= link_to t('project_badges.index.title'), project_project_badges_path(@project) - = show_badges - = more_badges_link - - .col-md-4.nutshell_container - .well - - p_name = truncate_project_name(@project.name, 40) - %h4.nutshell_heading!= t('.in_a_nutshell', name: @project.name.length > 40 ? p_name : p_name + '...') - - if @analysis.present? - = render 'projects/show/factoids' - - else - = render partial: 'projects/show/no_analysis_summary' - .col-md-4.quick_reference_container - .well - = render partial: 'projects/show/quick_reference' - -.row.mezzo -= render partial: 'projects/show/licenses' - -.row.mezzo -.project_security - = render partial: 'projects/show/security' - -.row.mezzo -- if @analysis.empty? - .analysis_blank_container - .row.project_row - .col-md-12.blank_message_container - .col-md-8.analysis_alert_container - = render partial: 'projects/show/no_analysis_summary' - .col-md-4#proj_rating= render 'projects/show/community_rating', title: t('.community_rating'), score: @score - - if @project.enlistments.empty? && @analysis.empty? - = image_tag('sample_ohloh_analysis.jpg', class: 'col-md-12') -- else - = render partial: 'projects/show/analysis_summary' - -- if @scan_analytics.present? - .row.mezzo - #scan_data_row - .project_row= render partial: 'projects/show/scan_analytics_summary', collection: @scan_analytics, as: :scan_analytics + = render partial: 'projects/show/no_analysis_summary' + + / Quick Reference - Collapsible + .gradient-card.accordion-card + %button.accordion-header{ onclick: "toggleAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" } + %polyline{ points: "15 3 21 3 21 9" } + %line{ x1: "10", y1: "14", x2: "21", y2: "3" } + %h3= t('.quick_reference.quick_reference') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content{ style: "display: none;" } + %ul.quick-ref-list + / Same content as desktop version + - if @project.organization + %li + %span.label= t('.organization') + %span.value= link_to @project.organization.name, organization_path(@project.organization) + / Add remaining items + + / Licenses Section + = render partial: 'projects/show/licenses_redesign' + + / Project Security Section + = render partial: 'projects/show/security' + + / Code, Activity, Community Grid + - if @analysis.present? + = render partial: 'projects/show/analysis_summary_redesign' + + / Project Navigation Footer + .footer-nav-section + / Desktop: 4-column grid + .footer-nav-grid + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %h4= t('.project_summary') + %ul + %li= link_to t('.news'), project_rss_subscriptions_path(@project) + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) + %li= link_to t('.related_projects'), similar_project_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %h4= t('.code_data') + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), security_project_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %h4= t('.scm_data') + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .nav-column + .column-header + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %h4= t('.community_data') + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) + + / Mobile: Accordion + .footer-nav-accordion + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } + %span= t('.project_summary') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.news'), project_rss_subscriptions_path(@project) + %li= link_to t('.settings'), settings_project_path(@project) + %li= link_to t('.sharing_widgets'), project_widgets_path(@project) + %li= link_to t('.related_projects'), similar_project_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "16 18 22 12 16 6" } + %polyline{ points: "8 6 2 12 8 18" } + %span= t('.code_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') + %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) + %li= link_to t('.security.title'), security_project_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %rect{ x: "3", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "3", width: "7", height: "7" } + %rect{ x: "14", y: "14", width: "7", height: "7" } + %rect{ x: "3", y: "14", width: "7", height: "7" } + %span= t('.scm_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.commits'), summary_project_commits_path(@project) + %li= link_to t('.contributors'), summary_project_contributors_path(@project) + + .accordion-item + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span= t('.community_data') + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + .accordion-content-footer{ style: "display: none;" } + %ul + %li= link_to t('.users'), users_project_path(@project) + %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) + %li= link_to t('.user_contributor_locations'), map_project_path(@project) + +:javascript + function toggleAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } + + function toggleFooterAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } diff --git a/app/views/projects/show/_analysis_summary_redesign.html.haml b/app/views/projects/show/_analysis_summary_redesign.html.haml new file mode 100644 index 000000000..73d21d806 --- /dev/null +++ b/app/views/projects/show/_analysis_summary_redesign.html.haml @@ -0,0 +1,104 @@ +/ Code, Activity, Community Grid +.analysis-grid + / Code Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.code') + + %h4.chart-title + = link_to t('.lines_of_code'), languages_summary_project_analysis_path(@project, id: 'latest') + + - if @analysis.present? + - chart_url = lines_of_code_project_analysis_path(@project, id: 'latest') + .chart.watermark440#loc_chart{ datasrc: chart_url, style: 'width: 100%; height: 200px;' } + - else + %p.no-data= t('.no_code_locations') + + %h4.subsection-title= t('.languages') + = render partial: 'projects/show/languages_redesign' + + / Activity Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.activity') + + .chart-header + %h4.chart-title + = link_to t('.commits_per_month'), summary_project_commits_path(@project) + + + - if @analysis.present? + - chart_url = commits_history_project_analysis_path(@project, id: 'latest') + #activity_chart.chart.watermark440{ datasrc: chart_url, style: 'width: 100%; min-height: 210px' } + - else + %p.no-data= t('.no_commits') + + .summary-grid + .summary-section.thirty-day-summary + - thirty_day_summary = @analysis&.thirty_day_summary + %h5.summary-header= t('.thirty_day_summary') + %p.summary-timespan + - if thirty_day_summary && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 30.days).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.commits_count.to_i) + = t('.commits') + %p.summary-stat + %span.stat-number= number_with_delimiter(thirty_day_summary&.committer_count.to_i) + = t('.contributors') + + .summary-section + %h5.summary-header= t('.twelve_month_summary') + %p.summary-timespan + - if @analysis.twelve_month_summary.present? && @analysis.oldest_code_set_time + = (@analysis.oldest_code_set_time - 12.months).strftime('%b %e %Y') + — + = @analysis.oldest_code_set_time.strftime('%b %e %Y') + - else + = t('.not_available') + + - if @analysis.twelve_month_summary.present? + - previous_summary = @analysis.previous_twelve_month_summary + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.commits_count) + Commits + - if previous_summary&.data? + - diff = previous_summary.commits_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_commits = previous_summary.commits_count + - if prev_commits.to_i > 0 + (#{(diff.abs.to_f / prev_commits.to_f * 100).floor}%) + from previous 12 + + %p.summary-stat + %span.stat-number= number_with_delimiter(@analysis.twelve_month_summary.committer_count) + Contributors + - if previous_summary&.data? + - diff = previous_summary.committers_difference + %p.summary-change{ class: diff > 0 ? 'positive' : 'negative' } + = diff > 0 ? "Up +#{number_with_delimiter(diff.abs)}" : "Down -#{number_with_delimiter(diff.abs)}" + - prev_committers = previous_summary.committer_count + - if prev_committers.to_i > 0 + (#{(diff.abs.to_f / prev_committers.to_f * 100).floor}%) + from previous 12 months + + / Community Card + .gradient-card.analysis-card + .card-content + %h3.section-header= t('.community') + + .community-chart-wrap + = render partial: 'projects/show/committers_per_month_chart' + + %h4.subsection-title= t('.most_recent_contributors') + = render partial: 'projects/show/community_recent_committers_redesign' + + .ratings-section + %h4.subsection-title= t('.ratings') + = render partial: 'projects/show/community_rating_redesign', locals: { score: @score } diff --git a/app/views/projects/show/_community_rating_redesign.html.haml b/app/views/projects/show/_community_rating_redesign.html.haml new file mode 100644 index 000000000..6095a4a7a --- /dev/null +++ b/app/views/projects/show/_community_rating_redesign.html.haml @@ -0,0 +1,28 @@ +.rating-content + .rating-top-row + .rating-main + - if @project.ratings.any? + %p.rating-info + = link_to t((@project.ratings.count == 1 ? 'projects.show.community_rating.one_user_rate' : 'projects.show.community_rating.users_rate'), n: @project.ratings.count), summary_project_reviews_path(@project), class: 'rating-link' + + .rating-display + .stars-container + != rating_stars('average_rating_stars', @project.rating_average.to_f || 0) + %span.rating-value + = "#{number_with_precision(@project.rating_average || 0, precision: 1)} /5.0" + - else + %p.rating-info= t('projects.show.community_rating.be_the_first') + + .rating-action + %p.rating-info + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.click_to_add') + + - unless current_user && Review.by_account(current_user).for_project(@project).exists? + %p.rating-info.rating-secondary + = link_to new_project_review_path(@project), class: 'rating-link' do + %svg.rating-action-icon{ viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', 'stroke-width': '2', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } + %polygon{ points: '12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2' } + %span= t('projects.show.community_rating.review_this_project') diff --git a/app/views/projects/show/_community_recent_committers_redesign.html.haml b/app/views/projects/show/_community_recent_committers_redesign.html.haml new file mode 100644 index 000000000..46aea669e --- /dev/null +++ b/app/views/projects/show/_community_recent_committers_redesign.html.haml @@ -0,0 +1,21 @@ +- if @analysis && @analysis.all_time_summary + - recent_contributors = @analysis.all_time_summary.recent_contribution_persons + - if recent_contributors.any? + .contributors-grid + - recent_contributors.take(8).each do |p| + :ruby + contrib_id = if p.account_id + Contribution.generate_id_from_project_id_and_account_id(@project.id, p.account_id) + else + p.id + end + person_name = obfuscate_email(p.person_name) + truncated_name = person_name.reverse.truncate(20).reverse + + .contributor-item + = image_tag avatar_img_path(p, 24), class: 'contributor-avatar', alt: person_name + = link_to truncated_name, project_contributor_path(@project.id, contrib_id), class: 'contributor-name', title: person_name + - else + %p.no-data= t('.no_one_recently') +- else + %p.no-data= t('.no_contributors') diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml new file mode 100644 index 000000000..6283af5ef --- /dev/null +++ b/app/views/projects/show/_header_redesign.html.haml @@ -0,0 +1,128 @@ +.project-header-gradient + / Decorative gradient circle is handled by CSS + + / Top Right Stats & Button (Desktop only, hidden on mobile) + .header-top-right + .activity-indicator-wrapper + / Activity indicator will be rendered here + - project_activity_level_class(@project, :thirtyfive) + - project_activity_level_text(@project, :thirtyfive) + + .stats-card + .stats-inner + / Users icon + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + .user-count + = number_with_delimiter(@project.user_count) + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + + .header-content + .header-main + .project-title-section + %h1{ itemprop: 'name' } + = @project.name + + .action-buttons + = link_to settings_project_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "3" } + %path{ d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" } + = t('.settings') + + - if @project.is_a_duplicate + - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) + = link_to edit_project_duplicate_path(@project, @project.is_a_duplicate), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + = t('.edit_duplicate') + - else + = link_to new_project_duplicate_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M4 15s1-1 4-1 5 2 9 2V3s-4 0-6-2-9-1-3 1-4 1-4 1z" } + %line{ x1: "4", y1: "22", x2: "4", y2: "15" } + = t('.report_duplicate') + + %p.project-description{ itemprop: 'description' } + - if @project.description.present? + = truncate(@project.description.strip_tags, length: 200) + - else + = t('.no_description') + + %p.timestamp + = t('.analyzed', time: time_ago_in_words(@project.analysis_updated_or_project_created_time)) + = t('.based_on_code', time: time_ago_in_words(@project.best_analysis.try(:logged_at))) if @project.best_analysis.try(:logged_at) + + / Mobile Stats & Button (shown only on mobile) + .header-mobile-stats + .activity-card-mobile + - project_activity_level_class(@project, :twentyfive) + - project_activity_level_text(@project, :twentyfive) + + .stats-card-mobile + .stats-top + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + %span.user-count= number_with_delimiter(@project.user_count) + %span.users-label= t('.users') + + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn-mobile.using{ 'data-project-id': @project.id } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.you_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + - else + %button.i-use-this-btn-mobile.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + %svg{ viewBox: "0 0 24 24", fill: "currentColor" } + %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } + %polyline{ points: "22 4 12 14.01 9 11.01" } + = t('.i_use_this') + +/ Login Modal for non-logged in users +- unless logged_in? + .modal.fade#LoginModal{ 'aria-hidden': 'true', 'aria-labelledby': 'LoginModalLabel', role: 'dialog' } + .modal-dialog.modal-sm + .modal-content + .modal-header + %button.close{ 'aria-label': 'Close', 'data-dismiss': 'modal', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %h4.modal-title#LoginModalLabel= t('.login_required') + .model-body + = render 'sessions/sign_in' diff --git a/app/views/projects/show/_languages_redesign.html.haml b/app/views/projects/show/_languages_redesign.html.haml new file mode 100644 index 000000000..e7f861312 --- /dev/null +++ b/app/views/projects/show/_languages_redesign.html.haml @@ -0,0 +1,20 @@ +- if @analysis && !@analysis.empty? + :ruby + languages_image_options = { project_id: @project.to_param, id: 'latest', height: 90, width: 90 } + language_data = Analysis::LanguagePercentages.new(@analysis).collection + + .languages-content + .language-pie-container + = image_tag languages_project_analysis_url(languages_image_options), class: 'language-pie-chart' + + .language-legend + - language_data.take(5).each do |id, name, attr| + - percent = attr[:percent] > 0 ? attr[:percent] : '<1' + - color = attr[:color] + .language-item + .language-color-box{ style: "background-color: ##{color}" } + %span.language-name + = id ? link_to(name, language_path(id)) : name + %span.language-percent= "#{percent}%" +- else + %p.no-data= t('.no_source') diff --git a/app/views/projects/show/_licenses_redesign.html.haml b/app/views/projects/show/_licenses_redesign.html.haml new file mode 100644 index 000000000..a1fe0ec22 --- /dev/null +++ b/app/views/projects/show/_licenses_redesign.html.haml @@ -0,0 +1,93 @@ +- if @project.licenses.count > 0 + .licenses-section + %h2.section-title= t('.licenses') + + - @project.licenses.each do |license| + .license-card.gradient-card + %button.license-header{ onclick: "toggleLicense(this)", class: "license-toggle-#{license.id}", type: "button" } + .header-left + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" } + %h4= license.name + + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %polyline{ points: "6 9 12 15 18 9" } + + .license-content{ class: "license-content-#{license.id}" } + .license-details-grid + / Permitted Column + .license-detail-card.permitted + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } + = t('.permitted') + %ul + - license.permitted_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "m9 12 2 2 4-4" } + = p.name + + / Forbidden Column + .license-detail-card.forbidden + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = t('.forbidden') + %ul + - license.forbidden_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "15", y1: "9", x2: "9", y2: "15" } + %line{ x1: "9", y1: "9", x2: "15", y2: "15" } + = p.name + + / Required Column + .license-detail-card.required + %h5 + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %path{ d: "M12 8h.01" } + = t('.required') + %ul + - license.required_license_permissions.each do |p| + %li{ 'data-tipso': p.description, title: p.description } + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %line{ x1: "12", y1: "16", x2: "12", y2: "12" } + %path{ d: "M12 8h.01" } + = p.name + + .license-disclaimer + %p= t('.disclaimer') + + .view-all-link + = link_to "#{t('.view_all_licenses')} →", project_licenses_path(@project) + +:javascript + function toggleLicense(button) { + // Only toggle on mobile (screen width < 1024px) + if (window.innerWidth >= 1024) { + return; + } + + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.classList.contains('expanded'); + + if (isExpanded) { + content.classList.remove('expanded'); + content.style.display = 'none'; + chevron.classList.remove('expanded'); + } else { + content.classList.add('expanded'); + content.style.display = 'block'; + chevron.classList.add('expanded'); + } + } diff --git a/app/views/projects/show/_security.html.haml b/app/views/projects/show/_security.html.haml index 0391ab395..ea15f7392 100644 --- a/app/views/projects/show/_security.html.haml +++ b/app/views/projects/show/_security.html.haml @@ -1,18 +1,84 @@ - blog_url = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Project-Security' -.row.project_row - - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report - .col-md-12.security_title_container - %h4.security-panel-title= t('.title') - .clearfix - .row-eq-height - .col-md-4#vulnerability_per_version - = render partial: 'projects/show/vulnerabilities_per_version' - .col-md-4#vulnerability-report - .well= render partial: 'projects/show/vulnerability_report' - .col-md-4#did_you_know - = render 'projects/show/site_features' - = link_to t('.blog_link'), blog_url, class: 'security_blog_link', target: '_blank' - - else - .col-md-12#did_you_know - %h4= t('.title_nvd') - = render 'projects/show/site_features' +- if @project.best_project_security_set.try(:most_recent_vulnerabilities?) || @project.project_vulnerability_report + #security.security-section + %h2.section-title= t('.title', default: 'Project Security') + + .security-grid + / Vulnerabilities per Version Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerabilities_per_version.title', default: 'Vulnerabilities per Version'), security_project_path(@project), class: 'security-link' + %span.subtitle-detail= t('.vulnerabilities_per_version.title_detail', default: '(Most recent versions)') + - if @project.best_project_security_set.try(:most_recent_vulnerabilities?) + - chart_url = recent_vulnerabilities_version_chart_project_path(@project) + #vulnerability_version_chart.chart-container{ data: { src: chart_url } } + - else + %p.no-data= t('vulnerabilities.index.no_vulnerability') + + / Vulnerability Report Card + .gradient-card + .card-content + %h4.card-subtitle + = link_to t('.vulnerability_report.title', default: 'Vulnerability Report'), security_project_path(@project), class: 'security-link' + + - pvr = @project.project_vulnerability_report + - if pvr + - pvr_link = 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-About-the-Project-Vulnerability-Report' + + .vulnerability-indices + / Security Confidence Index + .index-section + .index-title= t('.vulnerability_report.pss.title', default: 'Security Confidence Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr).to_f}%", 'data-tipso': pss_content(pvr), 'data-tipso-title': t('.vulnerability_report.pss.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pss.poor') + %span.label-right= t('.vulnerability_report.pss.good') + + / Vulnerability Exposure Index + .index-section + .index-title= t('.vulnerability_report.pvs.title', default: 'Vulnerability Exposure Index') + .gradient-bar-container + .gradient-bar + .indicator-pointer{ style: "left: #{100 - pointer_from_right(pvr, pvs: true).to_f}%", 'data-tipso': pvs_content(pvr), 'data-tipso-title': t('.vulnerability_report.pvs.title') } + .index-labels + %span.label-left= t('.vulnerability_report.pvs.poor') + %span.label-right= t('.vulnerability_report.pvs.good') + + .report-link-container + = link_to t('.vulnerability_report.blog_link', default: 'About Project Vulnerability Report'), pvr_link, class: 'report-link', target: '_blank' + - else + %p.no-data= t('.vulnerability_report.no_data') + + / Did You Know Card + .did-you-know-card + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe + + .security-footer-link + = link_to "#{t('.blog_link', default: 'About Project Security')} →", blog_url, target: '_blank' + +- else + #security.security-section + %h2.section-title= t('.title_nvd', default: 'This Project has No vulnerabilities Reported Against it') + .did-you-know-card.single + .card-content + %h4.did-you-know-title + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %circle{ cx: "12", cy: "12", r: "10" } + %path{ d: "M12 16v-4" } + %path{ d: "M12 8h.01" } + = t('.site_features.did_you_know', default: 'Did You Know...') + %ul.feature-list + - random_site_features.each do |feature| + %li= feature.html_safe diff --git a/config/charting/analysis/options_based_on_type.yml b/config/charting/analysis/options_based_on_type.yml index 46f2b4a25..f6de6294a 100644 --- a/config/charting/analysis/options_based_on_type.yml +++ b/config/charting/analysis/options_based_on_type.yml @@ -24,9 +24,10 @@ commits_history: series: - name: 'Commits' id: 'commits' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' @@ -40,12 +41,12 @@ code_history: stacking: 'normal' series: - name: 'Code' - color: '#482268' + color: '#1D0631' id: 'code' - name: 'Comments' - color: '#008893' + color: '#5A2A82' - name: 'Blanks' - color: '#99dde3' + color: '#9F7ABA' tooltip: shared: true @@ -56,9 +57,10 @@ committer_history: series: - name: 'Committers' id: 'committers' + color: '#5A2A82' - name: 'Current Month' type: 'scatter' - color: 'rgba(119, 152, 191, 3)' + color: '#5A2A82' marker: radius: 4 symbol: 'circle' diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 723000732..06c7de8b9 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -92,6 +92,16 @@ en: show: page_title: "The %{name} Open Source Project on Open Hub" project_summary: "Project Summary" + overview: "Overview" + news: "News" + settings: "Settings" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + languages: "Languages" + commits: "Commits" + contributors: "Contributors" + users: "Users" + security: "Security" no_description: "No description has been added for this project." add_description: "Add description" tags: "Tags" @@ -99,6 +109,33 @@ en: add_tags: "add some now" community_rating: "Community Rating" in_a_nutshell: "In a Nutshell, %{name}" + lines_of_code: "Lines of Code" + contributors: "Contributors" + total_commits: "Total Commits" + note: "Note:" + organization: "Organization" + homepage: "Homepage" + documentation: "Documentation" + code_location: "Code Location" + add_code_location_link: "Add code location" + similar_projects_label: "Similar Projects" + managers_label: "Managers" + become_first_manager: "Become the first manager of %{what}" + news: "News" + sharing_widgets: "Sharing Widgets" + related_projects: "Related Projects" + code_data: "Code Data" + languages_label: "Languages" + cost_estimates: "Cost Estimates" + security_label: "Security" + scm_data: "SCM Data" + commits_label: "Commits" + contributors_label: "Contributors" + community_data: "Community Data" + users_label: "Users" + ratings_reviews: "Ratings & Reviews" + user_contributor_locations: "User & Contributor Locations" + licenses_title: "Licenses" site_features: did_you_know: 'Did You Know' analysis_summary: @@ -113,6 +150,25 @@ en: add_a_code_location: "Add a code location" no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display the commits per month chart. %{link} to see this information." community: "Community" + analysis_summary_redesign: + code: "Code" + lines_of_code: "Lines of Code" + languages: "Languages" + no_code_locations: "There are no Code Locations added to this project so Open Hub cannot display this chart." + activity: "Activity" + commits_per_month: "Commits per Month" + zoom: "Zoom" + no_commits: "No commit data available" + thirty_day_summary: "30 Day Summary" + twelve_month_summary: "12 Month Summary" + not_available: "not available" + commits: "Commits" + contributors: "Contributors" + community: "Community" + contributors_per_month: "Contributors per Month" + no_contributors: "No contributor data available" + most_recent_contributors: "Most Recent Contributors" + ratings: "Ratings" committers_per_month_chart: contributors_per_month: "Contributors per Month" add_a_code_location: "Add a code location" @@ -125,14 +181,33 @@ en: be_the_first: "Be the first to rate this project" click_to_edit: "Click to edit your rating" click_to_add: "Click to add your rating" + click_to_add_review: "Click to add your review" clear: "Clear" - review_this_project: "Review this Project!" + review_this_project: "Review this project" community_recent_committers: most_recent: "Most Recent Contributors" no_one_recently: "No one has contributed to this project recently" + header_redesign: + i_use_this: 'I Use This!' + you_use_this: 'You use this' + login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' + no_description: 'No description available' header: i_use_this: 'I Use This!' + you_use_this: 'You use this' login_required: 'Login Required' + settings: 'Settings' + edit_duplicate: 'Edit Duplicate' + report_duplicate: 'Report Duplicate' + users: 'users' + analyzed: 'Analyzed %{time} ago.' + based_on_code: 'Based on code collected %{time} ago.' i_use_this: i_use_this: 'I Use This!' is_a_duplicate: @@ -179,7 +254,7 @@ en: vulnerability_report: no_data: 'No data available' pvs_tooltip: 'Score: %{score}
Percentage: %{percentage}
Rank: %{rank} of %{max_rank}' - title: 'Project Vulnerability Report' + title: 'Vulnerability Report' blog_link: 'About Project Vulnerability Report' pvs: title: 'Vulnerability Exposure Index' @@ -191,7 +266,7 @@ en: poor: 'Poor security track-record' vulnerabilities_per_version: title: 'Vulnerabilities per Version' - title_detail: '( last 10 releases )' + title_detail: '(Most recent versions)' no_data: 'No data available' thirty_day_summary: commit: "Commit" @@ -221,8 +296,15 @@ en: most_recent_commit: most recent commit %{time} security: title: 'Project Security' - title_nvd: 'This Project has No vulnerabilities Reported Against it' + title_nvd: 'This project has no vulnerabilities reported against it' blog_link: 'About Project Security' + licenses_redesign: + licenses: 'Licenses' + permitted: 'Permitted' + forbidden: 'Forbidden' + required: 'Required' + disclaimer: 'These details are provided for information only. No information here is legal advice and should not be used as such.' + view_all_licenses: 'View All Licenses' licenses: title: 'Licenses' all: 'All Licenses' From cec6e638885357884f0d5600183537f28538660c Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Thu, 12 Mar 2026 15:33:25 +0530 Subject: [PATCH 13/53] Rubocop fix --- app/decorators/analysis/commit_history_chart.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 6541fb0c5..3094a2c25 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -10,8 +10,8 @@ def initialize(analysis) def data chart_data = series_and_range_data(@defaults) - .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) - .deep_merge(chart_watermark) + .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) + .deep_merge(chart_watermark) apply_commits_palette(chart_data) end From 8920906a948b45ac78bbaa6a87688cbdd8341d27 Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Thu, 12 Mar 2026 17:59:43 +0530 Subject: [PATCH 14/53] Added i18n translation keys --- app/assets/stylesheets/buttons.sass | 10 +++--- app/assets/stylesheets/projects.sass | 3 ++ app/views/projects/_project_index.html.haml | 33 ++++++++++--------- app/views/projects/index.html.haml | 25 +++++++++++--- app/views/session_projects/_menu.html.haml | 14 ++++---- .../_page_entries_info.html.haml | 7 ++-- config/locales/en.yml | 7 +++- config/locales/projects.en.yml | 16 ++++----- 8 files changed, 70 insertions(+), 45 deletions(-) diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 2f9eff02c..6eb3346ff 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -19,25 +19,25 @@ position: relative !important padding: 8px 16px !important line-height: normal !important - font-size: 0.875rem !important + font-size: 14px !important text-decoration: none !important white-space: nowrap !important .btn-large, .btn-lg padding: 10px 24px !important - font-size: 1rem !important + font-size: 16px !important .btn-small, .btn-sm padding: 6px 12px !important - font-size: 0.8125rem !important + font-size: 13px !important .btn-mini padding: 4px 8px !important - font-size: 0.75rem !important + font-size: 12px !important .btn-minier padding: 2px 6px !important - font-size: 0.6875rem !important + font-size: 11px !important button.btn:active, a.btn:active top: 0 !important diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index af3de0fd2..da06c6e7b 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -964,6 +964,9 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) &.filled color: #ffb91a + &.half + color: #ffb91a + &.empty color: #d1d5db html.dark & diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index ad7ff65b1..3a3138e86 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -37,7 +37,7 @@ .project-icon-desc .project-icon - if project.logo.present? - = image_tag project.logo.url(:med), alt: project.name + = image_tag project.logo.attachment.url(:med), alt: project.name - else %i.fa.fa-cube @@ -48,20 +48,19 @@ - elsif project.description = project.description - else - %span.text-muted No description available + %span.text-muted= t('.no_description_available') / Language and License .project-lang-license .language - if project.best_analysis.present? && project.best_analysis.main_language - lang = project.best_analysis.main_language.nice_name - Mostly written in - = link_to lang, languages_summary_project_analysis_path(project, 'latest') + = t('.mostly_written_in', lang: link_to(lang, languages_summary_project_analysis_path(project, 'latest'))).html_safe - else - %span Not available + %span= t('.not_available') .licenses - %span.license-label Licenses: + %span.license-label= t('.licenses') - if project.licenses.any? = project.licenses.first(3).collect { |l| l.short_name.truncate(12) }.join(', ') - else @@ -70,13 +69,13 @@ / Tags .project-tags - unless project.tag_list.blank? - %span.tags-label Tags + %span.tags-label= t('.tags') - tags = project.tag_list.split(' ') - tags.first(8).each do |tag| - tag = tag.fix_encoding_if_invalid = link_to html_escape(tag), tags_path(names: tag), class: 'tag' - if tags.length > 8 - %span.more-tags= "#{tags.length - 8} more..." + %span.more-tags= t('.n_more', n: tags.length - 8) / Stats - Right Side .project-stats @@ -89,7 +88,7 @@ 0 - else = project.best_analysis.code_total.to_human - %span.stat-label lines of code + %span.stat-label= t('.loc') .activity-indicator .activity-icon @@ -100,7 +99,7 @@ / Row 2: Contributors .stat-item %span.stat-value= number_with_delimiter(project.active_committers) - %span.stat-label current contributors + %span.stat-label= t('.current_contributors') / Row 3: Last Commit | Star Rating .stat-item @@ -109,14 +108,18 @@ = time_ago_in_words(project.best_analysis.last_commit_time) - else 0 - %span.stat-label since last commit + %span.stat-label= t('.since_last_commit') .rating-section .stars - - rating = project.rating_average.to_f || 0 + - rating = project.rating_average.to_f + - full_stars = rating.floor + - has_half_star = (rating - full_stars) >= 0.5 - 5.times do |i| - - if i < rating.floor + - if i < full_stars %i.fa.fa-star.star.filled + - elsif has_half_star && i <= full_stars + %i.fa.fa-star-half-o.star.half - else %i.fa.fa-star.star.empty .review-count @@ -125,11 +128,11 @@ / Row 4: Users on Open Hub | I Use This Button .stat-item %span.stat-value= number_with_delimiter(project.user_count) - %span.stat-label users on Open Hub + %span.stat-label= t('.users_on_open_hub') .use-this-button - if logged_in? = render partial: 'projects/show/i_use_this', locals: { project: project } - else %button.btn-use-this{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' } - I Use This + = t('.i_use_this') diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 34506ed4c..5447eaf32 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -31,10 +31,25 @@ / Modern Pagination - if @projects.total_pages > 1 .modern-pagination - %button.page-btn{disabled: @projects.current_page == 1, onclick: "window.location.href='?page=#{@projects.current_page - 1}'"} + - current_page = @projects.current_page + - total_pages = @projects.total_pages + %button.page-btn{disabled: current_page == 1, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page - 1)).to_query}'"} %i.fa.fa-chevron-left - - (1..@projects.total_pages).each do |page| - %button.page-btn{class: ('active' if page == @projects.current_page), onclick: "window.location.href='?page=#{page}'"} - = page - %button.page-btn{disabled: @projects.current_page == @projects.total_pages, onclick: "window.location.href='?page=#{@projects.current_page + 1}'"} + - visible_pages = [] + - window_size = 2 + - visible_pages << 1 + - if total_pages > 1 + - left = [current_page - window_size, 2].max + - right = [current_page + window_size, total_pages - 1].min + - visible_pages << :gap if left > 2 + - visible_pages.concat((left..right).to_a) if left <= right + - visible_pages << :gap if right < total_pages - 1 + - visible_pages << total_pages + - visible_pages.each do |page| + - if page == :gap + %span.page-ellipsis … + - else + %button.page-btn{class: ('active' if page == current_page), onclick: "window.location.href='?#{request.query_parameters.merge(page: page).to_query}'"} + = page + %button.page-btn{disabled: current_page == total_pages, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page + 1)).to_query}'"} %i.fa.fa-chevron-right diff --git a/app/views/session_projects/_menu.html.haml b/app/views/session_projects/_menu.html.haml index 51236a48c..d142d7d70 100644 --- a/app/views/session_projects/_menu.html.haml +++ b/app/views/session_projects/_menu.html.haml @@ -3,7 +3,7 @@ .compare-toggle-tab %button#compare_toggle_btn.compare-toggle-button %i.fa.fa-exchange - Compare Projects (#{@session_projects.size}/3) + = t('.compare_projects', count: @session_projects.size) %i.fa.fa-chevron-up.toggle-icon / Expandable Tray Content @@ -17,19 +17,19 @@ %i.fa.fa-times .project-slot-icon - if project.logo.present? - = image_tag project.logo.url(:small), alt: project.name + = image_tag project.logo.attachment.url(:small), alt: project.name - else %i.fa.fa-cube .project-slot-info %p.project-slot-name{ title: project.name }= project.name.truncate(25) - if project.best_analysis - %p.project-slot-meta= "#{project.best_analysis.code_total.to_human} lines" - %p.project-slot-meta= "#{project.active_committers} contributors" + %p.project-slot-meta= t('.lines', count: project.best_analysis.code_total.to_human) + %p.project-slot-meta= t('.contributors', count: project.active_committers) - (3 - @session_projects.size).times do .compare-project-slot.empty-slot %i.fa.fa-plus - %span Select Project + %span= t('.select_project') / Compare Action Button .compare-action @@ -38,6 +38,6 @@ %input{ type: 'hidden', name: "project_#{i}", value: p.name } %button.btn-compare-submit{ type: 'submit', disabled: @session_projects.size < 2 } - if @session_projects.size >= 2 - = "Compare #{@session_projects.size} Projects" + = t('.compare_n_projects', count: @session_projects.size) - else - Compare (Select at least 2) + = t('.compare_select_at_least') diff --git a/app/views/shared/search_dingus/_page_entries_info.html.haml b/app/views/shared/search_dingus/_page_entries_info.html.haml index eb3f78ec3..985647b93 100644 --- a/app/views/shared/search_dingus/_page_entries_info.html.haml +++ b/app/views/shared/search_dingus/_page_entries_info.html.haml @@ -1,8 +1,7 @@ .pagination-info - if collection.respond_to?(:current_page) && !collection.length.zero? - Showing page - %span.page-number= collection.current_page - of - %span.page-number= collection.total_pages + = t('.pagination_status', + current_page: number_with_delimiter(collection.current_page), + total_pages: number_with_delimiter(collection.total_pages)) - elsif !total_count.to_i.zero? = t('.count_status', current_count: collection.length, total_count: number_with_delimiter(total_count)) diff --git a/config/locales/en.yml b/config/locales/en.yml index 403d2f936..f46f88118 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -398,7 +398,12 @@ en: session_projects: menu: - compare_projects: 'Compare Projects:' + compare_projects: 'Compare Projects (%{count}/3)' + select_project: 'Select Project' + lines: '%{count} lines' + contributors: '%{count} contributors' + compare_n_projects: 'Compare %{count} Projects' + compare_select_at_least: 'Compare (Select at least 2)' limit_reached: limit reached create: limit_exceeded: Project count limit exceeded diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 06c7de8b9..60339dc99 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -49,16 +49,20 @@ en: claimed_by: "Claimed by" analyzed_ago: "Analyzed %{time} ago" no_analysis_available: "No analysis available" + no_description_available: "No description available" + mostly_written_in: "Mostly written in %{lang}" + not_available: "Not available" + licenses: "Licenses:" + no_declared_licenses: "No declared licenses" + tags: "Tags" + n_more: "%{n} more..." loc: "lines of code" current_contributors: "current contributors" since_last_commit: "since last commit" users_on_open_hub: "users on Open Hub" review: "Review" - mostly_written_in: "Mostly written in %{lang}" + i_use_this: "I Use This" mostly_written_lang_not_available: "Mostly written in language not available" - licenses: "Licenses:" - no_declared_licenses: "No declared licenses" - n_more: "%{n} more..." index_managed: page_title: "%{name} : Projects - Open Hub" managed_projects: "Managed Projects" @@ -110,7 +114,6 @@ en: community_rating: "Community Rating" in_a_nutshell: "In a Nutshell, %{name}" lines_of_code: "Lines of Code" - contributors: "Contributors" total_commits: "Total Commits" note: "Note:" organization: "Organization" @@ -121,9 +124,6 @@ en: similar_projects_label: "Similar Projects" managers_label: "Managers" become_first_manager: "Become the first manager of %{what}" - news: "News" - sharing_widgets: "Sharing Widgets" - related_projects: "Related Projects" code_data: "Code Data" languages_label: "Languages" cost_estimates: "Cost Estimates" From f6bea7d3c72ec55e9ddfeaaa14c99280ee37e20c Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 16 Mar 2026 17:04:58 +0530 Subject: [PATCH 15/53] Fix testcases --- app/views/projects/show.html.haml | 9 +++++++-- app/views/projects/show/_header_redesign.html.haml | 9 ++++++++- config/locales/projects.en.yml | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 879072ea9..4c94e2bdc 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -52,7 +52,7 @@ .card-content %h3= t('.project_summary') - if @project.description.present? - %p= simple_format(@project.description.strip_tags) + = simple_format(@project.description.strip_tags) - else %p = t('.no_description') @@ -84,6 +84,11 @@ - if @project.edit_authorized? = link_to t('.add_tags'), project_tags_path(@project) + - if @project.project_badges.active.any? + %section#project_badges + = show_badges + = more_badges_link + / In a Nutshell .gradient-card .card-content @@ -213,7 +218,7 @@ - if @project.active_managers.present? = link_to "#{@project.active_managers.count} managers", project_managers_path(@project) - else - = link_to t('.become_first', what: @project.name), new_project_manager_path(@project) + = link_to t('.become_first_manager', what: @project.name), new_project_manager_path(@project) / Mobile: Collapsible accordion cards .mobile-accordion diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml index 6283af5ef..3c7cd197a 100644 --- a/app/views/projects/show/_header_redesign.html.haml +++ b/app/views/projects/show/_header_redesign.html.haml @@ -69,7 +69,14 @@ %line{ x1: "4", y1: "22", x2: "4", y2: "15" } = t('.report_duplicate') - %p.project-description{ itemprop: 'description' } + - if current_user_is_admin? + = link_to oh_admin_project_jobs_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %rect{ x: "3", y: "7", width: "18", height: "13", rx: "2", ry: "2" } + %path{ d: "M16 3v4M8 3v4M3 11h18" } + = t('projects.header.job') + + .project-description{ itemprop: 'description' } - if @project.description.present? = truncate(@project.description.strip_tags, length: 200) - else diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 06c7de8b9..aaf00da22 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -120,7 +120,7 @@ en: add_code_location_link: "Add code location" similar_projects_label: "Similar Projects" managers_label: "Managers" - become_first_manager: "Become the first manager of %{what}" + become_first_manager: "Become the first manager for %{what}" news: "News" sharing_widgets: "Sharing Widgets" related_projects: "Related Projects" From 272b894a1b405bda8d01b122c79de87efb56a004 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 16 Mar 2026 20:35:21 +0530 Subject: [PATCH 16/53] conflict fix --- app/assets/stylesheets/dark_theme.sass | 105 --- app/assets/stylesheets/footer.sass | 102 --- app/assets/stylesheets/home.sass | 721 --------------------- app/assets/stylesheets/page.sass | 60 +- app/views/home/_top_contributors.html.haml | 27 - app/views/home/index.html.haml | 26 - config/initializers/assets.rb | 4 - 7 files changed, 1 insertion(+), 1044 deletions(-) diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 5699aabc0..5b8198f4f 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -2,31 +2,20 @@ // Applied when .dark class is added to html element html.dark -<<<<<<< OTWO-7546 // Body and background colors - matches home page sections body background-color: #1D0631 !important -======= - // Body and background colors - body - background-color: #0f172a !important ->>>>>>> ui-redesign color: #e2e8f0 !important // Page container #page, .container#page -<<<<<<< OTWO-7546 background-color: #1D0631 !important -======= - background-color: #0f172a !important ->>>>>>> ui-redesign // Header stays with purple gradient (no change needed) // Footer stays with purple gradient (no change needed) // Content areas #page-contents, #page_contents -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important @@ -42,23 +31,6 @@ html.dark color: #e2e8f0 td, th border-color: #4b5563 !important -======= - background-color: #1e293b - color: #e2e8f0 - - // Cards and wells - .well - background-color: #1e293b !important - color: #e2e8f0 !important - border-color: #334155 !important - - // Tables - table - background-color: #1e293b - color: #e2e8f0 - td, th - border-color: #334155 !important ->>>>>>> ui-redesign color: #e2e8f0 !important // Links @@ -77,21 +49,14 @@ html.dark // Forms input, textarea, select -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important border-color: #4b5563 !important -======= - background-color: #1e293b !important - color: #e2e8f0 !important - border-color: #334155 !important ->>>>>>> ui-redesign &::placeholder color: #94a3b8 !important // Billboard/Showcase .billboard -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important @@ -101,17 +66,6 @@ html.dark // Project container #project_container background-color: #1D0631 !important -======= - background-color: #1e293b !important - color: #e2e8f0 !important - - .showcase - background-color: #0f172a !important - - // Project container - #project_container - background-color: #0f172a !important ->>>>>>> ui-redesign color: #e2e8f0 !important // Headings @@ -120,7 +74,6 @@ html.dark // Alerts .alert -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important border-color: #4b5563 !important @@ -141,28 +94,6 @@ html.dark .home_page_row .col-md-4 background-color: #1D0631 !important -======= - background-color: #1e293b !important - color: #e2e8f0 !important - border-color: #334155 !important - - // Dropdown menus - .dropdown-menu - background-color: #1e293b !important - border-color: #334155 !important - li a - color: #e2e8f0 !important - &:hover - background-color: #334155 !important - - // Home page sections - .top_ten, .top_ten.middle, .top_ten.last - background-color: #1e293b !important - color: #e2e8f0 !important - - .home_page_row .col-md-4 - background-color: #1e293b !important ->>>>>>> ui-redesign // Navigation menu - keep white text on purple gradient .navbar, #navbar-inner @@ -171,7 +102,6 @@ html.dark // Search inputs .for_search_all_code -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important border-color: #4b5563 !important @@ -179,20 +109,10 @@ html.dark // Badges .badge background-color: rgba(90, 42, 130, 0.5) !important -======= - background-color: #1e293b !important - color: #e2e8f0 !important - border-color: #334155 !important - - // Badges - .badge - background-color: #334155 !important ->>>>>>> ui-redesign color: #e2e8f0 !important // Panels .panel -<<<<<<< OTWO-7546 background-color: #1D0631 !important border-color: #4b5563 !important .panel-heading @@ -200,20 +120,10 @@ html.dark color: #e2e8f0 !important .panel-body background-color: #1D0631 !important -======= - background-color: #1e293b !important - border-color: #334155 !important - .panel-heading - background-color: #334155 !important - color: #e2e8f0 !important - .panel-body - background-color: #1e293b !important ->>>>>>> ui-redesign color: #e2e8f0 !important // Code blocks pre, code -<<<<<<< OTWO-7546 background-color: #1D0631 !important color: #e2e8f0 !important border-color: #4b5563 !important @@ -224,18 +134,6 @@ html.dark .right_border border-right-color: #4b5563 !important -======= - background-color: #1e293b !important - color: #e2e8f0 !important - border-color: #334155 !important - - // Borders - .mezzo - border-top-color: #334155 !important - - .right_border - border-right-color: #334155 !important ->>>>>>> ui-redesign // Text colors .signature_color @@ -250,7 +148,6 @@ html.dark .good color: #4ade80 !important -<<<<<<< OTWO-7546 // Project security: Did You Know block (legacy and redesign templates) #project_container .project_row #did_you_know @@ -271,5 +168,3 @@ html.dark color: #60a5fa !important &:hover color: #93c5fd !important -======= ->>>>>>> ui-redesign diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index 69279b8d1..61f45a71a 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -8,15 +8,11 @@ footer padding: 0 width: 100% max-width: 100% -<<<<<<< OTWO-7546 // Matches Figma: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 -======= ->>>>>>> ui-redesign .footer-container position: relative max-width: 1280px margin: 0 auto -<<<<<<< OTWO-7546 padding: 24px 16px @media (min-width: 640px) padding: 32px 24px @@ -36,66 +32,6 @@ footer grid-template-columns: repeat(3, 1fr) gap: 48px margin-bottom: 48px -======= - padding: 48px 32px 24px 32px - - // Mobile: < 640px - @media (max-width: 639px) - .footer-container - padding: 32px 16px 16px 16px - .footer-grid - gap: 32px - margin-bottom: 24px - .footer-left - .logo_img - height: 28px - .footer-mid, .footer-right - h3 - font-size: 10px - margin-bottom: 12px - a - font-size: 10px - li - margin-bottom: 8px - .footer-bottom - .footer-bottom-content - gap: 8px - .copyright - font-size: 9px - text-align: center - .follow-us - span - font-size: 10px - a - width: 28px - height: 28px - i - font-size: 14px - - // Tablet: 640px - 767px - @media (min-width: 640px) and (max-width: 767px) - .footer-container - padding: 40px 24px 20px 24px - .footer-grid - gap: 40px - margin-bottom: 28px - - // Tablet landscape: 768px - 1023px - @media (min-width: 768px) and (max-width: 1023px) - .footer-container - padding: 44px 32px 22px 32px - .footer-grid - gap: 36px - - .footer-grid - display: grid - grid-template-columns: 1fr - gap: 48px - margin-bottom: 32px - @media (min-width: 768px) - grid-template-columns: repeat(3, 1fr) - gap: 48px ->>>>>>> ui-redesign // Matches Figma: h-8 sm:h-10 .footer-left @@ -112,7 +48,6 @@ footer @media (min-width: 640px) height: 40px -<<<<<<< OTWO-7546 // Matches Figma: text-xs sm:text-sm for headers and links .footer-mid, .footer-right h3 @@ -124,18 +59,6 @@ footer letter-spacing: 0.05em @media (min-width: 640px) font-size: 14px -======= - .footer-mid, .footer-right - h3 - font-size: 11px - font-weight: 600 - color: white !important - margin-bottom: 16px - text-transform: uppercase - letter-spacing: 0.05em - @media (min-width: 640px) - font-size: 13px ->>>>>>> ui-redesign margin-bottom: 12px @media (min-width: 768px) margin-bottom: 16px @@ -144,21 +67,13 @@ footer margin: 0 padding: 0 li -<<<<<<< OTWO-7546 margin-bottom: 6px -======= - margin-bottom: 10px ->>>>>>> ui-redesign @media (min-width: 640px) margin-bottom: 8px @media (min-width: 768px) margin-bottom: 10px p -<<<<<<< OTWO-7546 margin: 0 0 6px 0 -======= - margin: 0 0 10px 0 ->>>>>>> ui-redesign @media (min-width: 640px) margin: 0 0 8px 0 @media (min-width: 768px) @@ -166,19 +81,11 @@ footer a color: #d1d5db !important text-decoration: none -<<<<<<< OTWO-7546 font-size: 12px font-weight: 400 transition: color 0.3s ease @media (min-width: 640px) font-size: 14px -======= - font-size: 11px - font-weight: 400 - transition: color 0.3s ease - @media (min-width: 640px) - font-size: 13px ->>>>>>> ui-redesign &:hover color: white !important @@ -198,10 +105,7 @@ footer gap: 12px @media (min-width: 640px) flex-direction: row -<<<<<<< OTWO-7546 // Matches Figma: text-[10px] sm:text-xs -======= ->>>>>>> ui-redesign .copyright color: #9ca3af !important font-size: 10px @@ -216,20 +120,14 @@ footer order: 1 @media (min-width: 640px) order: 2 -<<<<<<< OTWO-7546 // Matches Figma: text-xs sm:text-sm -======= ->>>>>>> ui-redesign span color: #d1d5db !important font-size: 12px font-weight: 500 @media (min-width: 640px) font-size: 14px -<<<<<<< OTWO-7546 // Matches Figma: w-8 h-8 -======= ->>>>>>> ui-redesign a display: flex align-items: center diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index fb07cedad..8e46c6e06 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,4 +1,3 @@ -<<<<<<< OTWO-7546 // ======================================== // Hero Section - Figma Design System // ======================================== @@ -79,149 +78,17 @@ margin: 0 auto 20px width: 100% box-sizing: border-box -======= -// Hero Section - Updated to match Figma exactly -.hero_section - padding-top: 60px - padding-bottom: 40px - padding-left: 16px - padding-right: 16px - background: #fff !important - transition: background 0.3s ease - @media (min-width: 640px) - padding-top: 72px - padding-left: 24px - padding-right: 24px - @media (min-width: 1024px) - padding-top: 88px - padding-left: 32px - padding-right: 32px - // Dark mode support - html.dark & - background: #1D0631 !important - -.hero_container - max-width: 80rem - margin: 0 auto - -// Hero Header - Figma Specs -.hero_header - text-align: center - margin-bottom: 24px - -.hero_title - font-size: 1.875rem !important - font-weight: 700 - color: #5A2A82 - margin-bottom: 8px - line-height: 2.25rem !important - transition: color 0.3s ease - @media (min-width: 640px) - font-size: 2.25rem !important - line-height: 2.5rem !important - @media (min-width: 768px) - font-size: 3rem !important - line-height: 1 !important - // Dark mode support - html.dark & - color: #ffffff - -.hero_subtitle - font-size: 0.875rem !important - color: #6b7280 - max-width: 42rem - margin: 0 auto - line-height: 1.25rem !important - transition: color 0.3s ease - @media (min-width: 640px) - font-size: 1rem !important - line-height: 1.5rem !important - // Dark mode support - html.dark & - color: #9ca3af - -// Search Container - Figma Specs -.search_container - max-width: 48rem - margin: 0 auto 20px - -.search_form - margin-bottom: 0 - position: relative - -.search_input_wrapper - position: relative - -.search_icon - position: absolute - top: 50% - transform: translateY(-50%) - left: 8px - font-size: 14px - color: #9ca3af - pointer-events: none - z-index: 1 - transition: color 0.3s ease - @media (min-width: 640px) - left: 24px - font-size: 20px - // Dark mode support - html.dark & - color: #6b7280 - -.search_input - width: 100% - padding: 10px 12px 10px 32px - font-size: 11px !important - border: 2px solid #e5e7eb - border-radius: 16px - outline: none - background: #fff - color: #111827 - font-family: inherit - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) - transition: all 0.2s ease - &::placeholder - color: #9ca3af - &:hover - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - &:focus - border-color: #0E4B7A - box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) - &::placeholder - color: #d1d5db - @media (min-width: 640px) - padding: 16px 16px 16px 56px - font-size: 16px !important - // Dark mode support - html.dark & - background: #1D0631 !important - color: #ffffff - border-color: #374151 - &::placeholder - color: #6b7280 - &:focus - border-color: #2E8B9E - box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) ->>>>>>> ui-redesign // Rotating Stats - Figma Animation .rotating_stats text-align: center margin-top: 12px -<<<<<<< OTWO-7546 margin-bottom: 24px -======= ->>>>>>> ui-redesign height: 20px #global_statistics p -<<<<<<< OTWO-7546 font-size: 12px -======= - font-size: 12px !important ->>>>>>> ui-redesign font-weight: 500 background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) -webkit-background-clip: text @@ -238,14 +105,10 @@ display: block // Dark mode support html.dark & -<<<<<<< OTWO-7546 background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) !important -webkit-background-clip: text !important -webkit-text-fill-color: transparent !important background-clip: text !important -======= - background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) ->>>>>>> ui-redesign // Old join_now and home_page_row styles removed - using new card-based design // Old placeholder and btn_join_now styles removed - handled in new components @@ -253,7 +116,6 @@ @extend .section_bg margin-top: 20px margin-left: 0 -<<<<<<< OTWO-7546 padding: 32px 16px width: 100% @media (min-width: 640px) @@ -263,10 +125,6 @@ // Dark mode support html.dark & background: #1D0631 !important -======= - padding: 0 20px - width: 100% ->>>>>>> ui-redesign .top_ten_main padding-left: 40px !important padding-bottom: 6px !important @@ -352,7 +210,6 @@ a.top_ten_icon p .top_ten_link a border: 0 -<<<<<<< OTWO-7546 color: #5A2A82 font-weight: 600 font-size: 14px @@ -370,8 +227,6 @@ a.top_ten_icon p color: #ffffff &:hover color: #ffb91a -======= ->>>>>>> ui-redesign // Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors @@ -397,18 +252,11 @@ button #no-background background-color: white !important -<<<<<<< OTWO-7546 // Feature Cards Section - Figma: 1024px width .features_section max-width: 1024px margin: 0 auto 24px box-sizing: border-box -======= -// Feature Cards Section - Figma Specs -.features_section - max-width: 64rem - margin: 0 auto 32px ->>>>>>> ui-redesign @media (min-width: 768px) display: grid grid-template-columns: repeat(3, 1fr) @@ -463,11 +311,7 @@ button .feature_description font-size: 10px !important color: #6b7280 -<<<<<<< OTWO-7546 line-height: 1.625 -======= - line-height: 1.5 ->>>>>>> ui-redesign padding: 0 8px transition: color 0.3s ease @media (min-width: 640px) @@ -476,20 +320,12 @@ button html.dark & color: #9ca3af -<<<<<<< OTWO-7546 // Quick Stats Bar - Compact and centered (768px) -======= -// Quick Stats Bar - Figma Specs (grid-cols-2 md:flex) ->>>>>>> ui-redesign .quick_stats_bar display: grid grid-template-columns: repeat(2, 1fr) gap: 16px -<<<<<<< OTWO-7546 max-width: 768px -======= - max-width: 48rem ->>>>>>> ui-redesign margin: 0 auto padding: 16px background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) @@ -498,10 +334,7 @@ button border: 1px solid rgba(229, 231, 235, 0.6) box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) transition: all 0.3s ease -<<<<<<< OTWO-7546 box-sizing: border-box -======= ->>>>>>> ui-redesign @media (min-width: 768px) display: flex align-items: center @@ -510,30 +343,20 @@ button padding: 12px 24px // Dark mode support - Yellow background with purple text html.dark & -<<<<<<< OTWO-7546 background: linear-gradient(90deg, #2D1548 0%, #2D1548 50%, #2D1548 100%) border-color: rgba(255, 255, 255, 0.1) -======= - background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) - border-color: #ffb91a ->>>>>>> ui-redesign .stat_item text-align: center .stat_value -<<<<<<< OTWO-7546 font-size: 16px -======= - font-size: 16px !important ->>>>>>> ui-redesign font-weight: 700 color: #5A2A82 line-height: 1.2 transition: color 0.3s ease @media (min-width: 640px) -<<<<<<< OTWO-7546 font-size: 18px @media (min-width: 768px) font-size: 20px @@ -544,17 +367,6 @@ button .stat_label font-size: 8px -======= - font-size: 18px !important - @media (min-width: 768px) - font-size: 20px !important - // Dark mode - Purple text on yellow background - html.dark & - color: #5A2A82 - -.stat_label - font-size: 8px !important ->>>>>>> ui-redesign color: #6b7280 text-transform: uppercase font-weight: 500 @@ -562,7 +374,6 @@ button margin-top: 2px transition: color 0.3s ease @media (min-width: 640px) -<<<<<<< OTWO-7546 font-size: 9px @media (min-width: 768px) font-size: 10px @@ -570,14 +381,6 @@ button html.dark & color: #9ca3af -======= - font-size: 9px !important - @media (min-width: 768px) - font-size: 10px !important - // Dark mode - Purple text on yellow background - html.dark & - color: #5A2A82 ->>>>>>> ui-redesign .stat_divider display: none @@ -586,7 +389,6 @@ button display: block width: 1px height: 32px -<<<<<<< OTWO-7546 background: #9ca3af opacity: 1 // Dark mode - White divider with low opacity on purple background @@ -600,26 +402,10 @@ button .content_section @extend .section_bg padding: 32px 0 -======= - background: #d1d5db - opacity: 0.3 - // Dark mode - Purple divider on yellow background - html.dark & - @media (min-width: 768px) - background: #5A2A82 - opacity: 0.3 - -// Content Section -.content_section - padding: 32px 0 - background: white !important - transition: background 0.3s ease ->>>>>>> ui-redesign // Dark mode support html.dark & background: #1D0631 !important -<<<<<<< OTWO-7546 .container max-width: 1280px margin: 0 auto @@ -643,18 +429,12 @@ button .whats_new_column width: 100% display: block -======= -.contributors_column, .whats_new_column - @media (max-width: 768px) - margin-bottom: 24px ->>>>>>> ui-redesign .whats_new_join_stack display: flex flex-direction: column gap: 24px -<<<<<<< OTWO-7546 // Top Contributors Card - Matches ContributorJourney.tsx .top_contributors_card background: white @@ -814,101 +594,11 @@ button gap: 2px .top_contributors_card .contributor_name_row -======= -// Top Contributors Card -.top_contributors_card - position: relative - padding: 3px - border-radius: 16px - overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s - &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - > div - background: white - border-radius: 14px - height: 100% - -.card_header - display: flex - align-items: flex-start - gap: 16px - padding: 24px - padding-bottom: 16px - background: white - -.header_icon - width: 48px - height: 48px - background: rgba(90, 42, 130, 0.1) - border-radius: 12px - display: flex - align-items: center - justify-content: center - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - flex-shrink: 0 - i - font-size: 24px - color: #5A2A82 - -.header_content - flex: 1 - -.card_title - font-size: 18px - font-weight: 700 - color: #5A2A82 - margin-bottom: 4px - -.card_subtitle - font-size: 12px - color: rgba(90, 42, 130, 0.7) - line-height: 1.5 - -.contributors_list - padding: 16px - flex: 1 - overflow-y: auto - -.contributor_item - background: linear-gradient(90deg, #f9fafb 0%, #f3f4f6 100%) - border-radius: 12px - padding: 12px - border: 1px solid #e5e7eb - cursor: pointer - transition: all 0.3s - margin-bottom: 8px - display: flex - align-items: center - gap: 12px - &:hover - background: linear-gradient(90deg, #f5f3ff 0%, #ede9fe 100%) - border-color: #5A2A82 - -.contributor_avatar - flex-shrink: 0 - .avatar_image - width: 40px - height: 40px - border-radius: 12px - object-fit: cover - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - border: 2px solid #9ca3af - -.contributor_info - flex: 1 - min-width: 0 - -.contributor_name_rank ->>>>>>> ui-redesign display: flex align-items: center gap: 8px margin-bottom: 2px -<<<<<<< OTWO-7546 .top_contributors_card .contributor_name font-size: 14px font-weight: 600 @@ -932,33 +622,11 @@ button color: #9CA3AF .top_contributors_card .contributor_stats -======= -.contributor_name - font-size: 14px - font-weight: 600 - color: #5A2A82 - margin: 0 - .contributor_link - color: inherit - text-decoration: none - &:hover - text-decoration: underline - -.contributor_rank - font-size: 9px - color: rgba(90, 42, 130, 0.6) - text-transform: uppercase - letter-spacing: 0.05em - flex-shrink: 0 - -.contributor_stats ->>>>>>> ui-redesign display: flex align-items: center gap: 12px font-size: 10px color: rgba(90, 42, 130, 0.7) -<<<<<<< OTWO-7546 html.dark & color: #9CA3AF @@ -1101,79 +769,6 @@ button display: block @media (min-width: 640px) padding: 0 24px 24px !important -======= - .stat_item - display: flex - align-items: center - gap: 4px - i - font-size: 10px - -.contributor_arrow - flex-shrink: 0 - color: rgba(90, 42, 130, 0.4) - transition: color 0.3s - i - font-size: 16px - -.contributor_item:hover .contributor_arrow - color: #5A2A82 - -.view_all_link - padding: 16px - .btn_view_all - width: 100% - display: flex - align-items: center - justify-center - gap: 8px - font-size: 14px - font-weight: 600 - color: #5A2A82 - transition: color 0.3s - padding: 12px - border-top: 1px solid rgba(90, 42, 130, 0.2) - text-decoration: none - i - font-size: 16px - &:hover - color: #1D0631 - -// What's New Card -.whats_new_card - position: relative - padding: 3px - border-radius: 16px - overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s - &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% - -.card_header_section - padding: 24px - padding-bottom: 16px - background: white - -.whats_new_title - font-size: 20px - font-weight: 700 - color: #5A2A82 - margin-bottom: 4px - -.whats_new_subtitle - font-size: 12px - color: rgba(90, 42, 130, 0.7) - -.featured_image_section - padding: 0 24px 24px ->>>>>>> ui-redesign .featured_image_link display: block @@ -1189,7 +784,6 @@ button .featured_image width: 100% height: auto -<<<<<<< OTWO-7546 display: block max-width: 100% transition: transform 0.3s @@ -1211,31 +805,10 @@ button &:hover box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) -======= - transition: transform 0.3s - -// Join Now Card -.join_now_card - position: relative - padding: 3px - border-radius: 16px - overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) - transition: box-shadow 0.3s - &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - .card_inner - background: white - border-radius: 14px - overflow: hidden - height: 100% ->>>>>>> ui-redesign .join_header display: flex align-items: center -<<<<<<< OTWO-7546 gap: 12px !important margin-bottom: 16px !important padding: 0 !important @@ -1243,15 +816,6 @@ button .join_icon width: 40px height: 40px -======= - gap: 12px - padding: 24px - padding-bottom: 16px - -.join_icon - width: 48px - height: 48px ->>>>>>> ui-redesign background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) border-radius: 12px display: flex @@ -1259,7 +823,6 @@ button justify-content: center box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) flex-shrink: 0 -<<<<<<< OTWO-7546 @media (min-width: 640px) width: 48px height: 48px @@ -1268,17 +831,11 @@ button color: white @media (min-width: 640px) font-size: 24px -======= - i - font-size: 24px - color: white ->>>>>>> ui-redesign .join_header_text flex: 1 .join_title -<<<<<<< OTWO-7546 font-size: 18px !important font-weight: 700 !important color: #5A2A82 !important @@ -1302,284 +859,6 @@ button // Dark mode support html.dark & color: #9ca3af !important -======= - font-size: 20px - font-weight: 700 - color: #5A2A82 - margin-bottom: 2px - -.join_subtitle - font-size: 12px - color: #6b7280 - -.benefits_list - padding: 0 24px - margin-bottom: 24px - -.benefit_item - display: flex - align-items: flex-start - gap: 12px - margin-bottom: 12px - -.benefit_icon - width: 32px - height: 32px - border-radius: 8px - display: flex - align-items: center - justify-content: center - flex-shrink: 0 - margin-top: 2px - -.benefit_icon_award - background: rgba(90, 42, 130, 0.1) - i - font-size: 16px - color: #5A2A82 - -.benefit_icon_trending - background: rgba(29, 6, 49, 0.1) - i - font-size: 16px - color: #1D0631 - -.benefit_content - flex: 1 - -.benefit_title - font-size: 13px - font-weight: 600 - color: #111827 - margin-bottom: 2px - -.benefit_description - font-size: 11px - color: #6b7280 - line-height: 1.5 - -.join_cta - padding: 0 24px 24px - -.btn_join_cta - width: 100% - padding: 12px 24px - background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) - color: white - border-radius: 8px - font-weight: 600 - transition: all 0.3s - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - display: flex - align-items: center - justify-content: center - gap: 8px - font-size: 16px - text-decoration: none - border: none - cursor: pointer - &:hover - background: linear-gradient(90deg, #1D0631dd 0%, #5A2A82dd 100%) - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) - color: white - text-decoration: none - i - font-size: 16px - -// User Journeys Section -.user_journeys_section - background: #1D0631 - padding: 64px 0 - @media (max-width: 768px) - padding: 48px 0 - -.journeys_header - text-align: center - margin-bottom: 48px - -.journeys_title - font-size: 30px - font-weight: 700 - color: white - margin-bottom: 12px - @media (max-width: 768px) - font-size: 24px - -.journeys_subtitle - font-size: 18px - color: #d1d5db - max-width: 768px - margin: 0 auto - @media (max-width: 768px) - font-size: 16px - -// Mobile View - Collapsible -.journeys_mobile - display: block - @media (min-width: 1024px) - display: none - -.journey_card - position: relative - padding: 3px - border-radius: 12px - overflow: hidden - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - margin-bottom: 16px - transition: all 0.3s - -.journey_card_header - width: 100% - display: flex - align-items: center - gap: 16px - padding: 16px - text-align: left - transition: background 0.3s - background: #1D0631 - border: none - cursor: pointer - border-radius: 10px - &:hover - background: rgba(90, 42, 130, 0.2) - -.journey_header_icon - width: 40px - height: 40px - border-radius: 8px - display: flex - align-items: center - justify-content: center - flex-shrink: 0 - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) - i - font-size: 20px - color: white - -.journey_header_title - flex: 1 - font-size: 14px - font-weight: 600 - color: white - -.journey_toggle_icon - flex-shrink: 0 - i - font-size: 20px - color: white - -.journey_card_content - display: none - padding: 0 16px 16px - background: #1D0631 - border-radius: 0 0 10px 10px - -.journey_card.expanded .journey_card_content - display: block - animation: slideDown 0.3s ease-out - -@keyframes slideDown - from - opacity: 0 - transform: translateY(-10px) - to - opacity: 1 - transform: translateY(0) - -.journey_description - font-size: 14px - color: #d1d5db - line-height: 1.5 - margin-bottom: 16px - -.journey_steps - background: rgba(90, 42, 130, 0.2) - border-radius: 8px - padding: 16px - border: 1px solid rgba(90, 42, 130, 0.4) - -.steps_title - font-size: 12px - font-weight: 600 - color: #e5e7eb - text-transform: uppercase - letter-spacing: 0.05em - margin-bottom: 8px - -.steps_list - list-style: none - padding: 0 - margin: 0 - li - display: flex - align-items: flex-start - gap: 8px - font-size: 14px - color: #d1d5db - margin-bottom: 6px - &:last-child - margin-bottom: 0 - -.step_bullet - color: #ffb91a - font-weight: 700 - -// Desktop View - Grid -.journeys_desktop - display: none - @media (min-width: 1024px) - display: grid - grid-template-columns: repeat(3, 1fr) - gap: 24px - -.journey_grid_card - border-radius: 12px - padding: 24px - border: 1px solid rgba(90, 42, 130, 0.4) - background: #1D0631 - transition: all 0.3s - &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) - border-color: #ffb91a - -.grid_card_icon - width: 56px - height: 56px - border-radius: 12px - display: flex - align-items: center - justify-content: center - margin-bottom: 16px - transition: transform 0.3s - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) - background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) - i - font-size: 28px - color: white - -.journey_grid_card:hover .grid_card_icon - transform: scale(1.1) - -.grid_card_title - font-size: 20px - font-weight: 700 - color: white - margin-bottom: 8px - -.grid_card_description - font-size: 14px - color: #d1d5db - margin-bottom: 16px - line-height: 1.5 - -.grid_card_steps - background: rgba(90, 42, 130, 0.2) - border-radius: 8px - padding: 16px - border: 1px solid rgba(90, 42, 130, 0.4) - ->>>>>>> ui-redesign .benefits_list padding: 0 !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index aaceef1ba..dfc37ab81 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -18,26 +18,17 @@ body color: #000 width: 100% overflow-x: hidden -<<<<<<< OTWO-7546 background-color: #f9fafb !important -======= ->>>>>>> ui-redesign .container#page, #page width: 100% !important max-width: 100% !important -<<<<<<< OTWO-7546 background-color: #f9fafb margin: 0 !important padding: 0 !important transition: background-color 0.3s ease html.dark & background-color: #1D0631 -======= - background-color: white - margin: 0 !important - padding: 0 !important ->>>>>>> ui-redesign header // Figma design: gradient background for entire header @@ -47,14 +38,9 @@ header margin: 0 padding: 0 width: 100% -<<<<<<< OTWO-7546 position: sticky top: 0 z-index: 1000 -======= - position: relative - z-index: 1 ->>>>>>> ui-redesign @media only all and (min-width: 320px) and (max-width: 480px) body @@ -144,10 +130,6 @@ header gap: 24px #page_contents max-width: 100% -<<<<<<< OTWO-7546 -======= - padding: 0 20px ->>>>>>> ui-redesign .separator-div @include site-separator-color @@ -246,11 +228,7 @@ header gap: 12px .logo-div .logo_img -<<<<<<< OTWO-7546 height: 32px !important -======= - height: 40px ->>>>>>> ui-redesign .company-div .navbar_large_text font-size: 20px @@ -337,13 +315,9 @@ header .navbar // Figma design: gradient background with purple theme -<<<<<<< OTWO-7546 position: sticky top: 0 z-index: 1000 -======= - position: relative ->>>>>>> ui-redesign background: linear-gradient(to right, #000000, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) @@ -358,11 +332,7 @@ header align-items: flex-start flex-wrap: wrap overflow: visible -<<<<<<< OTWO-7546 z-index: 1001 -======= - z-index: 100 ->>>>>>> ui-redesign // Override global button styles for navbar .btn border: none !important @@ -404,14 +374,11 @@ header text-decoration: none display: flex align-items: center -<<<<<<< OTWO-7546 .logo_img height: 20px width: auto display: block object-fit: contain -======= ->>>>>>> ui-redesign .company-div color: white !important display: flex @@ -640,28 +607,17 @@ header .mobile-menu display: none width: 100% -<<<<<<< OTWO-7546 -======= - flex-basis: 100% ->>>>>>> ui-redesign background: linear-gradient(to right, #1D0631, #5A2A82) backdrop-filter: blur(12px) -webkit-backdrop-filter: blur(12px) border-top: 1px solid rgba(255, 255, 255, 0.15) padding: 20px 0 -<<<<<<< OTWO-7546 position: absolute top: 64px left: 0 right: 0 z-index: 1002 overflow: visible -======= - position: relative - z-index: 100 - overflow: visible - order: 2 ->>>>>>> ui-redesign box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) &.show display: block !important @@ -743,11 +699,7 @@ header background-color: #FFB91A !important color: #000 !important border: none !important -<<<<<<< OTWO-7546 border-radius: 12px -======= - border-radius: 8px ->>>>>>> ui-redesign font-weight: 600 font-size: 15px text-decoration: none @@ -772,12 +724,8 @@ header padding: 14px 20px background-color: transparent !important color: #FFB91A !important - border: 2px solid #FFB91A !important -<<<<<<< OTWO-7546 + border: 2px solid #FFB91A !important border-radius: 12px -======= - border-radius: 8px ->>>>>>> ui-redesign font-weight: 600 font-size: 15px text-decoration: none @@ -793,7 +741,6 @@ header &:active transform: translateY(0) box-shadow: none -<<<<<<< OTWO-7546 html.dark & background-color: transparent !important color: #FFB91A !important @@ -801,8 +748,6 @@ header &:hover background-color: rgba(255, 185, 26, 0.15) !important -======= ->>>>>>> ui-redesign .dropdown#logged_user_menu position: relative @@ -1190,7 +1135,6 @@ fieldset .language_percentage_indicator width: 3rem -<<<<<<< OTWO-7546 // Dark mode guards for legacy project security/no-analysis blocks html.dark #project_container @@ -1229,8 +1173,6 @@ body.dark background-color: #1D0631 !important border: 1px solid #4b5563 !important -======= ->>>>>>> ui-redesign // Full-width layout styles .page-content-wrapper width: 100% diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml index b465446e9..1544ad3b9 100644 --- a/app/views/home/_top_contributors.html.haml +++ b/app/views/home/_top_contributors.html.haml @@ -7,7 +7,6 @@ %p.card_subtitle= t('.most_active_developers') .contributors_list -<<<<<<< OTWO-7546 - @home.most_active_contributors&.each_with_index do |contributor, index| = link_to account_path(contributor), class: 'contributor_item' do .contributor_avatar @@ -34,29 +33,3 @@ = link_to accounts_path, class: 'view_all_link' do %i.fa.fa-users %span= t('.view_all_contributors') -======= - - @top_contributors&.each_with_index do |contributor, index| - .contributor_item{class: "contributor-rank-#{index + 1}"} - .contributor_avatar - = link_to account_path(contributor) do - = gravatar_tag(contributor.email, size: 40, class: 'avatar_image', alt: contributor.name) - .contributor_info - .contributor_name_rank - %h4.contributor_name - = link_to contributor.name, account_path(contributor), class: 'contributor_link' - %span.contributor_rank= "##{index + 1}" - .contributor_stats - %span.stat_item - %i.fa.fa-code-fork - = number_with_delimiter(contributor.commits_count || 0) - %span.stat_item - %i.fa.fa-folder - = "#{contributor.projects_count || 0} projects" - .contributor_arrow - %i.fa.fa-chevron-right - - .view_all_link - = link_to accounts_path, class: 'btn btn_view_all' do - %i.fa.fa-users - = t('.view_all_contributors') ->>>>>>> ui-redesign diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 626692cac..82762ba3e 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,10 +1,6 @@ - content_for(:html_title) { t('.page_title') } -<<<<<<< OTWO-7546 - content_for(:javascript) do = javascript_include_tag 'project_swimlanes' -======= -- content_for(:javascript) { javascript_include_tag 'rotating_stats' } ->>>>>>> ui-redesign / Hero Section .hero_section @@ -19,12 +15,8 @@ %form.search_form#search_form{ action: projects_path } %input{ type: :hidden, name: 'ref', value: 'homepage' } .search_input_wrapper -<<<<<<< OTWO-7546 .search_icon_wrapper %i.fa.fa-search.search_icon -======= - %i.fa.fa-search.search_icon ->>>>>>> ui-redesign %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } / Rotating Stats @@ -73,7 +65,6 @@ / Top Contributors and What's New Section .content_section .container -<<<<<<< OTWO-7546 .content_grid / Top Contributors - 35% .contributors_column @@ -81,29 +72,12 @@ / What's New and Join Now - 65% .whats_new_column -======= - .row - / Top Contributors - 35% - .col-md-4.col-sm-12.contributors_column - = render partial: '/home/top_contributors' - - / What's New and Join Now - 65% - .col-md-8.col-sm-12.whats_new_column ->>>>>>> ui-redesign .whats_new_join_stack = render partial: '/home/whats_new' = render partial: '/home/join_now_home' -<<<<<<< OTWO-7546 / Project Swimlanes Section = home_top_lists / User Journeys Section = render partial: '/home/user_journeys' -======= -/ User Journeys Section -= render partial: '/home/user_journeys' - -.landing.col-md-12.col-sm-12.col-xs-12.last_section - = home_top_lists ->>>>>>> ui-redesign diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 641b63229..052e2ce67 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,10 +6,6 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css -<<<<<<< OTWO-7546 api/vulnerability.sass api/vulnerability.js project_swimlanes.js] -======= - api/vulnerability.sass api/vulnerability.js rotating_stats.js] ->>>>>>> ui-redesign Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] From 064b74f3ae8c1c701b25999f171fefaf1b59f640 Mon Sep 17 00:00:00 2001 From: bd-vaibhav Date: Mon, 16 Mar 2026 22:03:11 +0530 Subject: [PATCH 17/53] OTWO-7596 Implementation of project search index page (#1875) * OTWO-7596 Implementation of project search index page --------- Co-authored-by: Kumari Niharika --- app/assets/javascripts/search_dingus.js | 75 +- .../javascripts/session_projects.js.coffee | 145 ++-- app/assets/stylesheets/base.sass | 3 + app/assets/stylesheets/buttons.sass | 771 +++++++++++++----- app/assets/stylesheets/oh-styles.sass | 45 + app/assets/stylesheets/projects.sass | 648 +++++++++++++-- app/assets/stylesheets/search-dingus.sass | 649 +++++++++++---- app/assets/stylesheets/session_projects.sass | 288 ++++++- app/views/projects/_project_index.html.haml | 210 +++-- app/views/projects/index.html.haml | 58 +- app/views/session_projects/_menu.html.haml | 61 +- app/views/shared/_search.html.haml | 31 +- app/views/shared/_search_dingus.html.haml | 6 +- .../_page_entries_info.html.haml | 10 +- .../search_dingus/_search_bar.html.haml | 18 +- .../shared/search_dingus/_sort.html.haml | 59 +- config/locales/en.yml | 7 +- config/locales/projects.en.yml | 13 +- test/controllers/projects_controller_test.rb | 12 +- .../session_projects_controller_test.rb | 2 +- 20 files changed, 2395 insertions(+), 716 deletions(-) diff --git a/app/assets/javascripts/search_dingus.js b/app/assets/javascripts/search_dingus.js index f98d1251a..86c7a9120 100644 --- a/app/assets/javascripts/search_dingus.js +++ b/app/assets/javascripts/search_dingus.js @@ -20,58 +20,103 @@ var ohloh = (function builder($) { init: function(){ var $scope = $('.ux-dropdown'); var $search_text_field = $scope.find('.text'); - var dropdownHead = $('a span.selection', $scope); + var dropdownHead = $('button span.selection, a span.selection', $scope); var getSelectedValue = function() { return dropdownHead.first().attr('val'); }; - $('.dropdown-menu li a', $scope).click(function() { + $('.dropdown-menu .dropdown-item', $scope).click(function() { var selectedText = $(this).attr('val') dropdownHead.attr('val',selectedText) dropdownHead.html( $(this).html() ) dropdownHead.parents('form').attr('action',selectedText) + // Update active class + $('.dropdown-menu .dropdown-item', $scope).removeClass('active') + $(this).addClass('active') if(selectedText != "//code.ohloh.net/search"){ dropdownHead.parents('form').removeAttr('target'); } }); - $('ul.dropdown-menu li a', $scope).on('click', 'a', function() { + $('.dropdown-menu .dropdown-item', $scope).on('click', function() { var $this = $(this); var section_name = $this.text(); - $('ul.dropdown-menu li a', $scope).removeClass('selected'); + $('.dropdown-menu .dropdown-item', $scope).removeClass('selected'); $this.addClass('selected'); - $('.ux-dropdown a span.selection', $scope).html(section_name); + $('.ux-dropdown button span.selection', $scope).html(section_name); }); $(document).ajaxStop($.unblockUI); if( (/\/p(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="p"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="p"]', $scope).addClass('default'); } if( (/\/orgs(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="orgs"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="orgs"]', $scope).addClass('default'); } if( (/\/(posts|forums)(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="posts"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="posts"]', $scope).addClass('default'); } if( (/\/(people|accounts|committers)(\/|\?|$)/ig).test(window.location.href) ) { - $('a', $scope).removeClass('default'); - $('a[val="people"]', $scope).addClass('default'); + $('.dropdown-item', $scope).removeClass('default'); + $('.dropdown-item[val="people"]', $scope).addClass('default'); } - $('a.default', $scope).trigger('click'); + $('.dropdown-item.default', $scope).trigger('click'); - $('.dropdown-menu li a', $scope).on('click', 'a', function(){ + $('.dropdown-menu .dropdown-item', $scope).on('click', function(){ $('input[name="query"].search').trigger('click').focus(); }); + // Handle custom sort dropdown + $('.custom-sort-dropdown .sort-dropdown-item').on('click', function(e) { + e.preventDefault(); + var $item = $(this); + var $dropdown = $item.closest('.custom-sort-dropdown'); + var selectedValue = $item.data('value'); + var selectedLabel = $item.text(); + + // Update button text + $dropdown.find('.selection').text(selectedLabel).data('value', selectedValue); + + // Update hidden input + $dropdown.find('.sort-value-input').val(selectedValue); + + // Update active class + $dropdown.find('.sort-dropdown-item').removeClass('active'); + $item.addClass('active'); + + // Submit form + $dropdown.closest('form').submit(); + }); + + // Handle search input with cancel button + $('#query').on('input', function() { + var $input = $(this); + var $cancelBtn = $input.siblings('.search-cancel-btn'); + + if ($input.val().length > 0) { + $cancelBtn.show(); + } else { + $cancelBtn.hide(); + } + }); + + // Handle search cancel button + $('.search-cancel-btn').on('click', function() { + var $input = $('#query'); + $input.val(''); + $(this).hide(); + $input.focus(); + }); + if( (/\?[query]=.{1,}/ig).test(window.location.href) ) { var q = window.location.href.split('?')[1].split('=')[1]; diff --git a/app/assets/javascripts/session_projects.js.coffee b/app/assets/javascripts/session_projects.js.coffee index a3c968661..0487d47d2 100644 --- a/app/assets/javascripts/session_projects.js.coffee +++ b/app/assets/javascripts/session_projects.js.coffee @@ -9,83 +9,128 @@ SessionProjects = url: "/session_projects?#{ timestamp }" success: SessionProjects.success + # Initialize compare checkboxes + SessionProjects.enable() + enable: -> - # Update all checkboxes on page to match those in the menu. + # Update all compare checkboxes on page to match those in the menu. # This is primarily so that state will be cleaned up after back button. - $('.sp_input').prop('checked', false) - $('.sp_form.styled').removeClass('selected') - $('#sp_menu .sp_input').each -> - $(".sp_input[project_id='#{ $(this).attr('project_id') }']").prop('checked', true) - $("#sp_form_#{ $(this).attr('project_id') }").addClass('selected') + $('.compare-checkbox').removeClass('selected') + $('.compare-checkbox i').removeClass('fa-check-square-o').addClass('fa-square-o') - if $('#sp_menu .sp_input').length < 3 + # Mark selected projects from menu + $('#sp_menu .remove-project').each -> + project_id = $(this).data('project-id') + $(".compare-checkbox[data-project-id='#{ project_id }']").addClass('selected') + $(".compare-checkbox[data-project-id='#{ project_id }'] i").removeClass('fa-square-o').addClass('fa-check-square-o') + + if $('#sp_menu .remove-project').length < 3 # All checkboxes are enabled. - $('.sp_input') - .unbind('change') - .bind('change', SessionProjects.change) - .prop('disabled', false) - .parent().removeClass('disabled') + $('.compare-checkbox') + .off('click') + .on('click', SessionProjects.change) + .removeClass('disabled') else # Project limit reached. Can only uncheck checkboxes. - $('.sp_input') - .filter(':checked') - .unbind('change') - .bind('change', SessionProjects.change) - .prop('disabled', false) - .parent().removeClass 'disabled' + $('.compare-checkbox.selected') + .off('click') + .on('click', SessionProjects.change) + .removeClass('disabled') SessionProjects.disableUnchecked() - # Activate the x icon. - $('#sp_menu .sp_input') - .unbind('click') - .bind('click', SessionProjects.change) + # Activate the remove icons in menu. + $('#sp_menu .remove-project') + .off('click') + .on('click', SessionProjects.remove) - # Animate the appearance/disappearance of menu. - height = '0' - height = '2.6em' if $('#sp_menu .sp_input').length - $('#sp_menu').animate { 'min-height': height }, duration: 300 + # Activate toggle button + $('#compare_toggle_btn') + .off('click') + .on('click', SessionProjects.toggleTray) + + # Show/hide the entire compare bar + if $('#sp_menu .remove-project').length > 0 + $('#sp_menu').fadeIn(200) + $('#projects_index_page').addClass('has-compare-tray') + else + $('#sp_menu').fadeOut(200) + $('#projects_index_page').removeClass('has-compare-tray') disableUnchecked: -> - $('.sp_input') - .not(':checked') - .unbind('change') - .prop('disabled', true) - .parent().addClass('disabled') + $('.compare-checkbox') + .not('.selected') + .off('click') + .addClass('disabled') - busy_span: " " - compare_span: "Compare" + toggleTray: -> + $tray = $('.compare-tray-content') + $icon = $('#compare_toggle_btn .toggle-icon') + + if $tray.hasClass('expanded') + $tray.removeClass('expanded') + $icon.removeClass('rotate') + else + $tray.addClass('expanded') + $icon.addClass('rotate') + false change: -> - checked = $(this).is(':checked') - checked = false if $(this).hasClass('icon-remove-sign') - # force false for remove-icons - project_id = $(this).attr('project_id') - sel = $('.sp_input[project_id="' + project_id + '"]') - sel.siblings('span').replaceWith SessionProjects.busy_span - sel.prop('checked', checked).prop 'disabled', true - SessionProjects.disableUnchecked() - if checked + $checkbox = $(this) + return false if $checkbox.hasClass('disabled') + + is_selected = $checkbox.hasClass('selected') + project_id = $checkbox.data('project-id') + + # Show loading state + $checkbox.addClass('disabled') + + if is_selected + # Uncheck $.ajax type: 'POST' - url: '/session_projects?project_id=' + project_id + url: '/session_projects/' + project_id + data: '_method': 'delete' success: SessionProjects.success + error: -> $checkbox.removeClass('disabled') else + # Check $.ajax type: 'POST' - url: '/session_projects/' + project_id - data: '_method': 'delete' + url: '/session_projects?project_id=' + project_id success: SessionProjects.success + error: (xhr) -> + if xhr.status == 403 + alert(xhr.responseText) + $checkbox.removeClass('disabled') + false + + remove: -> + project_id = $(this).data('project-id') + $.ajax + type: 'POST' + url: '/session_projects/' + project_id + data: '_method': 'delete' + success: SessionProjects.success false success: (data, textStatus, jqXHR) -> + # Remember if the tray was expanded + wasExpanded = $('.compare-tray-content').hasClass('expanded') + + # Replace the HTML $('#sp_menu').html(data) - if _(data).isEmpty() - $('#page').css('margin-top', '0px') - else - $('#page').css('margin-top', '52px') - $('.busy').replaceWith(SessionProjects.compare_span) SessionProjects.enable() + + # Restore expanded state if it was open + if wasExpanded + $('.compare-tray-content').addClass('expanded') + $('#compare_toggle_btn .toggle-icon').addClass('rotate') + false $(document).on 'page:change', -> SessionProjects.init() + +# Also initialize on regular page load +$ -> + SessionProjects.init() diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 43c4cbed2..5d76be46e 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -175,5 +175,8 @@ ul.unstyled, ol.unstyled .pagination @include site-pagination-colors +.modern-pagination + @include modern-pagination-styles + .font-1em font-size: 1em diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 8baec979b..6eb3346ff 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -1,93 +1,229 @@ -/** buttons **/ +/** buttons - Updated to match Black Duck / OpenHub redesign system **/ .btn - font-family: 'Roboto' !important - font-weight: bold !important - display: inline-block - color: #FFF !important + font-family: 'Roboto', sans-serif !important + font-weight: 500 !important + display: inline-flex !important + align-items: center !important + justify-content: center !important + gap: 8px !important + color: #374151 !important background-image: none !important - border: 5px solid - border-radius: 0 - box-shadow: none !important - -webkit-transition: all ease .15s - -moz-transition: all ease .15s - -o-transition: all ease .15s - transition: all ease .15s - cursor: pointer - vertical-align: middle - margin: 0 - position: relative - padding: 0 12px 1px - line-height: 32px - font-size: 14px - -.btn-large - padding: 0 14px 1px - line-height: 38px - border-width: 6px - font-size: 16px - -.btn-small - padding: 0 5.5px - line-height: 24px - border-width: 4px - font-size: 15px - /*margin: 2.5px; + border: 1px solid !important + border-radius: 6px !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + transition: all 0.2s ease !important + cursor: pointer !important + vertical-align: middle !important + margin: 0 !important + position: relative !important + padding: 8px 16px !important + line-height: normal !important + font-size: 14px !important + text-decoration: none !important + white-space: nowrap !important + +.btn-large, .btn-lg + padding: 10px 24px !important + font-size: 16px !important + +.btn-small, .btn-sm + padding: 6px 12px !important + font-size: 13px !important .btn-mini - padding: 0 5px - line-height: 22px - border-width: 2px - font-size: 12px + padding: 4px 8px !important + font-size: 12px !important .btn-minier - padding: 0 4px - line-height: 18px - border-width: 1px - font-size: 11px + padding: 2px 6px !important + font-size: 11px !important -button.btn:active - top: 1px - left: 1px +button.btn:active, a.btn:active + top: 0 !important + left: 0 !important .btn, .btn-default - @include default-button-colors + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important -.btn.active, .btn-default.active - background-color: #9baab3 !important - border-color: #8a9ba6 + &:hover + border-color: #d1d5db !important + background-color: #f9fafb !important -.btn.no-border.active, .btn-default.no-border.active - background-color: #92a3ac !important - border-color: #92a3ac + &.active + background-color: #f3f4f6 !important + border-color: #d1d5db !important -.btn.disabled, .btn-default.disabled, .btn[disabled], .btn-default[disabled] - background-color: #abbac3 !important + &.disabled, &[disabled] + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important -.btn-header - width: 100px + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important -.i_use_this_btn - @include secondary-button-colors - width: 100% + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important -.btn-info - @include info-button-colors - &.no-hover:hover - background-color: #0082C6 !important - &.active, &.no-border.active - background-color: #66B7E8 !important - border-color: #66B7E8 - &.disabled, &[disabled] - background-color: #66CEF6 !important + &.active + background-color: #1D0631 !important + border-color: #ffb91a !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + opacity: 0.6 !important .btn-primary - @include primary-button-colors - &.no-hover:hover - background-color: #005481 !important - &.active, &.no-border.active - background-color: #0082C6 !important - border-color: #005481 + background-color: #5A2A82 !important + border-color: #5A2A82 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) !important + + &:hover + opacity: 0.9 !important + background-color: #5A2A82 !important + border-color: #5A2A82 !important + color: white !important + + &:active, &:focus + background-color: #4A1A72 !important + border-color: #4A1A72 !important + color: white !important + + &.active + background-color: #4A1A72 !important + border-color: #4A1A72 !important + + &.disabled, &[disabled] + background-color: #8A5AAA !important + border-color: #8A5AAA !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + box-shadow: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) !important + + &:hover + opacity: 0.9 !important + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + + &:active, &:focus + background-color: #e6a617 !important + border-color: #e6a617 !important + color: #1D0631 !important + + &.active + background-color: #e6a617 !important + border-color: #e6a617 !important + + &.disabled, &[disabled] + background-color: #ffb91a !important + border-color: #ffb91a !important + opacity: 0.4 !important + +.btn-secondary + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + border-color: #5A2A82 !important + background-color: white !important + color: #374151 !important + + &:active, &:focus + background-color: white !important + border-color: #5A2A82 !important + color: #374151 !important + + &.active + background-color: #f9fafb !important + border-color: #5A2A82 !important + + &.disabled, &[disabled] + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important + color: #d1d5db !important + + &:active, &:focus + background-color: #2D1548 !important + border-color: #ffb91a !important + + &.active + background-color: #1D0631 !important + border-color: #ffb91a !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + opacity: 0.6 !important + +.btn-outline + background-color: transparent !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + + &:hover + background-color: #f9fafb !important + border-color: #d1d5db !important + + &:active, &:focus + background-color: #f3f4f6 !important + + &.disabled, &[disabled] + background-color: transparent !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: transparent !important + border-color: #5A2A82 !important + color: #d1d5db !important + + &:hover + background-color: rgba(90, 42, 130, 0.15) !important + border-color: #ffb91a !important + + &:active, &:focus + background-color: rgba(90, 42, 130, 0.25) !important + + &.disabled, &[disabled] + background-color: transparent !important + color: #6b7280 !important + opacity: 0.6 !important .btn-refresh color: white !important @@ -98,222 +234,487 @@ button.btn:active padding: 10px 18px min-width: 48px transition: all 0.15s ease-in-out + &:hover background-color: rgba(90, 42, 130, 0.9) !important border-color: #5A2A82 !important transform: none + &:active background-color: #4A1A72 !important border-color: #4A1A72 !important + &.no-hover:hover background-color: #5A2A82 !important + &.active, &.no-border.active background-color: #4A1A72 !important border-color: #4A1A72 + &.disabled, &[disabled] background-color: #8A5AAA !important border-color: #8A5AAA !important opacity: 0.6 cursor: not-allowed + i font-size: 16px line-height: 1 - - // Dark mode support + + // Dark mode .dark & background-color: #ffb91a !important border-color: #ffb91a !important color: #1D0631 !important + &:hover background-color: rgba(255, 185, 26, 0.9) !important border-color: #ffb91a !important + &:active background-color: #e6a617 !important border-color: #e6a617 !important + &.active, &.no-border.active background-color: #e6a617 !important border-color: #e6a617 -.btn-success - @include join-now-button-colors - &.no-border:hover - border-color: #659965 - &.no-hover:hover - background-color: #437417 !important - &.active, &.no-border.active - background-color: #79B247 !important - border-color: #79B247 + &.disabled, &[disabled] + background-color: #ffb91a !important + border-color: #ffb91a !important + opacity: 0.4 + +.i_use_this_btn + background-color: white !important + border-color: #e5e7eb !important + color: #374151 !important + font-weight: 500 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + width: 100% + + &:hover + border-color: #5A2A82 !important + background-color: white !important + color: #374151 !important + + &:active, &:focus + background-color: white !important + border-color: #5A2A82 !important + &.disabled, &[disabled] - background-color: #79B247 !important - background-color: #FFB819 !important + background-color: #f9fafb !important + color: #9ca3af !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #2D1548 !important + border-color: #5A2A82 !important + color: #d1d5db !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + border-color: #ffb91a !important + background-color: #2D1548 !important + + &.disabled, &[disabled] + background-color: #1D0631 !important + color: #6b7280 !important + +.btn-header + width: 100px + +.btn-info + background-color: #3b82f6 !important + border-color: #3b82f6 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + &:hover - background-color: #E07C05 !important - border-color: #E07C05 !important + opacity: 0.9 !important + background-color: #3b82f6 !important + border-color: #3b82f6 !important + color: white !important + + &:active, &:focus + background-color: #2563eb !important + border-color: #2563eb !important + + &.active + background-color: #2563eb !important + border-color: #2563eb !important + + &.disabled, &[disabled] + background-color: #93c5fd !important + border-color: #93c5fd !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #3b82f6 !important + border-color: #3b82f6 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #2563eb !important + border-color: #2563eb !important + + &.disabled, &[disabled] + background-color: #3b82f6 !important + opacity: 0.4 !important + +.btn-success + background-color: #10b981 !important + border-color: #10b981 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #10b981 !important + border-color: #10b981 !important + color: white !important + + &:active, &:focus + background-color: #059669 !important + border-color: #059669 !important + + &.active + background-color: #059669 !important + border-color: #059669 !important + + &.disabled, &[disabled] + background-color: #6ee7b7 !important + border-color: #6ee7b7 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #10b981 !important + border-color: #10b981 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #059669 !important + border-color: #059669 !important + + &.disabled, &[disabled] + background-color: #10b981 !important + opacity: 0.4 !important + .btn-warning - @include warning-button-colors - &.no-hover:hover - background-color: #9F4F0A !important - &.active, &.no-border.active - background-color: #F58220 !important - border-color: #F58220 + background-color: #f59e0b !important + border-color: #f59e0b !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #f59e0b !important + border-color: #f59e0b !important + color: white !important + + &:active, &:focus + background-color: #d97706 !important + border-color: #d97706 !important + + &.active + background-color: #d97706 !important + border-color: #d97706 !important + + &.disabled, &[disabled] + background-color: #fbbf24 !important + border-color: #fbbf24 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #f59e0b !important + border-color: #f59e0b !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #d97706 !important + border-color: #d97706 !important + + &.disabled, &[disabled] + background-color: #f59e0b !important + opacity: 0.4 !important .btn-danger - @include danger-button-colors - &.no-hover:hover - background-color: #9F2609 !important - &.active, &.no-border.active - background-color: #F5471D !important - border-color: #F5471D + background-color: #ef4444 !important + border-color: #ef4444 !important + color: white !important + font-weight: 600 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important + + &:hover + opacity: 0.9 !important + background-color: #ef4444 !important + border-color: #ef4444 !important + color: white !important + + &:active, &:focus + background-color: #dc2626 !important + border-color: #dc2626 !important + + &.active + background-color: #dc2626 !important + border-color: #dc2626 !important + &.disabled, &[disabled] - background-color: #F5471D !important + background-color: #fca5a5 !important + border-color: #fca5a5 !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #ef4444 !important + border-color: #ef4444 !important + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important + + &:hover + background-color: #dc2626 !important + border-color: #dc2626 !important + + &.disabled, &[disabled] + background-color: #ef4444 !important + opacity: 0.4 !important + +// Dark neutral (unchanged - still works with branding) .btn-inverse background-color: #575757 !important - border-color: #575757 + border-color: #575757 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #303030 !important - &.no-border:hover - border-color: #303030 - &.no-hover:hover - background-color: #555555 !important - &.active + border-color: #303030 !important + + &.active, &.no-border.active background-color: #434343 !important border-color: #333333 - &.no-border.active - background-color: #3b3b3b !important - border-color: #3b3b3b + &.disabled, &[disabled] background-color: #555555 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #4b5563 !important + border-color: #4b5563 !important + color: white !important + + &:hover + background-color: #374151 !important + border-color: #374151 !important + + &.disabled, &[disabled] + opacity: 0.4 !important +// Muted purple accent (renamed intent: subtle brand accent) .btn-pink background-color: #7E7392 !important - border-color: #7E7392 + border-color: #7E7392 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #53456D !important - &.no-border:hover - border-color: #b73766 - &.no-hover:hover - background-color: #d6487e !important - &.active - background-color: #c74072 !important - border-color: #b33564 - &.no-border.active - background-color: #be386a !important - border-color: #be386a + border-color: #53456D !important + + &.active, &.no-border.active + background-color: #53456D !important + border-color: #53456D + &.disabled, &[disabled] - background-color: #d6487e !important + background-color: #7E7392 !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #7E7392 !important + border-color: #7E7392 !important + color: white !important + + &:hover + background-color: #53456D !important + border-color: #53456D !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Teal accent (renamed intent: secondary brand color) .btn-purple background-color: #45867A !important - border-color: #45867A + border-color: #45867A !important + color: white !important + font-weight: 600 !important + &:hover background-color: #166859 !important - &.no-border:hover - border-color: #281649 - &.no-hover:hover - background-color: #281649 !important + border-color: #166859 !important + &.active, &.no-border.active - background-color: #7865A4 !important - border-color: #7865A4 + background-color: #166859 !important + border-color: #166859 + &.disabled, &[disabled] - background-color: #7865A4 !important + background-color: #45867A !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #45867A !important + border-color: #45867A !important + color: white !important + &:hover + background-color: #166859 !important + border-color: #166859 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Neutral grey .btn-grey background-color: #797979 !important - border-color: #797979 + border-color: #797979 !important + color: white !important + font-weight: 600 !important + &:hover background-color: #575757 !important - &.no-border:hover - border-color: #575757 - &.no-hover:hover - background-color: #575757 !important + border-color: #575757 !important + &.active, &.no-border.active - background-color: #797979 !important - border-color: #797979 + background-color: #575757 !important + border-color: #575757 + &.disabled, &[disabled] background-color: #9A9A9A !important + opacity: 0.6 !important + cursor: not-allowed !important + // Dark mode + .dark & + background-color: #6b7280 !important + border-color: #6b7280 !important + color: white !important + + &:hover + background-color: #4b5563 !important + border-color: #4b5563 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Soft accent (green-ish, used for subtle highlights) .btn-yellow background-color: #B3D084 !important - border-color: #B3D084 - color: #996633 !important + border-color: #B3D084 !important + color: #374151 !important + font-weight: 600 !important + &:hover background-color: #A0C465 !important - &.no-border:hover - border-color: #A0C465 - &.no-hover:hover - background-color: #A0C465 !important + border-color: #A0C465 !important + &.active, &.no-border.active - background-color: #B3D084 !important - border-color: #B3D084 + background-color: #A0C465 !important + border-color: #A0C465 + &.disabled, &[disabled] background-color: #B3D084 !important + opacity: 0.6 !important + cursor: not-allowed !important + + // Dark mode + .dark & + background-color: #B3D084 !important + border-color: #B3D084 !important + color: #1D0631 !important + &:hover + background-color: #A0C465 !important + border-color: #A0C465 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + +// Light / muted (used for de-emphasized actions) .btn-light background-color: #DDD !important - border-color: #DDD + border-color: #DDD !important color: #888888 !important + font-weight: 500 !important + &:hover background-color: #BCBCBC !important - &.no-border:hover - border-color: #BCBCBC - &.no-hover:hover - background-color: #BCBCBC !important + border-color: #BCBCBC !important + &.active, &.no-border.active - background-color: #DDD !important - border-color: #DDD + background-color: #BCBCBC !important + border-color: #BCBCBC + &.disabled, &[disabled] background-color: #DDD !important + opacity: 0.6 !important + cursor: not-allowed !important + &.btn-mini:after left: -2px right: -2px top: -2px bottom: -2px + &.btn-small:after left: -4px right: -4px top: -4px bottom: -4px + &.btn-large:after left: -6px right: -6px top: -6px bottom: -6px + // Dark mode + .dark & + background-color: #374151 !important + border-color: #374151 !important + color: #9ca3af !important + + &:hover + background-color: #4b5563 !important + border-color: #4b5563 !important + + &.disabled, &[disabled] + opacity: 0.4 !important + .btn &.disabled.active, &[disabled].active, &.disabled:focus, &[disabled]:focus, &.disabled:active, &[disabled]:active - outline: none + outline: none !important + top: 0 !important + left: 0 !important + &.disabled:active, &[disabled]:active - top: 0 - left: 0 - &.active - color: #efe5b5 - &:after - display: inline-block - content: "" - position: absolute - border-bottom: 1px solid #efe5b5 - left: -4px - right: -4px - bottom: -4px - &.btn-small:after - left: -3px - right: -3px - bottom: -3px - border-bottom-width: 1px - &.btn-large:after - left: -5px - right: -5px - bottom: -5px - border-bottom-width: 1px - &.btn-mini:after, &.btn-minier:after - left: -1px - right: -1px - bottom: -1px - border-bottom-width: 1px - &.btn-yellow:after - border-bottom-color: #c96338 - &.btn-light - color: #515151 - &:after - border-bottom-color: #B5B5B5 + top: 0 !important + left: 0 !important + + &:hover + top: 0 !important + left: 0 !important diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index db0833582..1a2fec7de 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -382,6 +382,51 @@ color: white background-color: color-gradient($SECONDARY_SLATE_BLUE, 120) !important +@mixin modern-pagination-styles + display: flex + align-items: center + justify-content: center + gap: 8px + margin-top: 32px + + .page-btn + width: 36px + height: 36px + border-radius: 8px + border: 1px solid #e5e7eb + background-color: #ffffff !important + color: #111827 + font-size: 14px + font-weight: 500 + cursor: pointer + transition: all 0.2s ease + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + + html.dark & + border-color: rgba(255, 255, 255, 0.1) + background-color: #2D1548 !important + color: #ffffff + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.1) + + &:hover:not(:disabled) + border-color: #5A2A82 + + html.dark & + border-color: #ffb91a + + &.active + background-color: #5A2A82 !important + color: #ffffff + border-color: transparent + + html.dark & + background-color: #ffb91a !important + color: #1D0631 + + &:disabled + opacity: 0.4 + cursor: not-allowed + //////////////////////////////////////////////////////////////////// // // Project mixins diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index 2187fe1e3..da06c6e7b 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -28,10 +28,10 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ background-position: -#{($multiplier * $dimension)+($multiplier*0.8)}px 0 background-repeat: no-repeat position: absolute - top: 1rem + top: 16px .margin_top_two - margin-top: 2rem + margin-top: 32px .thirtyfive_project_activity_text width: 200px @@ -42,77 +42,6 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ display: -ms-flexbox display: flex -#projects_index_list, #managed_projects_list - margin: 20px 0 0 0 - .well - padding: 15px - h2 - line-height: inherit - .compare - margin: -15px -15px 0 0 - .sp_form.styled - background-color: #DBE4EE - padding: 6px 4px 6px 10px - margin: 0 - border-radius: 0 4px 0 0 - -webkit-border-radius: 0 4px 0 0 - &.selected - background-color: #0088cc - color: #fff - .logo - margin: 25px 20px 25px 0 - .info - width: 490px - i - float: right - .claimed-by-org - margin: 0 15px 0 10px - .desc - margin-bottom: 10px - &.last - display: none - margin-bottom: 20px - .stats - margin: 0 15px 0 0 - span - margin-left: 10px - a - width: 80px - text-align: right - display: inline-block - font-weight: bold - font-size: 1.2em - span - margin-left: 0 - font-size: 12px - .reviews-and-pai - position: relative - @each $level in $pai_levels - .twentyfive_project_activity_level_#{nth($level, 1)} - margin-left: 0 - top: 0 - .twentyfive_project_activity_text - margin: -5px 0 0 25px - text-align: center - width: 65px - min-height: 35px - .stars - margin: 10px 0 5px 4px - .reviews - clear: left - width: 85px - text-align: center - .add-info - clear: left - width: 574px - .main_language - margin-left: 85px - a - text-decoration: underline - .tags - clear: left - margin-top: 10px - .cocomo-form border: 2px solid #E0E4E8 .control-label @@ -137,7 +66,7 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ margin-top: 8px #widgets - width: 31rem + width: 496px #project_header padding-right: 0px @@ -153,15 +82,15 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ li margin: 15px 0 15px 30px #add_this - margin-left: 5.3rem - margin-top: 5.3rem + margin-left: 85px + margin-top: 85px a @include add-this-link-colors #project_container #project_masthead #widgets - width: 30rem + width: 480px // settings page styles .settings_module :cursor pointer @@ -261,7 +190,7 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ padding-left: 10px padding-right: 10px .range-good, .range-poor - font-size: 1.2rem + font-size: 19px text-align: center #pss_bg, #pvs_bg @@ -440,10 +369,10 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ .ctrl width: auto .title - padding-right: 1rem + padding-right: 16px #vulnerability-chart-divider - padding: 40px 4rem 20px 4rem !important + padding: 40px 64px 20px 64px !important .vulnerabilities-datatable tbody tr @@ -476,9 +405,9 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ margin-right: auto #project_badges_page - margin-bottom: 2rem + margin-bottom: 32px .title - margin-bottom: 2rem + margin-bottom: 32px .no-left-padding padding-left: 0 .badge_table_container @@ -486,22 +415,22 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ .table tbody tr td - padding-top: 1.5rem - padding-bottom: 1.5rem + padding-top: 24px + padding-bottom: 24px vertical-align: top !important .btn-mini.col-xs-12 - padding: 0.12rem 1.5rem + padding: 2px 24px width: 100% .url_update_btn color: green - font-size: 2.1rem + font-size: 34px cursor: pointer .edit_url_close_btn color: red - font-size: 2.1rem + font-size: 34px cursor: pointer #save_badge - margin-bottom: 0.5rem + margin-bottom: 8px .project_summary_container section @@ -569,3 +498,546 @@ dl.kpi margin: 0 30px .scan_data_title padding-left: 1em + +// Modern Card Layout Design - Figma Update +// SASS Variables for Light Mode +$primary-color: #5A2A82 +$primary-hover: rgba(90, 42, 130, 0.9) +$accent-color: #ffb91a +$bg-color: #f9fafb +$card-bg: #ffffff +$text-primary: #1f2937 +$text-secondary: #6b7280 +$border-color: rgba(229, 231, 235, 0.8) +$shadow-sm: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) +$shadow-md: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) +$shadow-lg: 0 8px 16px rgba(0,0,0,0.16) + +// Dark Mode Variables +$dark-primary-color: #ffb91a +$dark-primary-hover: rgba(255, 185, 26, 0.9) +$dark-accent-color: #5A2A82 +$dark-bg-color: #1D0631 +$dark-card-bg: #2D1548 +$dark-text-primary: #ffffff +$dark-text-secondary: #d1d5db +$dark-border-color: rgba(255, 255, 255, 0.1) +$dark-shadow-sm: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) +$dark-shadow-md: 0 3px 6px rgba(0,0,0,0.32), 0 3px 6px rgba(0,0,0,0.46) +$dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) + +// Modern Projects Index Page +#projects_index_page + background-color: $bg-color + min-height: 100vh + padding: 32px 0 + transition: background-color 0.3s ease, color 0.3s ease + html.dark & + background-color: $dark-bg-color + + .projects-container + max-width: 1280px + margin: 0 auto + padding: 0 16px + + // Header Section + .projects-header + display: flex + justify-content: space-between + align-items: center + margin-bottom: 24px + flex-wrap: wrap + gap: 16px + + h1 + font-size: 30px + font-weight: bold + color: $text-primary + margin: 0 + html.dark & + color: $dark-text-primary + + @media (min-width: 640px) + font-size: 36px + + .header-actions + display: flex + gap: 12px + align-items: center + flex-wrap: wrap + + .btn-browse-tags, + .btn-add-project + padding: 8px 16px + border-radius: 8px + font-weight: 600 + font-size: 14px + transition: all 0.2s ease + text-decoration: none + display: inline-flex + align-items: center + gap: 8px + white-space: nowrap + + i + font-size: 14px + flex-shrink: 0 + + .btn-browse-tags + background-color: $card-bg + border: 1px solid $border-color + color: $text-secondary + box-shadow: $shadow-sm + &:hover + border-color: $primary-color + html.dark & + background-color: $dark-card-bg + border-color: $dark-border-color + color: #ffffff !important + box-shadow: $dark-shadow-sm + &:hover + border-color: $dark-primary-color + + .btn-add-project + background-color: $primary-color + color: #ffffff + border: none + box-shadow: $shadow-md + &:hover + background-color: $primary-hover + html.dark & + background-color: $dark-primary-color + color: #1D0631 !important + box-shadow: $dark-shadow-md + &:hover + background-color: $dark-primary-hover + + +// Modern Project Card +.project-card + background-color: $card-bg + border-radius: 12px + border: 1px solid $border-color + padding: 20px 24px + margin-bottom: 16px + box-shadow: $shadow-sm + transition: all 0.3s ease + html.dark & + background-color: $dark-card-bg + border-color: $dark-border-color + box-shadow: $dark-shadow-sm + + &:hover + border-color: $primary-color + box-shadow: $shadow-md + html.dark & + border-color: $dark-primary-color + box-shadow: $dark-shadow-md + + .project-header + display: flex + justify-content: space-between + align-items: flex-start + margin-bottom: 16px + + .project-title-section + flex: 1 + + h2 + margin: 0 0 4px 0 + font-size: 18px + font-weight: 600 + + @media (min-width: 640px) + font-size: 20px + + a + color: $primary-color + text-decoration: none + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color !important + + .compare-section + .compare-checkbox + display: flex + align-items: center + gap: 6px + font-size: 14px + color: $text-secondary + cursor: pointer + transition: color 0.15s ease + + &:hover + color: $primary-color + + &.selected + color: $primary-color + font-weight: 500 + + i + color: $primary-color + + html.dark & + color: $dark-text-secondary + + &:hover + color: $dark-primary-color + + &.selected + color: $dark-primary-color + + i + color: $dark-primary-color + + .project-content + display: grid + grid-template-columns: 1fr auto + column-gap: 24px + row-gap: 0 + + @media (max-width: 768px) + grid-template-columns: 1fr + + .project-meta + grid-column: 1 + grid-row: 1 + display: flex + gap: 16px + font-size: 12px + color: $text-secondary + html.dark & + color: $dark-text-secondary + + @media (min-width: 640px) + font-size: 14px + + .claimed-by + span + color: $primary-color + font-weight: 500 + margin-left: 4px + + a + color: $primary-color + text-decoration: none + &:hover + text-decoration: underline + + html.dark & + color: $dark-primary-color + + .analyzed-date + font-style: italic + margin-left: auto + + > i + color: $primary-color !important + + abbr + color: inherit !important + + html.dark & + > i + color: $dark-primary-color !important + + .soft + color: $text-secondary + html.dark & + color: $dark-text-secondary + + .project-main + grid-column: 1 + grid-row: 2 + min-width: 300px + + .project-icon-desc + display: flex + gap: 12px + margin-bottom: 12px + + .project-icon + flex-shrink: 0 + width: 48px + height: 48px + background-color: $bg-color + border-radius: 12px + border: 1px solid $border-color + display: flex + align-items: center + justify-content: center + font-size: 24px + box-shadow: 0 1px 2px rgba(0,0,0,0.05) + html.dark & + background-color: $dark-bg-color + border-color: $dark-border-color + + @media (min-width: 640px) + width: 56px + height: 56px + font-size: 30px + + .project-description + flex: 1 + font-size: 14px + color: $text-secondary + line-height: 1.5 + html.dark & + color: $dark-text-secondary + + .read-more + color: $primary-color + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .project-lang-license + display: flex + gap: 24px + margin-bottom: 12px + font-size: 14px + color: $text-secondary + flex-wrap: wrap + html.dark & + color: $dark-text-secondary + + .language + a + color: $primary-color + font-weight: 500 + text-decoration: none + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .licenses + .license-label + font-weight: 500 + color: $text-primary + html.dark & + color: $dark-text-primary + + .project-tags + display: flex + flex-wrap: wrap + gap: 6px + align-items: center + + .tags-label + font-size: 12px + color: $text-secondary + margin-right: 4px + html.dark & + color: $dark-text-secondary + + .tag + padding: 2px 10px + border-radius: 9999px + font-size: 11px + font-weight: 500 + background-color: rgba(255, 185, 26, 0.2) + color: $accent-color + border: 1px solid rgba(255, 185, 26, 0.3) + transition: background-color 0.2s ease + cursor: pointer + text-decoration: none + &:hover + background-color: rgba(255, 185, 26, 0.3) + html.dark & + background-color: rgba(255, 185, 26, 0.15) + color: $dark-primary-color + border-color: rgba(255, 185, 26, 0.2) + &:hover + background-color: rgba(255, 185, 26, 0.25) + + .more-tags + color: $primary-color + font-size: 12px + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .project-stats + grid-column: 2 + grid-row: 1 / 3 + min-width: 280px + border-left: 1px solid $border-color + padding-left: 24px + html.dark & + border-color: $dark-border-color + + @media (max-width: 768px) + grid-column: 1 + grid-row: auto + border-left: none + border-top: 1px solid $border-color + padding-left: 0 + padding-top: 16px + + .stats-grid + display: grid + grid-template-columns: 1fr auto + gap: 10px 20px + align-items: center + + .stat-item + display: flex + align-items: center + gap: 10px + + .stat-value + font-size: 16px + font-weight: bold + color: $primary-color + min-width: 70px + text-align: right + html.dark & + color: $dark-primary-color + + @media (min-width: 640px) + font-size: 18px + + @media (min-width: 768px) + font-size: 20px + + .stat-label + font-size: 12px + color: $text-secondary + html.dark & + color: $dark-text-secondary + + @media (min-width: 640px) + font-size: 14px + + .activity-indicator + grid-row: span 2 + display: flex + flex-direction: column + align-items: center + gap: 4px + + .activity-icon + position: relative + width: 40px + height: 40px + display: flex + align-items: center + justify-content: center + + a[class^="twentyfive_project_activity_level_"] + position: static !important + margin-left: 0 !important + top: auto !important + display: block + transform: scale(1.6) + + .activity-label + font-size: 11px + color: $text-secondary + text-align: center + line-height: 1.2 + max-width: 80px + html.dark & + color: $dark-text-secondary + + .rating-section + display: flex + align-items: center + gap: 6px + + .stars + display: flex + gap: 2px + + .star + width: 14px + height: 14px + + &.filled + color: #ffb91a + + &.half + color: #ffb91a + + &.empty + color: #d1d5db + html.dark & + color: #4b5563 + + .review-count + font-size: 12px + color: $primary-color + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color + + .use-this-button + display: flex + justify-content: center + align-items: center + + .btn-use-this + padding: 6px 16px + background-color: $primary-color + color: #ffffff + border: none + border-radius: 8px + font-size: 12px + font-weight: 600 + cursor: pointer + transition: background-color 0.2s ease + &:hover + background-color: $primary-hover + html.dark & + background-color: $dark-primary-color + color: $dark-bg-color + &:hover + background-color: $dark-primary-hover + +// Responsive adjustments +@media (max-width: 1024px) + .project-card + padding: 16px + + .project-header + .project-meta + font-size: 12px + +@media (max-width: 640px) + #projects_index_page + .projects-header + .header-actions + width: 100% + justify-content: flex-start + + .btn-browse-tags, + .btn-add-project + flex: 1 + min-width: 0 + justify-content: center + + .project-card + padding: 16px + + .project-header + .project-meta + font-size: 12px + + .project-content + .project-main + .project-icon-desc + .project-icon + width: 48px + height: 48px + font-size: 24px diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index 6164b1c78..f2b1faa6f 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -1,164 +1,515 @@ -#search-dingus - @include site-well-color - padding: 10px 15px - line-height: 29px - min-height: 50px - margin-bottom: 1em - border-radius: 8px - #value_select - display: inline !important - select - width: 400px - .col-md-5 - width: 50% - display: flex - align-items: stretch - padding: 0 - - .col-md-4 - width: 32% - #sort_by - float: right - padding-right: 10px - - label.paginate - font-size: 13px - - label.sr-only - position: absolute - width: 1px - height: 1px - padding: 0 - margin: -1px - overflow: hidden - clip: rect(0, 0, 0, 0) - white-space: nowrap - border-width: 0 - - .search-input-group - display: flex - align-items: stretch - width: 100% - max-width: 500px - - button.btn - height: auto - border: 1px solid #5A2A82 - line-height: 1 - padding: 8px 16px +// Modern Header Search Form +.header-search-form + display: flex + align-items: stretch + flex: 1 + max-width: 400px + height: 40px + + @media (max-width: 640px) + order: 1 + max-width: 100% + flex-basis: 100% + + .header-search-dropdown + position: relative display: flex - align-items: center - justify-content: center - - input[type="text"] + + .search-filter-btn + display: flex + align-items: center + gap: 6px + height: 40px + padding: 0 12px + background-color: #f9fafb + border: 1px solid #d1d5db + border-right: none + border-radius: 8px 0 0 8px + color: #1f2937 + font-size: 14px + font-weight: 500 + white-space: nowrap + transition: all 0.2s ease + cursor: pointer + box-sizing: border-box + line-height: normal + + &:hover + background-color: #f3f4f6 + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &:hover + background-color: rgba(29, 6, 49, 0.8) + + .selection + display: inline-block + + i + font-size: 12px + transition: transform 0.2s ease + + .search-dropdown-menu + width: 160px + margin-top: 4px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px + overflow: hidden + z-index: 1000 + // Material Design elevation shadow (level 3) + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + .dropdown-item + display: block + width: 100% + padding: 8px 16px + color: #111827 + font-size: 14px + font-weight: 400 + text-align: left + text-decoration: none + background: transparent + border: none + box-sizing: border-box + transition: background 0.15s ease, color 0.15s ease + cursor: pointer + + &:hover + background: #f3f4f6 + + &.active + background-color: #5A2A82 !important + color: #ffffff + font-weight: 400 + + &:hover + background-color: #5A2A82 + + html.dark & + color: #ffffff + + &:hover + background: #374151 + + &.active + background-color: #ffb91a !important + color: #5A2A82 + font-weight: 400 + + &:hover + background-color: #ffb91a + + .header-search-input flex: 1 - min-width: 250px - max-width: 100% - font-size: 14px - color: #1f2937 + height: 40px + padding: 0 12px background-color: #ffffff border: 1px solid #d1d5db - padding: 10px 14px - transition: all 0.15s ease-in-out - border-radius: 12px 0 0 12px - margin: 0 - height: auto - line-height: 1.5 - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) - &:hover - border-color: #9ca3af + color: #1f2937 + font-size: 14px + outline: none + border-right: none + box-shadow: none + min-width: 0 + transition: border-color 0.2s ease + box-sizing: border-box + line-height: normal + + &::placeholder + color: #6b7280 + &:focus outline: none border-color: #5A2A82 - box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) - background-color: #ffffff - &::placeholder - color: #9ca3af - font-size: 14px - - // Dark mode support - .dark & - background-color: rgba(29, 6, 49, 0.5) - input[type="text"] + box-shadow: none + + html.dark & + border-color: #ffb91a !important + + html.dark & background-color: #1D0631 + border-color: #5A2A82 !important color: #ffffff - border-color: #4b5563 - &:hover - border-color: #6b7280 - &:focus - border-color: #ffb91a - box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) - background-color: #1D0631 + &::placeholder + color: #d1d5db + + .header-search-btn + height: 40px + padding: 0 16px + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 0 8px 8px 0 + color: #ffffff + font-size: 14px + cursor: pointer + transition: background-color 0.2s ease + display: flex + align-items: center + justify-content: center + box-sizing: border-box + line-height: normal + + &:hover + background-color: rgba(90, 42, 130, 0.9) + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + + &:hover + background-color: rgba(255, 185, 26, 0.9) + + i + font-size: 14px + +// Modern Search/Filter Bar (for projects index and other listing pages) +.search-filter-bar + background-color: #ffffff + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.8) + padding: 16px + margin-bottom: 24px + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + transition: background-color 0.3s ease + + html.dark & + background-color: #2D1548 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) + + .search-filter-content + display: flex + align-items: center + gap: 12px + flex-wrap: wrap + + .pagination-info + font-size: 14px + color: #6b7280 + white-space: nowrap + + html.dark & + color: #d1d5db + + .page-number + font-weight: 600 + color: #1f2937 + + html.dark & + color: #ffffff + + .search-section + flex: 1 + display: flex + align-items: center + gap: 8px + min-width: 300px + + label + font-size: 14px + color: #6b7280 + white-space: nowrap + line-height: 40px + margin: 0 + padding: 0 + + html.dark & + color: #d1d5db + + .search-input-wrapper + flex: 1 + position: relative + + input + width: 100% + height: 40px + padding: 0 40px 0 12px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px !important + color: #111827 + font-size: 14px + font-weight: 400 + line-height: normal + margin: 0 + box-sizing: border-box + transition: background 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease + + &::placeholder + color: #9ca3af + + &:hover + background-color: #f9fafb + border-color: #5A2A82 + + &:focus + outline: none + background-color: #ffffff + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &::placeholder + color: #9ca3af + + &:hover + background-color: rgba(29, 6, 49, 0.8) + border-color: #ffb91a + + &:focus + background-color: #1D0631 + border-color: #ffb91a + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) + + .search-cancel-btn + position: absolute + right: 8px + top: 50% + transform: translateY(-50%) + background: transparent + border: none + color: #9ca3af + font-size: 14px + cursor: pointer + padding: 4px 6px + line-height: 1 + transition: color 0.15s ease + + &:hover + color: #6b7280 + + html.dark & + color: #6b7280 + + &:hover + color: #d1d5db + + .search-refresh-btn + display: flex + align-items: center + justify-content: center + height: 40px + width: 40px + padding: 0 + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 8px + color: #ffffff + font-size: 14px + cursor: pointer + transition: background-color 0.2s ease, opacity 0.2s ease + box-sizing: border-box + flex-shrink: 0 + + &:hover + opacity: 0.9 + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + + &:hover + opacity: 0.9 + + i + font-size: 14px + + .sort-section + display: flex + align-items: center + gap: 8px + + label + font-size: 14px color: #6b7280 - - select - width: 150px + white-space: nowrap + + html.dark & + color: #d1d5db + + // Custom Sort Dropdown + .custom-sort-dropdown + position: relative + display: inline-block + + .sort-dropdown-btn + display: flex + align-items: center + gap: 8px + height: 40px + padding: 0 12px + background-color: #f9fafb + border: 1px solid #d1d5db + border-radius: 8px + color: #111827 + font-size: 14px + font-weight: 400 + min-width: 180px + cursor: pointer + transition: background 0.15s ease, border-color 0.15s ease + white-space: nowrap + box-sizing: border-box + + &:hover + background-color: #f3f4f6 + border-color: #5A2A82 + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &:hover + background-color: rgba(29, 6, 49, 0.8) + border-color: #ffb91a + + .selection + flex: 1 + text-align: left + + i + font-size: 12px + transition: transform 0.2s ease + + .sort-dropdown-menu + position: absolute + top: 100% + left: 0 + margin-top: 4px + min-width: 180px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px + overflow: hidden + z-index: 1000 + // Material Design elevation shadow (level 3) + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + .sort-dropdown-item + display: block + width: 100% + padding: 8px 16px + color: #111827 + font-size: 14px + font-weight: 400 + text-align: left + text-decoration: none + background: transparent + border: none + box-sizing: border-box + transition: background 0.15s ease, color 0.15s ease + cursor: pointer + + &:hover + background: #f3f4f6 + + &.active + background-color: #5A2A82 !important + color: #ffffff + font-weight: 400 + + &:hover + background-color: #5A2A82 + + html.dark & + color: #ffffff + + &:hover + background: #374151 + + &.active + background-color: #ffb91a !important + color: #5A2A82 + font-weight: 400 + + &:hover + background-color: #ffb91a + + .sort-value-input + display: none + + // Chosen select plugin support (for tag filtering) + #value_select + display: inline-block + margin-left: 8px + + select + min-width: 300px + + // Checkbox filters label.checkbox + display: inline-flex + align-items: center + gap: 6px + font-size: 14px + color: #6b7280 + margin: 0 12px 0 0 + cursor: pointer + font-weight: normal + + html.dark & + color: #d1d5db + input - margin-top: 4px - label.radio - input - width: 13px !important - margin-top: 4px - #human_edits - input - width: 14px + width: 16px + height: 16px + margin: 0 + cursor: pointer + + #human_edits, #enlistment_edits input - width: 14px - -#nav-top-bar .new_main_menu form#quicksearch - margin-top: -4px - button.submit - background: none - border: 0 - ul.dropdown-menu - border-radius: 0px - margin-top: -10px !important - li - display: inline - a - @include search-dropdown-link-color - font-size: 14px - text-transform: none - &:hover - @include search-dropdown-link-hover-colors - .btn-small - font-size: 10px - border-width: 2px !important - margin-right: 0px !important - .selection - font-weight: 100 - text-transform: none - &:hover - @include search-button-hover-colors - -@media (min-width: 320px) and (max-width: 360px) - form#quicksearch - margin-top: -20px !important - margin-right: -61px !important - .btn-small - font-size: 8px !important -@media (min-width: 361px) and (max-width: 480px) - form#quicksearch - margin-top: -6px !important - margin-right: -61px !important - .btn-small - font-size: 8px !important - -.global_top_search - height: 18px !important - @include global-top-search-colors - -.global_top_search_icon - @include global-top-search-icon-color - padding-bottom: 5px - font-size: 18px - -@media (min-width: 320px) and (max-width: 480px) - .global_top_search - height: 15px !important - font-size: 8px !important - .global_top_search_icon - font-size: 10px !important + width: 16px + height: 16px + + // Radio button filters + label.radio + display: inline-flex + align-items: center + gap: 6px + font-size: 14px + color: #6b7280 + margin: 0 12px 0 0 + cursor: pointer + font-weight: normal + + html.dark & + color: #d1d5db + + input + width: 16px + height: 16px + margin: 0 + cursor: pointer + + &.inline + display: inline-flex + +// Responsive adjustments for search-filter-bar +@media (max-width: 640px) + .search-filter-bar + .search-filter-content + .search-section, + .sort-section + width: 100% diff --git a/app/assets/stylesheets/session_projects.sass b/app/assets/stylesheets/session_projects.sass index b0f3d3e7f..8041df5cf 100644 --- a/app/assets/stylesheets/session_projects.sass +++ b/app/assets/stylesheets/session_projects.sass @@ -1,44 +1,250 @@ -.sp_label - margin-right: 10px - #sp_menu position: fixed - left: 50% - transform: translate(-50%, 0) - top: 0 - width: 960px - z-index: 1 - color: white - .inner - background-color: #0088cc - padding: 10px 20px 0 - box-shadow: 0 5px 0 #666 - -webkit-box-shadow: 0 5px 0 #666 - -ms-filter: "progid:DXImageTransform.Microsoft.DropShadow(color='#666666',offX='0', offY='5')" - ul.nav li - margin-right: 3em - &.last - margin-right: 0 - .sp_form - margin-bottom: 0 - .sp_label - color: #fff - margin-right: 0 - .sp_input - font-size: 17px - line-height: 17px - vertical-align: middle - margin-top: 2px + bottom: 0 + left: 0 + right: 0 + z-index: 1000 + display: none + transition: all 0.3s ease + + // Toggle Tab Button + .compare-toggle-tab + display: flex + justify-content: center + + .compare-toggle-button + display: flex + align-items: center + gap: 8px + padding: 8px 20px + background-color: #5A2A82 + color: #ffffff + border: none + border-radius: 12px 12px 0 0 + font-size: 14px + font-weight: 600 + cursor: pointer + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.15) + transition: all 0.2s ease + + &:hover + opacity: 0.9 + + html.dark & + background-color: #ffb91a + color: #1D0631 + box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.3) + + .toggle-icon + transition: transform 0.2s ease + font-size: 12px + + &.rotate + transform: rotate(180deg) + + // Expandable Tray Content + .compare-tray-content + max-height: 0 + overflow: hidden + background-color: #ffffff + border-top: 1px solid #e5e7eb + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.12) + transition: max-height 0.3s ease + + &.expanded + max-height: 280px + + html.dark & + background-color: #2D1548 + border-top-color: #5A2A82 + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.4) + + .compare-tray-inner + max-width: 1200px + margin: 0 auto + padding: 16px 24px + + @media (max-width: 768px) + padding: 12px 16px + + // Projects Grid + .compare-projects-grid + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 12px + margin-bottom: 16px + + @media (min-width: 640px) + grid-template-columns: repeat(3, 1fr) + + .compare-project-slot + position: relative + background-color: #f9fafb + border: 1px solid #e5e7eb + border-radius: 12px + padding: 12px + display: flex + align-items: start + gap: 12px + min-height: 80px + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + + &.empty-slot + border: 2px dashed #d1d5db + flex-direction: column + align-items: center + justify-content: center + color: #9ca3af + + html.dark & + border-color: #5A2A82 + color: #6b7280 + + i + font-size: 20px + margin-bottom: 4px + + span + font-size: 12px + + .remove-project + position: absolute + top: 8px + right: 8px + width: 20px + height: 20px + padding: 0 + background-color: #e5e7eb + border: none + border-radius: 50% + color: #6b7280 + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.15s ease + + &:hover + background-color: #fee2e2 + color: #ef4444 + + html.dark & + background-color: rgba(255, 255, 255, 0.1) + color: #9ca3af + + &:hover + background-color: rgba(239, 68, 68, 0.2) + color: #fca5a5 + + i + font-size: 10px + + .project-slot-icon + flex-shrink: 0 + width: 40px + height: 40px + background-color: #e5e7eb + border: 1px solid #d1d5db + border-radius: 8px + display: flex + align-items: center + justify-content: center + overflow: hidden + + html.dark & + background-color: rgba(255, 255, 255, 0.1) + border-color: #6b7280 + + img + width: 100% + height: 100% + object-fit: cover + + i + font-size: 18px + color: #9ca3af + + .project-slot-info + flex: 1 + min-width: 0 + padding-right: 20px + + .project-slot-name + font-size: 14px + font-weight: 600 + color: #111827 + margin: 0 0 4px 0 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + html.dark & + color: #ffffff + + .project-slot-meta + font-size: 12px + color: #6b7280 + margin: 2px 0 + + html.dark & + color: #9ca3af + + // Compare Action + .compare-action + display: flex + justify-content: center + + form + width: auto + + .btn-compare-submit + padding: 10px 32px + background-color: #5A2A82 + border: 1px solid #5A2A82 + border-radius: 8px + color: #ffffff + font-size: 14px + font-weight: 600 cursor: pointer - .compare - float: right - font-weight: bold - .limit - display: inline - margin-left: 0.5em - -#compare-submit-button - margin: -2px 0px 10px - padding: 0px 12px 1px - height: 29px - width: 75px + transition: all 0.2s ease + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + + &:hover:not(:disabled) + opacity: 0.9 + + &:disabled + background-color: #d1d5db + border-color: #d1d5db + color: #9ca3af + cursor: not-allowed + box-shadow: none + + html.dark & + background-color: #ffb91a + border-color: #ffb91a + color: #1D0631 + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + + &:disabled + background-color: #4b5563 + border-color: #4b5563 + color: #6b7280 + +// Add bottom padding when compare tray is visible +#projects_index_page.has-compare-tray + .projects-container + padding-bottom: 120px + +// Compare checkbox disabled state +.compare-checkbox.disabled + opacity: 0.5 + cursor: not-allowed !important + + &:hover + color: #9ca3af !important + + html.dark & + color: #6b7280 !important diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index 7334835b6..3a3138e86 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -1,104 +1,138 @@ - project = project_index -.well.searchable{ id: "project_#{project.id}" } - %h2.title.pull-left= link_to h(project.name.truncate(70)), project_path(project), title: project.name - .pull-right - .compare - - project_compare_button(project, t('.compare')) if defined?(compare) && compare - - if params[:action].to_s != 'check_forge' - .i_use_this - - project_iusethis_button(project) if defined?(i_use_this) && i_use_this - .clear +.project-card.searchable{ id: "project_#{project.id}" } + / Project Header with Compare Option + .project-header + .project-title-section + %h2 + = link_to h(project.name.truncate(70)), project_path(project), title: project.name - .pull-left#inner_content - %a.pull-left.logo{ href: project_path(project), style: 'max-width: 64px;' } - - project_icon(project, :med) + - if defined?(compare) && compare + .compare-section + .compare-checkbox{ data: { project_id: project.to_param } } + %span Compare + %i.fa.fa-square-o - .info.pull-left - %p - - if project.organization + / Project Content + .project-content + .project-meta + - if project.organization + .claimed-by = t('.claimed_by') - = link_to h(truncate(project.organization.name, length: 30)), - organization_path(project.organization), class: 'claimed-by-org', title: project.organization.name - - else -   - + %span + = link_to h(truncate(project.organization.name, length: 30)), + organization_path(project.organization), title: project.organization.name + .analyzed-date - if !project.best_analysis.nil? || @analysis - analysis = (project.best_analysis || @analysis) %i %abbr.date{ title: analysis.updated_on } = t('.analyzed_ago', time: time_ago_in_words(analysis.updated_on)) - else - %span.soft.pull-right - %i= t('.no_analysis_available') + %span.soft + %i= t('.no_analysis_available') - .desc - - if project.description && project.description.strip.size > 340 - = expander(project.description, 300, 340) - - elsif project.description - %span.proj_desc_toggle{ style: 'display: inline', id: "proj_desc_#{project.id}_lg" } + / Main Content - Left Side + .project-main + / Icon and Description + .project-icon-desc + .project-icon + - if project.logo.present? + = image_tag project.logo.attachment.url(:med), alt: project.name + - else + %i.fa.fa-cube + + .project-description + - if project.description && project.description.strip.size > 220 + = project.description.truncate(220) + %span.read-more [More] + - elsif project.description = project.description + - else + %span.text-muted= t('.no_description_available') - .stats.pull-left - %p - - if project.best_analysis.empty? - = link_to 0, '#' - - else - - code_total_str = project.best_analysis.code_total.to_human - = link_to code_total_str, languages_summary_project_analysis_path(project, 'latest') - %span= t('.loc') - %p - = link_to number_with_delimiter(project.active_committers), summary_project_contributors_path(project) - %span= t('.current_contributors') - %p - - if !project.best_analysis.nil? && project.best_analysis.last_commit_time - %a{ href: summary_project_commits_path(project) }= time_ago_in_words(project.best_analysis.last_commit_time) - - else - = link_to 0, '#' - %span= t('.since_last_commit') - %p - = link_to number_with_delimiter(project.user_count), users_project_path(project) - %span= t('.users_on_open_hub') + / Language and License + .project-lang-license + .language + - if project.best_analysis.present? && project.best_analysis.main_language + - lang = project.best_analysis.main_language.nice_name + = t('.mostly_written_in', lang: link_to(lang, languages_summary_project_analysis_path(project, 'latest'))).html_safe + - else + %span= t('.not_available') - .reviews-and-pai.pull-left - - project_activity_level_class(project, :twentyfive) - - project_activity_level_text(project, :twentyfive) - .stars!= rating_stars('average_rating_stars', project.rating_average.to_f || 0, mini: true) - .reviews - = link_to pluralize_with_delimiter(project.reviews.count, t('.review')), summary_project_reviews_path(project) - .use_this{ style: 'margin-top: 10px; margin-left: 10px' } - - if logged_in? - = render partial: 'projects/show/i_use_this', locals: { project: project } - - else - .btn.btn-mini.i_use_this_btn{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' }= t('.i_use_this') - .modal.fade#LoginModal{ 'aria-hidden' => 'true', 'aria-labelledby' => 'LoginModalLabel', :role => 'dialog' } - .modal-dialog.modal-sm - .modal-content{ style: 'width: 398px' } - .modal-header - %button.close{ 'aria-label' => 'Close', 'data-dismiss' => 'modal', :type => 'button' } - %span{ 'aria-hidden' => 'true' } × - %h4.modal-title#LoginModalLabel= t('.login_required') - .model-body - = render 'sessions/sign_in' + .licenses + %span.license-label= t('.licenses') + - if project.licenses.any? + = project.licenses.first(3).collect { |l| l.short_name.truncate(12) }.join(', ') + - else + = t('.no_declared_licenses') + + / Tags + .project-tags + - unless project.tag_list.blank? + %span.tags-label= t('.tags') + - tags = project.tag_list.split(' ') + - tags.first(8).each do |tag| + - tag = tag.fix_encoding_if_invalid + = link_to html_escape(tag), tags_path(names: tag), class: 'tag' + - if tags.length > 8 + %span.more-tags= t('.n_more', n: tags.length - 8) + + / Stats - Right Side + .project-stats + + .stats-grid + / Row 1: Lines of Code | Activity Indicator (spans 2 rows) + .stat-item + %span.stat-value + - if project.best_analysis.empty? + 0 + - else + = project.best_analysis.code_total.to_human + %span.stat-label= t('.loc') + + .activity-indicator + .activity-icon + - project_activity_level_class(project, :twentyfive) + .activity-label + = project_activity_text(project, true) + + / Row 2: Contributors + .stat-item + %span.stat-value= number_with_delimiter(project.active_committers) + %span.stat-label= t('.current_contributors') + + / Row 3: Last Commit | Star Rating + .stat-item + %span.stat-value + - if !project.best_analysis.nil? && project.best_analysis.last_commit_time + = time_ago_in_words(project.best_analysis.last_commit_time) + - else + 0 + %span.stat-label= t('.since_last_commit') + + .rating-section + .stars + - rating = project.rating_average.to_f + - full_stars = rating.floor + - has_half_star = (rating - full_stars) >= 0.5 + - 5.times do |i| + - if i < full_stars + %i.fa.fa-star.star.filled + - elsif has_half_star && i <= full_stars + %i.fa.fa-star-half-o.star.half + - else + %i.fa.fa-star.star.empty + .review-count + = link_to pluralize_with_delimiter(project.reviews.count, t('.review')), summary_project_reviews_path(project) - .add-info - .main_language.pull-left - - if project.best_analysis.present? && project.best_analysis.main_language - - lang = project.best_analysis.main_language.nice_name - = link_to t('.mostly_written_in', lang: lang), languages_summary_project_analysis_path(project, 'latest') - - else - %span= t('.mostly_written_lang_not_available') - .licenses.pull-right - = link_to t('.licenses'), project_licenses_path(project) - - if project.licenses.any? - = project.licenses.first(3).collect { |l| l.short_name.truncate(12) }.join(', ') - - else - %span= t('.no_declared_licenses') - .clear_both + / Row 4: Users on Open Hub | I Use This Button + .stat-item + %span.stat-value= number_with_delimiter(project.user_count) + %span.stat-label= t('.users_on_open_hub') - .tags - - unless project.tag_list.blank? - - tags = project.tag_list.split(' ') - = tag_icon_link(project) - = tag_links(tags, 12) - - if tags.length > 12 - = link_to t('.n_more', n: (tags.length - 12)), project_path(project) + .use-this-button + - if logged_in? + = render partial: 'projects/show/i_use_this', locals: { project: project } + - else + %button.btn-use-this{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' } + = t('.i_use_this') diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 93a0e6eab..5447eaf32 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -2,20 +2,54 @@ - content_for(:html_title) { @query ? t('.page_title_query', query: @query) : t('.page_title') } - page_context[:select_top_menu_nav] = 'select_projects' - content_for :session_projects_banner do - .navbar.container#sp_menu + #sp_menu = render 'session_projects/menu' -#projects_index_header - %h1.pull-left= t('.projects') - .pull-right.margin_top_20 - = link_to t('.browse_projects'), tags_path, class: 'btn btn-light btn-mini' - = link_to t('.add_new_project'), new_project_path, class: 'btn btn-light btn-mini' +#projects_index_page + .projects-container + / Header Section with Actions + .projects-header + %h1= t('.projects') + .header-actions + = link_to tags_path, class: 'btn-browse-tags' do + %i.fa.fa-tag + = t('.browse_projects') + = link_to new_project_path, class: 'btn-add-project' do + %i.fa.fa-plus + = t('.add_new_project') -.clearfix   + / Header Search with Dropdown + = render 'shared/search' -= render partial: 'shared/search_dingus', locals: { collection: @projects, sort_context: :projects } -.clear + / Search and Filter Bar + = render 'shared/search_dingus', collection: @projects, sort_context: :projects -#projects_index_list - = render partial: 'project_index', collection: @projects, locals: { compare: true, show_active_committers: true } - = will_paginate @projects + / Projects List + #projects_index_list + = render partial: 'project_index', collection: @projects, locals: { compare: true, show_active_committers: true } + + / Modern Pagination + - if @projects.total_pages > 1 + .modern-pagination + - current_page = @projects.current_page + - total_pages = @projects.total_pages + %button.page-btn{disabled: current_page == 1, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page - 1)).to_query}'"} + %i.fa.fa-chevron-left + - visible_pages = [] + - window_size = 2 + - visible_pages << 1 + - if total_pages > 1 + - left = [current_page - window_size, 2].max + - right = [current_page + window_size, total_pages - 1].min + - visible_pages << :gap if left > 2 + - visible_pages.concat((left..right).to_a) if left <= right + - visible_pages << :gap if right < total_pages - 1 + - visible_pages << total_pages + - visible_pages.each do |page| + - if page == :gap + %span.page-ellipsis … + - else + %button.page-btn{class: ('active' if page == current_page), onclick: "window.location.href='?#{request.query_parameters.merge(page: page).to_query}'"} + = page + %button.page-btn{disabled: current_page == total_pages, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page + 1)).to_query}'"} + %i.fa.fa-chevron-right diff --git a/app/views/session_projects/_menu.html.haml b/app/views/session_projects/_menu.html.haml index a4255a8b1..d142d7d70 100644 --- a/app/views/session_projects/_menu.html.haml +++ b/app/views/session_projects/_menu.html.haml @@ -1,20 +1,43 @@ - if @session_projects.any? - .inner - %p.col.pull-left{ style: 'margin-right: 3em' } - %strong= t('.compare_projects') - - if @session_projects.size == 3 - %span.limit - = surround '(', ')' do - = t('.limit_reached') - %ul.nav.pull-left.margin_top_0 - - @session_projects.each_with_index do |project, i| - %li{ class: ('last' if i == @session_projects.length - 1) } - = form_tag nil, class: 'sp_form form-inline', id: "sp_frm_#{project.to_param}" do - %span.sp_label{ title: project.name } - = project.name.truncate(20) - %i.sp_input.icon.icon-remove-sign{ project_id: project.to_param } - %form.sp_form.form_inline.pull-right{ action: compare_projects_path, method: :get } - - @session_projects.each_with_index do |p, i| - %input{ type: 'hidden', name: "project_#{i}", value: p.name } - %input.btn.btn-sm#compare-submit-button{ type: 'submit', value: t('compare') } - .clearfix + / Toggle Button + .compare-toggle-tab + %button#compare_toggle_btn.compare-toggle-button + %i.fa.fa-exchange + = t('.compare_projects', count: @session_projects.size) + %i.fa.fa-chevron-up.toggle-icon + + / Expandable Tray Content + .compare-tray-content + .compare-tray-inner + / Selected Projects Grid + .compare-projects-grid + - @session_projects.each do |project| + .compare-project-slot + %button.remove-project{ type: 'button', data: { project_id: project.to_param } } + %i.fa.fa-times + .project-slot-icon + - if project.logo.present? + = image_tag project.logo.attachment.url(:small), alt: project.name + - else + %i.fa.fa-cube + .project-slot-info + %p.project-slot-name{ title: project.name }= project.name.truncate(25) + - if project.best_analysis + %p.project-slot-meta= t('.lines', count: project.best_analysis.code_total.to_human) + %p.project-slot-meta= t('.contributors', count: project.active_committers) + + - (3 - @session_projects.size).times do + .compare-project-slot.empty-slot + %i.fa.fa-plus + %span= t('.select_project') + + / Compare Action Button + .compare-action + %form{ action: compare_projects_path, method: :get } + - @session_projects.each_with_index do |p, i| + %input{ type: 'hidden', name: "project_#{i}", value: p.name } + %button.btn-compare-submit{ type: 'submit', disabled: @session_projects.size < 2 } + - if @session_projects.size >= 2 + = t('.compare_n_projects', count: @session_projects.size) + - else + = t('.compare_select_at_least') diff --git a/app/views/shared/_search.html.haml b/app/views/shared/_search.html.haml index 62493903a..283e26988 100644 --- a/app/views/shared/_search.html.haml +++ b/app/views/shared/_search.html.haml @@ -3,22 +3,17 @@ global_search_param = '' if @avoid_global_search - unless request.path == root_path - %form.pull-right#quicksearch{ action: projects_path } - .dropdown - .btn-group.ux-dropdown - %a.btn.btn-small.dropdown-toggle{ 'data-toggle' => 'dropdown' } - %span.selection= t('projects_menu') - %span.caret - %ul.dropdown-menu - %li - %a{ val: 'people' }= t('people_menu') - %li - %a.default{ val: 'p' }= t('projects_menu') - %li - %a{ val: 'orgs' }= t('organizations_menu') - %input.search.text.global_top_search{ type: :text, name: 'query', placeholder: t('shared.search.search_text'), - value: global_search_param, autocomplete: 'off' } - %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type', value: 'projects' } - %button.submit.no_padding{ type: 'submit' } - .icon-search.global_top_search_icon + %form#header-search-form.header-search-form{ action: projects_path } + .btn-group.ux-dropdown.header-search-dropdown + %button.search-filter-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ val: 'p' }= t('projects_menu') + %i.fa.fa-chevron-down + .dropdown-menu.search-dropdown-menu + %button.dropdown-item.active{ type: 'button', val: 'p' }= t('projects_menu') + %button.dropdown-item{ type: 'button', val: 'people' }= t('people_menu') + %button.dropdown-item{ type: 'button', val: 'orgs' }= t('organizations_menu') + %input.header-search-input{ type: 'text', name: 'query', placeholder: t('shared.search.search_text'), value: global_search_param, autocomplete: 'off' } + %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type', value: 'projects' } + %button.header-search-btn{ type: 'submit' } + %i.fa.fa-search diff --git a/app/views/shared/_search_dingus.html.haml b/app/views/shared/_search_dingus.html.haml index 496109b75..61893b1f8 100644 --- a/app/views/shared/_search_dingus.html.haml +++ b/app/views/shared/_search_dingus.html.haml @@ -6,9 +6,9 @@ sort_context ||= nil no_match_found_type ||= :message -.margin_top_20#search-dingus - %form.form-inline - .row +.search-filter-bar + %form + .search-filter-content = render 'shared/search_dingus/page_entries_info', collection: collection, total_count: total_count = render 'shared/search_dingus/search_bar', search_type: search_type, tags: tags diff --git a/app/views/shared/search_dingus/_page_entries_info.html.haml b/app/views/shared/search_dingus/_page_entries_info.html.haml index f0bb5827a..985647b93 100644 --- a/app/views/shared/search_dingus/_page_entries_info.html.haml +++ b/app/views/shared/search_dingus/_page_entries_info.html.haml @@ -1,7 +1,7 @@ -.col-md-3.col-sm-3.col-xs-3.padding_left_25 +.pagination-info - if collection.respond_to?(:current_page) && !collection.length.zero? - %label.paginate= t('.pagination_status', current_page: number_with_delimiter(collection.current_page), - total_pages: number_with_delimiter(collection.total_pages)) + = t('.pagination_status', + current_page: number_with_delimiter(collection.current_page), + total_pages: number_with_delimiter(collection.total_pages)) - elsif !total_count.to_i.zero? - %label.paginate= t('.count_status', current_count: collection.length, - total_count: number_with_delimiter(total_count)) + = t('.count_status', current_count: collection.length, total_count: number_with_delimiter(total_count)) diff --git a/app/views/shared/search_dingus/_search_bar.html.haml b/app/views/shared/search_dingus/_search_bar.html.haml index ad48ad78a..da45c3607 100644 --- a/app/views/shared/search_dingus/_search_bar.html.haml +++ b/app/views/shared/search_dingus/_search_bar.html.haml @@ -1,5 +1,5 @@ - if search_type == :tags - .search_tags + .search-section %label= t('.filter_by_tags') .chosen#value_select = select_tag :names, options_for_select(tags.map { |ary| ary.map(&:fix_encoding_if_invalid) }, params[:names]), @@ -7,11 +7,11 @@ multiple: true, onchange: 'this.form.submit()' - else - .col-md-5.col-sm-5.col-xs-5.no_padding - %label.sr-only= t('.search_text') - .search-input-group - = text_field_tag :query, params[:query], - placeholder: t('.search_text') - - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-search + .search-section + %label{for: 'query'}= t('.search_text') + .search-input-wrapper + %input#query{type: 'text', name: 'query', placeholder: t('.search_text'), value: params[:query]} + %button.search-cancel-btn{ type: 'button', style: (params[:query].blank? ? 'display: none;' : '') } + %i.fa.fa-times + %button.search-refresh-btn{ type: 'submit' } + %i.fa.fa-refresh diff --git a/app/views/shared/search_dingus/_sort.html.haml b/app/views/shared/search_dingus/_sort.html.haml index b3e99c0a7..792727a04 100644 --- a/app/views/shared/search_dingus/_sort.html.haml +++ b/app/views/shared/search_dingus/_sort.html.haml @@ -1,28 +1,45 @@ -.col-md-4.col-sm-4.col-xs-4 - - if filter_type == :checkbox - %label.checkbox.pull-right.padding_right_20#human_edits - %input{ type: 'checkbox', checked: params[:human] == 'true' } - = t('.show_only_humans') - %label.checkbox.pull-right.padding_right_15#enlistment_edits - %input{ type: 'checkbox', id: 'enlistment_checkbox', checked: params[:enlistment] == 'true'} - = t('.filter_only_enlistment') - #sort_by - %label= t('.sort_by') - = select_tag :sort, options_for_select([["updated_at", "updated_at"], ["created_at", "created_at"]], - params[:sort]), - class: 'chzn-select', onchange: 'this.form.submit()' +- if filter_type == :checkbox + %label.checkbox#human_edits + %input{ type: 'checkbox', checked: params[:human] == 'true' } + = t('.show_only_humans') + %label.checkbox#enlistment_edits + %input{ type: 'checkbox', id: 'enlistment_checkbox', checked: params[:enlistment] == 'true'} + = t('.filter_only_enlistment') + .sort-section + %label= t('.sort_by') + :ruby + sort_options = [["updated_at", "updated_at"], ["created_at", "created_at"]] + current_sort = params[:sort] || "updated_at" + selected_label = sort_options.find { |opt| opt[1] == current_sort }&.first || sort_options.first.first + .custom-sort-dropdown + %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ data: { value: current_sort } }= selected_label + %i.fa.fa-chevron-down + .dropdown-menu.sort-dropdown-menu + - sort_options.each do |label, value| + %button.sort-dropdown-item{ type: 'button', data: { value: value }, class: (value == current_sort ? 'active' : '') }= label + %input{ type: 'hidden', name: 'sort', value: current_sort, class: 'sort-value-input' } - - elsif filter_type == :radiobutton - %label.radio.inline.margin_left_40 +- elsif filter_type == :radiobutton + .search-section + %label.radio.inline = radio_button_tag('find_by', 'name', params[:find_by] != 'email') = t('.by_name') %label.radio.inline = radio_button_tag('find_by', 'email', params[:find_by] == 'email') = t('.by_email') - - elsif sort_context.present? - #sort_by - %label= t('.sort_by') - = select_tag :sort, options_for_select(SORT_OPTIONS[sort_context][:options], - params[:sort] || SORT_OPTIONS[sort_context][:default]), - class: 'chzn-select', onchange: 'this.form.submit()' +- elsif sort_context.present? + .sort-section + %label= t('.sort_by') + :ruby + current_sort = params[:sort] || SORT_OPTIONS[sort_context][:default] + selected_label = SORT_OPTIONS[sort_context][:options].find { |opt| opt[1] == current_sort }&.first || SORT_OPTIONS[sort_context][:options].first.first + .custom-sort-dropdown + %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ data: { value: current_sort } }= selected_label + %i.fa.fa-chevron-down + .dropdown-menu.sort-dropdown-menu + - SORT_OPTIONS[sort_context][:options].each do |label, value| + %button.sort-dropdown-item{ type: 'button', data: { value: value }, class: (value == current_sort ? 'active' : '') }= label + %input{ type: 'hidden', name: 'sort', value: current_sort, class: 'sort-value-input' } diff --git a/config/locales/en.yml b/config/locales/en.yml index 403d2f936..f46f88118 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -398,7 +398,12 @@ en: session_projects: menu: - compare_projects: 'Compare Projects:' + compare_projects: 'Compare Projects (%{count}/3)' + select_project: 'Select Project' + lines: '%{count} lines' + contributors: '%{count} contributors' + compare_n_projects: 'Compare %{count} Projects' + compare_select_at_least: 'Compare (Select at least 2)' limit_reached: limit reached create: limit_exceeded: Project count limit exceeded diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index aaf00da22..7bd854cc8 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -49,16 +49,20 @@ en: claimed_by: "Claimed by" analyzed_ago: "Analyzed %{time} ago" no_analysis_available: "No analysis available" + no_description_available: "No description available" + mostly_written_in: "Mostly written in %{lang}" + not_available: "Not available" + licenses: "Licenses:" + no_declared_licenses: "No declared licenses" + tags: "Tags" + n_more: "%{n} more..." loc: "lines of code" current_contributors: "current contributors" since_last_commit: "since last commit" users_on_open_hub: "users on Open Hub" review: "Review" - mostly_written_in: "Mostly written in %{lang}" + i_use_this: "I Use This" mostly_written_lang_not_available: "Mostly written in language not available" - licenses: "Licenses:" - no_declared_licenses: "No declared licenses" - n_more: "%{n} more..." index_managed: page_title: "%{name} : Projects - Open Hub" managed_projects: "Managed Projects" @@ -110,7 +114,6 @@ en: community_rating: "Community Rating" in_a_nutshell: "In a Nutshell, %{name}" lines_of_code: "Lines of Code" - contributors: "Contributors" total_commits: "Total Commits" note: "Note:" organization: "Organization" diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index 0c4a958ea..c9de90f8f 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -20,9 +20,9 @@ class ProjectsControllerTest < ActionController::TestCase login_as nil get :index, params: { query: 'foo' } assert_response :ok - assert_select "div.well#project_#{project1.id}", true - assert_select "div.well#project_#{project2.id}", true - assert_select "div.well#project_#{project3.id}", false + assert_select "div#project_#{project1.id}", true + assert_select "div#project_#{project2.id}", true + assert_select "div#project_#{project3.id}", false end it 'index should handle the q param for unlogged users' do @@ -32,9 +32,9 @@ class ProjectsControllerTest < ActionController::TestCase login_as nil get :index, params: { q: 'foo' } assert_response :ok - assert_select "div.well#project_#{project1.id}", true - assert_select "div.well#project_#{project2.id}", true - assert_select "div.well#project_#{project3.id}", false + assert_select "div#project_#{project1.id}", true + assert_select "div#project_#{project2.id}", true + assert_select "div#project_#{project3.id}", false end it 'index should handle query param that matches no project' do diff --git a/test/controllers/session_projects_controller_test.rb b/test/controllers/session_projects_controller_test.rb index 3391bca51..f7fb07734 100644 --- a/test/controllers/session_projects_controller_test.rb +++ b/test/controllers/session_projects_controller_test.rb @@ -28,7 +28,7 @@ class SessionProjectsControllerTest < ActionController::TestCase assert_response :success _(session[:session_projects]).must_equal [project.to_param] - assert_select "form#sp_frm_#{project.to_param}" + assert_select "button.remove-project[data-project-id='#{project.to_param}']" end it 'must prevent bot access' do From f8ce76f30756fd5dab35e0b17e2da16fb5c28b54 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Tue, 17 Mar 2026 16:14:19 +0530 Subject: [PATCH 18/53] OTWO-7595 Implement project explore page redesign per Figma --- app/assets/javascripts/demographics.js.coffee | 2 +- app/assets/javascripts/explore.js.coffee | 12 +- app/assets/stylesheets/charts.sass | 58 +- app/assets/stylesheets/dark_theme.sass | 105 ++ app/assets/stylesheets/explore.sass | 1058 +++++++++++++++-- app/assets/stylesheets/search-dingus.sass | 1 + app/controllers/explore_controller.rb | 6 +- .../project/activity_level_index.rb | 4 +- app/decorators/project/demographic_chart.rb | 9 +- app/views/explore/_hot_projects.html.haml | 98 +- .../explore/_project_demographics.html.haml | 48 +- app/views/explore/_projects.html.haml | 54 +- app/views/explore/_projects_sidebar.html.haml | 125 +- app/views/explore/projects.html.haml | 2 - config/charting/demographic.yml | 24 +- 15 files changed, 1393 insertions(+), 213 deletions(-) diff --git a/app/assets/javascripts/demographics.js.coffee b/app/assets/javascripts/demographics.js.coffee index 69a951832..dc7f669d3 100644 --- a/app/assets/javascripts/demographics.js.coffee +++ b/app/assets/javascripts/demographics.js.coffee @@ -1,6 +1,6 @@ ProjectDemographics = init: () -> - return if $('#project_demographics').length == 0 + return if $('#demographics_chart').length == 0 $.ajax url: $('#demographics_chart').data('src') diff --git a/app/assets/javascripts/explore.js.coffee b/app/assets/javascripts/explore.js.coffee index 10e85e195..530d0b7b4 100644 --- a/app/assets/javascripts/explore.js.coffee +++ b/app/assets/javascripts/explore.js.coffee @@ -1,6 +1,6 @@ App.Explore = init: () -> - return if $('#explore_projects_page').length == 0 + return if $('#explore_projects_page').length == 0 && $('.explore-projects-page').length == 0 $('#explore_search_form .icon-search').click (e) -> e.preventDefault() @@ -30,5 +30,15 @@ App.Explore = $(this).attr('disabled', 'disabled') $(this).parents('form').attr('action', document.location).submit() + # Language filter dropdown toggle + $(document).on 'click', '.language-toggle', (e) -> + e.stopPropagation() + menu = $(this).siblings('.language-dropdown-menu') + menu.toggleClass('show') + + $(document).on 'click', (e) -> + unless $(e.target).closest('.language-dropdown').length + $('.language-dropdown-menu').removeClass('show') + $(document).on 'page:change', -> App.Explore.init() diff --git a/app/assets/stylesheets/charts.sass b/app/assets/stylesheets/charts.sass index 3fd13b400..7f3f511f2 100644 --- a/app/assets/stylesheets/charts.sass +++ b/app/assets/stylesheets/charts.sass @@ -237,93 +237,75 @@ .highcharts-point-inactive opacity: 1.0 + .highcharts-series .highcharts-point + stroke: rgba(255, 255, 255, 0.6) + + .highcharts-legend-item .highcharts-point + stroke-width: 0 + .highcharts-point.highcharts-color-0, .highcharts-legend-item.highcharts-color-0 .highcharts-point, .highcharts-tooltip .highcharts-color-0 .highcharts-color-0, .highcharts-pointhighcharts-color-0 - @include projects-demographics-inactive-fill + fill: #64748b .highcharts-tooltip.highcharts-color-0, .highcharts-data-label-connector.highcharts-color-0 - @include projects-demographics-inactive-stroke + stroke: #64748b .highcharts-point.highcharts-color-1, .highcharts-legend-item.highcharts-color-1 .highcharts-point, .highcharts-tooltip .highcharts-color-1 - @include projects-demographics-very-low-fill - - .highcharts-halo.highcharts-color-1 - @include projects-demographics-very-low-fill - opacity: 0.3 + fill: #06b6d4 .highcharts-tooltip.highcharts-color-1, .highcharts-data-label-connector.highcharts-color-1 - @include projects-demographics-very-low-stroke + stroke: #06b6d4 .highcharts-point.highcharts-color-2, .highcharts-legend-item.highcharts-color-2 .highcharts-point, .highcharts-tooltip .highcharts-color-2 - @include projects-demographics-low-fill - - .highcharts-halo.highcharts-color-2 - @include projects-demographics-low-fill - opacity: 0.3 + fill: #3b82f6 .highcharts-tooltip.highcharts-color-2, .highcharts-data-label-connector.highcharts-color-2 - @include projects-demographics-low-stroke + stroke: #3b82f6 .highcharts-point.highcharts-color-3, .highcharts-legend-item.highcharts-color-3 .highcharts-point, .highcharts-tooltip .highcharts-color-3 - @include projects-demographics-moderate-fill - - .highcharts-halo.highcharts-color-3 - @include projects-demographics-moderate-fill - opacity: 0.3 + fill: #10b981 .highcharts-tooltip.highcharts-color-3, .highcharts-data-label-connector.highcharts-color-3 - @include projects-demographics-moderate-stroke + stroke: #10b981 .highcharts-point.highcharts-color-4, .highcharts-legend-item.highcharts-color-4 .highcharts-point, .highcharts-tooltip .highcharts-color-4 - @include projects-demographics-high-fill - - .highcharts-halo.highcharts-color-4 - @include projects-demographics-high-fill - opacity: 0.3 + fill: #f59e0b .highcharts-tooltip.highcharts-color-4, .highcharts-data-label-connector.highcharts-color-4 - @include projects-demographics-high-stroke + stroke: #f59e0b .highcharts-point.highcharts-color-5, .highcharts-legend-item.highcharts-color-5 .highcharts-point, .highcharts-tooltip .highcharts-color-5 - @include projects-demographics-very-high-fill - - .highcharts-halo.highcharts-color-5 - @include projects-demographics-very-high-fill - opacity: 0.3 + fill: #ef4444 .highcharts-tooltip.highcharts-color-5, .highcharts-data-label-connector.highcharts-color-5 - @include projects-demographics-very-high-stroke + stroke: #ef4444 .highcharts-point.highcharts-color-6, .highcharts-legend-item.highcharts-color-6 .highcharts-point, .highcharts-tooltip .highcharts-color-6 - @include projects-demographics-new-fill - - .highcharts-halo.highcharts-color-6 - @include projects-demographics-new-fill - opacity: 0.3 + fill: #a855f7 .highcharts-tooltip.highcharts-color-6, .highcharts-data-label-connector.highcharts-color-6 - @include projects-demographics-new-stroke + stroke: #a855f7 #vulnerability_all_version_chart overflow: visible !important diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 5b8198f4f..ca6ff9992 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -168,3 +168,108 @@ html.dark color: #60a5fa !important &:hover color: #93c5fd !important + + // ── Explore Projects page overrides ────────────────────────────────────── + .explore-projects-page + + // Global input/form resets for explore-specific components + .sidebar-search-input, + .tag-input, + .compare-input + background-color: #1D0631 !important + color: #ffffff !important + + .sidebar-search-input + border-color: #5A2A82 !important + + .tag-input, + .compare-input + border-color: #374151 !important + + // Sidebar cards + .sidebar-card + background: #2D1548 !important + border-color: #ffb91a !important + + // Table container + .hot-projects-container, + .demographics-container + background: #2D1548 !important + + // Table header row + .projects-table-header + background: #3A1D58 !important + border-color: #374151 !important + + // Table cells header text + .header-cell + color: #d1d5db !important + + // Project rows + .project-row + border-bottom-color: #374151 !important + + // Rank numbers + .rank-number + color: #ffb91a !important + + // Project icon gradient + .project-icon + background: linear-gradient(to bottom right, #374151, #1f2937) !important + + // Stat cards + .stat-card + &.total-card + background: linear-gradient(to bottom right, #2D1548, #3A1D58) !important + border-color: #5A2A82 !important + &.pai-card + background: linear-gradient(to bottom right, #134e4a, #0f766e) !important + border-color: #0d9488 !important + &.active-card + background: linear-gradient(to bottom right, #3b0764, #4c1d95) !important + border-color: #7c3aed !important + &.new-card + background: linear-gradient(to bottom right, #451a03, #78350f) !important + border-color: #d97706 !important + + // Stat labels and values + .stat-label + color: #d1d5db !important + .stat-value + color: #ffffff !important + + // Buttons + .btn-add-project, + .btn-add-project-full, + .btn-compare + background: #ffb91a !important + color: #1D0631 !important + + // Language dropdown menu + .language-dropdown-menu + background: #2D1548 !important + border-color: #5A2A82 !important + + .language-option + color: #ffffff !important + &:hover + background: #374151 !important + &.active + background: #ffb91a !important + color: #1D0631 !important + + // PAI link + .pai-link + color: #ffb91a !important + + // Hotness score + .hotness-score + color: #ffffff !important + + // Demographics subtitle + .demographics-subtitle + color: #9ca3af !important + + // Add project text + .add-project-text + color: #9ca3af !important diff --git a/app/assets/stylesheets/explore.sass b/app/assets/stylesheets/explore.sass index 9b90ecc23..72dffa6f9 100644 --- a/app/assets/stylesheets/explore.sass +++ b/app/assets/stylesheets/explore.sass @@ -1,86 +1,974 @@ -#explore_projects_page - #explore_sidebar - box-shadow: none - width: 28% - min-height: 650px - border-radius: 0px - border: none - #tagcloud - a - line-height: 17px - .icon-search - &.search - @include explore-sidebar-icon-search-color - cursor: pointer - font-size: 18px - position: relative - top: 3px - input - @include explore-sidebar-search-text-color - &:-moz-placeholder - @include explore-sidebar-placeholder-color - font-style: italic - &:-ms-input-placeholder - @include explore-sidebar-placeholder-color - font-style: italic - &::-webkit-input-placeholder - @include explore-sidebar-placeholder-color - font-style: italic - .explore_search - width: 173px - margin-top: 7px - &.tag_autocomplete - width: 194px - .explore_compare - width: 194px - - #hot_projects td - :padding 5px - &.pai - position: relative - a[class^="twentyfive_project_activity_level_"] - margin-left: 12px - margin-top: -12px - position: absolute - top: 24px - - #project_demographics - margin-top: 30px - - #demographics_chart - min-height: 480px - - .similar_tags_project_name - margin-bottom: 0.5em - width: 185px - margin-left: 10px - .similar_tags_tagged - width: 245px - margin-left: 10px - - #tagcloud - :margin 5px - - span.tag.container - background: none - border: none - margin-top: -2px - margin-bottom: -2px - padding: 2px - cursor: default - - #value_selector_selected_tags - width: 400px - - .add_new_project_btn_bottom - margin-top: 25px - margin-left: -10px - - .add_new_project_btn_top - float: right - top: -9px - left: 9px - height: 30px - &.btn-mini - padding: 3px 5px !important +// Explore Projects Page - Redesigned to match Figma specifications +// Color Palette +$primary-purple: #5A2A82 +$primary-yellow: #ffb91a +$bg-white: #ffffff +$bg-light: #f8f9fa +$bg-dark: #1D0631 +$bg-card-dark: #2D1548 +$bg-table-dark: #3A1D58 +$text-dark: #1a202c +$text-gray: #4a5568 +$text-light: #718096 +$border-gray: #e2e8f0 +$shadow-light: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) +$shadow-medium: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) +$shadow-hover: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) +$shadow-dark: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) +$shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) +// Main Container +.explore-projects-page + min-height: 100vh + background: $bg-white + padding: 32px 0 + transition: background-color 0.3s ease + + html.dark & + background: $bg-dark + + @media (max-width: 768px) + padding: 16px 0 + +// Full-width wrapper for header (above the grid) +.explore-page-wrapper + max-width: 1280px + margin: 0 auto + padding: 0 16px + margin-bottom: 24px + +// Main Layout Container (sidebar + content grid) +.explore-main-container + max-width: 1280px + margin: 0 auto + padding: 0 16px + display: grid + grid-template-columns: 280px 1fr + gap: 32px + + @media (max-width: 1024px) + grid-template-columns: 1fr + gap: 24px + +// Sidebar Styles +.explore-sidebar + display: block + + @media (max-width: 1024px) + display: none + +.sidebar-container + display: flex + flex-direction: column + gap: 24px + +.sidebar-card + background: $bg-white + border: 2px solid $primary-purple + border-radius: 8px + box-shadow: $shadow-light + overflow: hidden + transition: all 0.3s ease + + html.dark & + background: $bg-card-dark + border-color: $primary-yellow + box-shadow: $shadow-dark + + &:hover + box-shadow: $shadow-medium + + html.dark & + box-shadow: $shadow-dark-hover + + .card-header + padding: 12px 16px + background: transparent + display: flex + justify-content: space-between + align-items: center + + html.dark & + border-bottom-color: rgba(255, 255, 255, 0.1) + + &.collapsible + cursor: pointer + border: none + background: transparent + width: 100% + text-align: left + transition: background 0.2s ease + + &:hover + background: rgba($primary-purple, 0.05) + + html.dark & + background: rgba($primary-yellow, 0.1) + + .card-title + font-size: 14px + font-weight: 600 + color: $text-dark + margin: 0 + + html.dark & + color: #ffffff !important + + .card-body + padding: 16px + +// Sidebar Search Form +.sidebar-search-form, +.similar-form + .search-input-wrapper + position: relative + +.search-input-wrapper + position: relative + +.explore-projects-page .sidebar-search-input + width: 100% + display: block + padding: 8px 40px 8px 12px + border: 1px solid $border-gray + border-radius: 4px + font-size: 14px + color: $text-dark + background: $bg-white + box-sizing: border-box + height: auto + margin-bottom: 0 + transition: all 0.2s ease + + html.dark & + background: $bg-dark !important + border-color: $primary-purple !important + color: #ffffff !important + + &:focus + outline: none + border-color: $primary-purple + box-shadow: 0 0 0 3px rgba($primary-purple, 0.1) + + html.dark & + border-color: $primary-yellow !important + box-shadow: 0 0 0 3px rgba($primary-yellow, 0.1) + + &::placeholder + color: $text-light + + html.dark & + color: #9ca3af !important + +.search-icon-btn + position: absolute + right: 8px + top: 50% + transform: translateY(-50%) + background: transparent + border: none + padding: 4px + cursor: pointer + color: $text-light + line-height: 1 + transition: color 0.2s ease + + html.dark & + color: #9ca3af + + &:hover + color: $primary-purple + + html.dark & + color: $primary-yellow + +// Tag Cloud +.tag-cloud + display: flex + flex-wrap: wrap + gap: 8px + margin-bottom: 16px + +.tag-item + display: inline-block + padding: 6px 12px + background: $bg-light + border: 1px solid $border-gray + border-radius: 4px + font-size: 12px + color: $primary-purple + text-decoration: none + transition: all 0.2s ease + cursor: pointer + + html.dark & + background: $bg-dark + border-color: #374151 + color: $primary-yellow + + &:hover + background: $primary-purple + color: $bg-white + border-color: $primary-purple + text-decoration: none + transform: translateY(-2px) + + html.dark & + background: $primary-yellow + color: $bg-dark + border-color: $primary-yellow + +// Tag custom input with icon inside +.tag-input-wrapper + position: relative + margin-top: 12px + +.explore-projects-page .tag-input + width: 100% + display: block + padding: 8px 36px 8px 12px + border: 1px solid #d1d5db + border-radius: 4px + font-size: 14px + color: #111827 + background: $bg-white + box-sizing: border-box + height: auto + margin-bottom: 0 + transition: all 0.2s ease + + html.dark & + background: $bg-dark !important + border-color: $primary-purple !important + color: #ffffff !important + + &::placeholder + color: #9ca3af + + html.dark & + color: #6b7280 !important + + &:focus + outline: none + border-color: $primary-purple + box-shadow: 0 0 0 3px rgba($primary-purple, 0.1) + + html.dark & + border-color: $primary-yellow !important + box-shadow: 0 0 0 3px rgba($primary-yellow, 0.1) + +.tag-search-btn + position: absolute + right: 8px + top: 50% + transform: translateY(-50%) + background: transparent + border: none + padding: 4px + cursor: pointer + color: #9ca3af + transition: color 0.2s ease + line-height: 1 + + html.dark & + color: #6b7280 + + &:hover + color: $primary-purple + + html.dark & + color: $primary-yellow + + i + font-size: 14px + +// Compare Form +.compare-inputs + display: flex + flex-direction: column + gap: 8px + margin-bottom: 12px + +.explore-projects-page .compare-input + width: 100% + display: block + padding: 8px 12px + border: 1px solid #d1d5db + border-radius: 4px + font-size: 14px + color: #111827 + background: $bg-white + box-sizing: border-box + height: auto + margin-bottom: 0 + transition: all 0.2s ease + + html.dark & + background: $bg-dark !important + border-color: #374151 !important + color: #ffffff !important + + &::placeholder + color: #9ca3af + + html.dark & + color: #6b7280 !important + + &:focus + outline: none + border-color: $primary-purple + box-shadow: 0 0 0 3px rgba($primary-purple, 0.1) + + html.dark & + border-color: $primary-yellow !important + box-shadow: 0 0 0 3px rgba($primary-yellow, 0.1) + +.btn-compare + width: 100% + padding: 8px 16px + background: $primary-purple + color: $bg-white + border: none + border-radius: 4px + font-weight: 600 + font-size: 14px + cursor: pointer + transition: all 0.2s ease + box-shadow: $shadow-light + + html.dark & + background: $primary-yellow + color: $bg-dark + box-shadow: $shadow-dark + + &:hover + background: darken($primary-purple, 10%) + box-shadow: $shadow-medium + + html.dark & + background: rgba($primary-yellow, 0.9) + box-shadow: $shadow-dark-hover + +// Add Project Card +.add-project-text + font-size: 12px + color: $text-gray + line-height: 1.5 + margin-bottom: 12px + + html.dark & + color: #9ca3af + +.btn-add-project-full + display: flex + align-items: center + justify-content: center + gap: 8px + width: 100% + padding: 8px 16px + background: $primary-purple + color: $bg-white + border: none + border-radius: 4px + font-weight: 600 + font-size: 14px + text-decoration: none + cursor: pointer + transition: all 0.2s ease + box-shadow: $shadow-light + + &:hover + text-decoration: none + + html.dark & + background: $primary-yellow + color: $bg-dark + box-shadow: $shadow-dark + + &:hover + background: darken($primary-purple, 10%) + box-shadow: $shadow-medium + color: $bg-white + + html.dark & + background: rgba($primary-yellow, 0.9) + color: $bg-dark + +// Main Content Area +.explore-content + min-width: 0 + +// Header Section +.explore-header + margin-bottom: 32px + +.explore-header-top + display: flex + justify-content: space-between + align-items: flex-start + gap: 16px + margin-bottom: 24px + flex-wrap: wrap + + @media (max-width: 768px) + flex-direction: column + +.explore-title + font-size: 36px !important + font-weight: 700 !important + color: $text-dark !important + margin: 0 !important + letter-spacing: normal !important + line-height: 1.2 !important + + html.dark & + color: #ffffff !important + + @media (max-width: 768px) + font-size: 30px !important + +.explore-header-actions + display: flex + gap: 16px + align-items: center + flex-wrap: wrap + + @media (max-width: 768px) + width: 100% + +.btn-add-project + display: inline-flex + align-items: center + justify-content: center + gap: 8px + padding: 8px 16px + background: $primary-purple + color: $bg-white + border: none + border-radius: 4px + font-weight: 600 + font-size: 14px + text-decoration: none + cursor: pointer + transition: all 0.2s ease + box-shadow: $shadow-light + white-space: nowrap + + &:hover + text-decoration: none + + html.dark & + background: $primary-yellow + color: $bg-dark + box-shadow: $shadow-dark + + &:hover + background: darken($primary-purple, 10%) + box-shadow: $shadow-medium + color: $bg-white + + html.dark & + background: rgba($primary-yellow, 0.9) + color: $bg-dark + +.btn-desktop-only + @media (max-width: 640px) + display: none + +// Mobile Discover More +.discover-more-mobile + display: none + margin-bottom: 24px + + @media (max-width: 1024px) + display: block + +.discover-toggle + width: 100% + display: flex + justify-content: space-between + align-items: center + padding: 12px 16px + background: linear-gradient(to right, $primary-purple, $primary-yellow) + color: $bg-white + border: none + border-radius: 8px + cursor: pointer + box-shadow: $shadow-light + transition: all 0.2s ease + + html.dark & + background: linear-gradient(to right, $primary-yellow, $primary-purple) + color: $bg-dark + box-shadow: $shadow-dark + + &:hover + box-shadow: $shadow-medium + + html.dark & + box-shadow: $shadow-dark-hover + +.discover-title + font-size: 18px + font-weight: 700 + +.discover-content + margin-top: 16px + display: none + + &.show + display: block + +// Hot Projects Section +.hot-projects-section + margin-bottom: 32px + +.hot-projects-container + background: $bg-white + border-radius: 16px + overflow: hidden + box-shadow: $shadow-light + transition: box-shadow 0.3s ease + + html.dark & + background: $bg-card-dark + box-shadow: $shadow-dark + + &:hover + box-shadow: $shadow-medium + + html.dark & + box-shadow: $shadow-dark-hover + +.hot-projects-header + display: flex + justify-content: space-between + align-items: center + padding: 0 0 16px 0 + flex-wrap: wrap + gap: 16px + + @media (max-width: 768px) + flex-direction: column + align-items: flex-start + +.section-title + font-size: 24px !important + font-weight: 700 !important + color: $text-dark !important + line-height: 1.2 !important + margin: 0 !important + + html.dark & + color: #ffffff !important + + @media (max-width: 768px) + font-size: 20px !important + +.language-filter-wrapper + display: flex + align-items: center + gap: 12px + flex-wrap: wrap + +.filter-label + font-size: 14px + color: $text-gray + white-space: nowrap + + html.dark & + color: #d1d5db + +.language-dropdown + position: relative + + .language-toggle + display: flex + align-items: center + gap: 8px + padding: 6px 12px + background: $bg-light + border: 1px solid $border-gray + border-radius: 4px + cursor: pointer + transition: all 0.2s ease + + html.dark & + background: $bg-dark + border-color: $primary-purple + color: #ffffff + + &:hover + background: darken($bg-light, 5%) + + html.dark & + background: rgba($bg-dark, 0.8) + border-color: $primary-yellow + + .language-selected + font-size: 14px + color: $text-dark + + html.dark & + color: #ffffff + + .language-dropdown-menu + display: none + position: absolute + top: 100% + right: 0 + margin-top: 4px + background: $bg-white + border: 1px solid #d1d5db + border-radius: 8px + box-shadow: $shadow-hover + z-index: 1000 + min-width: 180px + max-height: 300px + overflow-y: auto + + html.dark & + background: $bg-card-dark !important + border-color: $primary-purple !important + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + &.show + display: block + +.language-option + display: block + padding: 8px 12px + font-size: 14px + color: $text-dark + text-decoration: none + transition: all 0.2s ease + + &:hover + text-decoration: none + + html.dark & + color: #ffffff !important + + &:hover + background: $bg-light + + html.dark & + background: #374151 !important + + &.active + background: $primary-purple + color: $bg-white + + html.dark & + background: $primary-yellow !important + color: $bg-dark !important + +// Projects Table +.hot-projects-table-wrapper + overflow-x: auto + +.projects-table-header + display: grid + grid-template-columns: 50px 50px minmax(80px, 1fr) minmax(80px, 150px) 80px 80px + gap: 16px + padding: 16px 24px + background: $bg-light + border-top: 1px solid $border-gray + border-bottom: 1px solid $border-gray + + html.dark & + background: $bg-table-dark + border-color: #374151 + + @media (max-width: 768px) + grid-template-columns: 40px 40px 1fr 80px 60px 60px + gap: 8px + padding: 12px 16px + +.header-cell + font-size: 12px + font-weight: 600 + color: $text-gray + text-transform: uppercase + letter-spacing: 0.05em + + html.dark & + color: #d1d5db + + &.rank-col, &.pai-col, &.hotness-col + text-align: center + + &.icon-col + text-align: center + +.projects-table-body + display: flex + flex-direction: column + +.project-row + display: grid + grid-template-columns: 50px 50px minmax(80px, 1fr) minmax(80px, 150px) 80px 80px + gap: 16px + padding: 16px 24px + border-bottom: 1px solid $border-gray + transition: background 0.2s ease + cursor: pointer + + html.dark & + border-bottom-color: #374151 + + &:hover + background: rgba($primary-purple, 0.05) + + html.dark & + background: rgba($bg-dark, 0.5) + + @media (max-width: 768px) + grid-template-columns: 40px 40px 1fr 80px 60px 60px + gap: 8px + padding: 12px 16px + +.table-cell + display: flex + align-items: center + font-size: 14px + + &.rank-col + justify-content: center + + &.icon-col + justify-content: center + + &.name-col + min-width: 0 + + &.pai-col + justify-content: center + + &.hotness-col + justify-content: flex-end + +.rank-number + font-size: 18px + font-weight: 700 + color: $primary-purple + + html.dark & + color: $primary-yellow + + @media (max-width: 768px) + font-size: 14px + +.project-icon + width: 40px + height: 40px + display: flex + align-items: center + justify-content: center + background: linear-gradient(to bottom right, $bg-light, darken($bg-light, 5%)) + border-radius: 4px + + html.dark & + background: linear-gradient(to bottom right, #374151, #1f2937) + + @media (max-width: 768px) + width: 32px + height: 32px + +.project-name-link + color: #3182ce + font-weight: 500 + font-size: 14px + text-decoration: none + overflow: hidden + text-overflow: ellipsis + white-space: nowrap + + html.dark & + color: #60a5fa !important + + &:hover + text-decoration: underline + +.org-link + color: #3182ce + font-size: 14px + text-decoration: none + overflow: hidden + text-overflow: ellipsis + white-space: nowrap + + html.dark & + color: #60a5fa !important + + &:hover + text-decoration: underline + +.no-org + color: $text-light + font-size: 14px + + html.dark & + color: #9ca3af + +.hotness-score + font-weight: 600 + color: $text-dark + + html.dark & + color: #ffffff + +.no-projects + padding: 48px + text-align: center + +.no-projects-message + color: $text-light + font-size: 16px + + html.dark & + color: #9ca3af + +// Demographics Section +.demographics-section + margin-bottom: 32px + +.demographics-container + background: $bg-white + border-radius: 16px + overflow: hidden + box-shadow: $shadow-light + padding: 24px + transition: box-shadow 0.3s ease + + html.dark & + background: $bg-card-dark + box-shadow: $shadow-dark + + &:hover + box-shadow: $shadow-medium + + html.dark & + box-shadow: $shadow-dark-hover + +.demographics-header + margin-bottom: 24px + +.demographics-subtitle + font-size: 14px + color: $text-gray + margin-top: 8px + + html.dark & + color: #9ca3af + +.pai-link + color: $primary-purple + text-decoration: none + + html.dark & + color: $primary-yellow + + &:hover + text-decoration: underline + +.demographics-chart-wrapper + min-height: 480px + margin-bottom: 24px + + @media (max-width: 768px) + min-height: 400px + +// Statistics Summary +.statistics-summary + display: grid + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)) + gap: 16px + margin-top: 24px + + @media (max-width: 640px) + grid-template-columns: repeat(2, 1fr) + +.stat-card + padding: 16px + border-radius: 8px + border: 1px solid $border-gray + transition: all 0.2s ease + + &:hover + transform: translateY(-2px) + box-shadow: $shadow-light + + html.dark & + box-shadow: $shadow-dark + + &.total-card + background: linear-gradient(to bottom right, lighten($bg-light, 2%), $bg-light) + border-color: darken($border-gray, 5%) + + html.dark & + background: linear-gradient(to bottom right, #2D1548, #3A1D58) + border-color: #5A2A82 + + &.pai-card + background: linear-gradient(to bottom right, #e6fffa, #b2f5ea) + border-color: #81e6d9 + + html.dark & + background: linear-gradient(to bottom right, #134e4a, #0f766e) + border-color: #0d9488 + + &.active-card + background: linear-gradient(to bottom right, #faf5ff, #e9d8fd) + border-color: #d6bcfa + + html.dark & + background: linear-gradient(to bottom right, #3b0764, #4c1d95) + border-color: #7c3aed + + &.new-card + background: linear-gradient(to bottom right, #fffbeb, #fef3c7) + border-color: #fde68a + + html.dark & + background: linear-gradient(to bottom right, #451a03, #78350f) + border-color: #d97706 + +.stat-label + font-size: 12px + color: $text-gray + margin-bottom: 4px + font-weight: 500 + + html.dark & + color: #d1d5db + +.stat-value + font-size: 20px + font-weight: 700 + color: $text-dark + margin: 0 + + html.dark & + color: #ffffff + + @media (max-width: 768px) + font-size: 18px + +// Utility Classes +.hidden + display: none + +// PAI Indicator fix for explore table +// project_activity_level_class renders with position: absolute; top: 16px (from projects.sass) +// We need a positioned parent and override the top offset +.explore-projects-page + .pai-indicator + position: relative + min-height: 41px + display: flex + align-items: center + justify-content: center + [class^="twentyfive_project_activity_level_"] + top: 8px !important + margin-left: 0 !important diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index f2b1faa6f..563a2c71e 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -126,6 +126,7 @@ &::placeholder color: #6b7280 + font-style: normal &:focus outline: none diff --git a/app/controllers/explore_controller.rb b/app/controllers/explore_controller.rb index 687b3f2f5..a9ee6dfd6 100644 --- a/app/controllers/explore_controller.rb +++ b/app/controllers/explore_controller.rb @@ -12,7 +12,11 @@ def index end def demographic_chart - render json: Project::DemographicChart.data + data = Project::DemographicChart.data + render json: data + rescue StandardError => e + Rails.logger.error("DemographicChart failed: #{e.message}") + render json: nil, status: :ok end def orgs diff --git a/app/decorators/project/activity_level_index.rb b/app/decorators/project/activity_level_index.rb index c22d0703e..a6f4ddfa0 100644 --- a/app/decorators/project/activity_level_index.rb +++ b/app/decorators/project/activity_level_index.rb @@ -30,10 +30,10 @@ def percentage end def sliced - @level_index == INACTIVE_INDEX + false end def selected - name == ACTIVITY_LEVEL_INDEX[INACTIVE_INDEX] + false end end diff --git a/app/decorators/project/demographic_chart.rb b/app/decorators/project/demographic_chart.rb index ba058a06c..da7fdb9f2 100644 --- a/app/decorators/project/demographic_chart.rb +++ b/app/decorators/project/demographic_chart.rb @@ -8,15 +8,16 @@ class << self def data default_options = DEMOGRAPHIC_CHART_DEFAULTS.deep_dup default_options['plotOptions']['pie']['startAngle'] = angle - default_options['series'] << { 'data' => activity_level_data } + default_options['series'][0]['data'] = activity_level_data default_options end private def activity_level_data - level_data = count_by_activity_level.each_with_object([]) do |(level, count), array| - array.push Project::ActivityLevelIndex.new(level, count, total_count).demographic_chart_data + level_data = Project::ActivityLevelIndex::ACTIVITY_LEVEL_INDEX.map do |level, _| + count = count_by_activity_level[level].to_i + Project::ActivityLevelIndex.new(level, count, [total_count, 1].max).demographic_chart_data end level_data.sort_by { |d| SORT_ORDER.index(d[:name]) } end @@ -32,7 +33,7 @@ def total_count end def angle - 90.0 - (360 * (count_by_activity_level[INACTIVE_INDEX].to_f / total_count) / 2) + 0 end end end diff --git a/app/views/explore/_hot_projects.html.haml b/app/views/explore/_hot_projects.html.haml index 07e4c341e..cd98d5978 100644 --- a/app/views/explore/_hot_projects.html.haml +++ b/app/views/explore/_hot_projects.html.haml @@ -1,20 +1,78 @@ -.pull-right.col-md-8.margin_top_20 - %h2.pull-left= t('.title') - %form{ action: '', rel: 'sort_filter' } - .pull-right.nav-select-container#sort_by - %label.pull-left{ style: 'line-height: 28px' } - = t('.filter') -   - = select_tag :lang, options_for_select(@languages, params[:lang]), class: 'chzn-select col-md-2 pull-right' - .clearfix - %table.table.table-striped.center-block#hot_projects - %tr - %th{ width: '5%' }= t('.rank') - %th{ width: '5%' } - %th{ width: '35%' }= t('.name') - %th{ width: '15%' }= t('.claimed') - %th.center{ width: '8%' }= t('.pai') - %th.center{ width: '12%' } - = link_to t('.hotness_score'), blog_url_for(:hotness_score), class: 'meta', target: '_blank' - = render 'hot_projects_list' - .clearfix +/ Header with Language Filter (outside table card, matching Figma) +.hot-projects-header + %h2.section-title= t('.title', default: 'Hot Projects on Open Hub') + + .language-filter-wrapper + %span.filter-label= t('.filter_by', default: 'Filter by:') + + .language-dropdown + %button.language-toggle{ type: 'button', data: { toggle: 'language' } } + %span.language-selected= params[:lang].present? ? params[:lang] : t('.all_languages', default: 'All Languages') + %span.chevron-down ▼ + + .language-dropdown-menu + - if defined?(@languages) && @languages.present? + - @languages.each do |lang_name, lang_code| + = link_to lang_name, lang_code.blank? ? projects_explores_path : projects_explores_path(lang: lang_code), class: "language-option#{(lang_code.blank? && params[:lang].blank?) || params[:lang] == lang_code ? ' active' : ''}" + - else + = link_to t('.all_languages', default: 'All Languages'), projects_explores_path, class: "language-option#{params[:lang].blank? ? ' active' : ''}" + = link_to 'C++', projects_explores_path(lang: 'c++'), class: "language-option#{params[:lang] == 'c++' ? ' active' : ''}" + = link_to 'PHP', projects_explores_path(lang: 'php'), class: "language-option#{params[:lang] == 'php' ? ' active' : ''}" + = link_to 'Python', projects_explores_path(lang: 'python'), class: "language-option#{params[:lang] == 'python' ? ' active' : ''}" + = link_to 'JavaScript', projects_explores_path(lang: 'javascript'), class: "language-option#{params[:lang] == 'javascript' ? ' active' : ''}" + = link_to 'Java', projects_explores_path(lang: 'java'), class: "language-option#{params[:lang] == 'java' ? ' active' : ''}" + +/ Hot Projects Table Card +.hot-projects-container + .hot-projects-table-wrapper + / Table Header + .projects-table-header + .header-cell.rank-col= t('.rank', default: 'Rank') + .header-cell.icon-col + .header-cell.name-col= t('.name', default: 'Name') + .header-cell.claimed-col= t('.claimed_by', default: 'Claimed By') + .header-cell.pai-col= t('.pai', default: 'PAI') + .header-cell.hotness-col + = link_to t('.hotness_score', default: 'Hotness'), blog_url_for(:hotness_score), class: 'meta', target: '_blank' + + / Table Body + .projects-table-body + - if defined?(@projects) && @projects.present? + - @projects.each_with_index do |project, index| + - project_name = truncate(project.name, length: 26) + - org = project.organization + - org_name = truncate(org.name, length: 30) if org + + .project-row + / Rank + .table-cell.rank-col + %span.rank-number= (index + 1) + + / Icon + .table-cell.icon-col + .project-icon + = project.decorate.icon(:small) + + / Name + .table-cell.name-col + = link_to h(project_name), project_path(project), class: 'project-name-link' + + / Claimed By + .table-cell.claimed-col + - if org + = link_to h(org_name), organization_path(org), class: 'org-link', title: org.name + - else + %span.no-org — + + / PAI Indicator + .table-cell.pai-col + .pai-indicator + - project_activity_level_class(project, :twentyfive) + + / Hotness Score + .table-cell.hotness-col + %span.hotness-score= number_with_precision(project.best_analysis.angle, precision: 1) + + - else + .no-projects + %p.no-projects-message= t('.no_matching_records', default: 'No matching projects found') diff --git a/app/views/explore/_project_demographics.html.haml b/app/views/explore/_project_demographics.html.haml index 6a01fbb8e..5f578696e 100644 --- a/app/views/explore/_project_demographics.html.haml +++ b/app/views/explore/_project_demographics.html.haml @@ -1,7 +1,41 @@ -.pull-right.col-md-8#project_demographics - %h2 - = t('.demographics_by') - = link_to 'PAI', blog_url_for(:pai_about), target: '_blank' - = number_with_delimiter(@with_pai_count) - = t '.out_of', count: number_with_delimiter(@total_count) - #demographics_chart{ data: { src: demographic_chart_explores_path } } +.demographics-container#project_demographics + .demographics-header + %h2.section-title + = t('.demographics_by', default: 'Project Demographics by') + = link_to 'PAI', blog_url_for(:pai_about), target: '_blank', class: 'pai-link' + + %p.demographics-subtitle + - if defined?(@with_pai_count) && defined?(@total_count) + = "#{number_with_delimiter(@with_pai_count)} #{t('.projects_out_of', default: 'Projects out of')} #{number_with_delimiter(@total_count)} #{t('.total', default: 'total have a PAI available')}" + - else + = t('.default_stats', default: '85,813 Projects out of 255,787 total have a PAI available') + + / Demographics Chart + .demographics-chart-wrapper + #demographics_chart{ data: { src: demographic_chart_explores_path } } + + / Statistics Summary Cards + .statistics-summary + .stat-card.total-card + %p.stat-label= t('.total_projects', default: 'Total Projects') + %p.stat-value + - if defined?(@total_count) + = number_with_delimiter(@total_count) + - else + = '255,787' + + .stat-card.pai-card + %p.stat-label= t('.with_pai', default: 'With PAI') + %p.stat-value + - if defined?(@with_pai_count) + = number_with_delimiter(@with_pai_count) + - else + = '85,813' + + .stat-card.active-card + %p.stat-label= t('.active', default: 'Active') + %p.stat-value= '3.2%' + + .stat-card.new-card + %p.stat-label= t('.new', default: 'New') + %p.stat-value= '0.2%' diff --git a/app/views/explore/_projects.html.haml b/app/views/explore/_projects.html.haml index 90d50efc6..7bfbe527b 100644 --- a/app/views/explore/_projects.html.haml +++ b/app/views/explore/_projects.html.haml @@ -1,5 +1,53 @@ -= render 'projects_sidebar' +.explore-projects-page + / Full-width header above the sidebar+content grid (matches Figma layout) + .explore-page-wrapper + .explore-header + .explore-header-top + %h1.explore-title= t('.title', default: 'Projects') -= render 'hot_projects' + / Add New Project Button and Search Bar + .explore-header-actions + / Add New Project Button (hidden on mobile) + = link_to new_project_path, class: 'btn-add-project btn-desktop-only' do + %i.fa.fa-plus + = t('.add_new_project', default: 'Add New Project') -= render 'project_demographics' + / Search Bar with Filters (uses search-dingus styles) + %form.header-search-form{ action: projects_path, method: :get, role: 'search' } + .btn-group.ux-dropdown.header-search-dropdown + %button.search-filter-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ val: 'p' }= t('.projects', default: 'Projects') + %i.fa.fa-chevron-down + .dropdown-menu.search-dropdown-menu + %button.dropdown-item.active{ type: 'button', val: 'p' }= t('.projects', default: 'Projects') + %button.dropdown-item{ type: 'button', val: 'people' }= t('.people', default: 'People') + %button.dropdown-item{ type: 'button', val: 'orgs' }= t('.orgs', default: 'Organizations') + = text_field_tag :query, params[:query], placeholder: t('.search_placeholder', default: 'Search projects...'), class: 'header-search-input' + %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type_explore', value: 'projects' } + %button.header-search-btn{ type: 'submit' } + %i.fa.fa-search + + / Mobile - Discover More Collapsible + .discover-more-mobile + %button.discover-toggle{ type: 'button', data: { toggle: 'collapse' } } + %span.discover-title= t('.discover_more', default: 'Discover More') + %span.chevron ▼ + + .discover-content.collapse + = render 'projects_sidebar' + + / Sidebar + Main Content Grid + .explore-main-container + / Left Sidebar - Desktop Only + %aside.explore-sidebar + = render 'projects_sidebar' + + / Main Content Area + %main.explore-content + / Hot Projects Section + .explore-section.hot-projects-section + = render 'hot_projects' + + / Project Demographics Section + .explore-section.demographics-section + = render 'project_demographics' diff --git a/app/views/explore/_projects_sidebar.html.haml b/app/views/explore/_projects_sidebar.html.haml index 21accd7b7..f9bcefb2c 100644 --- a/app/views/explore/_projects_sidebar.html.haml +++ b/app/views/explore/_projects_sidebar.html.haml @@ -1,44 +1,83 @@ -.well.col-md-3.margin_left_10.margin_top_20#explore_sidebar - = link_to t('.add_new_project'), new_project_path, class: 'btn btn-primary btn-mini add_new_project_btn_top' - %div - %h3= t('.search') - .margin_left_10 - %form.search#explore_search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'explore_project' } - %input.explore_search.margin_top_10{ type: :text, name: 'query', placeholder: t('.enter_string') } - .icon-search.search.submit - - %h3= t('.tags') - .margin_left_10{ style: 'min-height: 100px; padding-left: 15px' } - .row - #tagcloud +.sidebar-container + / Search Projects Section + .sidebar-card.search-card + .card-header + %h3.card-title= t('.search_projects', default: 'Search Projects') + + .card-body + %form.sidebar-search-form{ action: projects_path, role: 'search' } + .search-input-wrapper + = text_field_tag :query, params[:query], placeholder: t('.enter_search_string', default: 'Enter search string'), class: 'sidebar-search-input' + %button.search-icon-btn{ type: 'submit' } + %i.fa.fa-search + + / Explore Tags Section + .sidebar-card.tags-card + %button.card-header.collapsible{ type: 'button', data: { toggle: 'tags' } } + %h3.card-title= t('.explore_tags', default: 'Explore Tags') + + .card-body.tags-body + / Tag Cloud + .tag-cloud + - if defined?(@tags) && @tags.present? - @tags.each do |tag| - = link_to h(tag[0]), tags_path(names: tag[0]), data: { weight: tag[1] } - .margin_left_10 - %form.autocomplete-submit{ rel: 'tag_jump' } - %input.explore_search.tag_autocomplete.autocompletable#input_tag{ type: :text, placeholder: t('.enter_tag'), - data: { source: '/autocompletes/tags', select: 'submitForm' } } - - %p   - %h3= t('.compare') - %form.projects_compare.margin_left_15{ action: '/p/_compare' } - - compare_project_inputs.each do |options| - %input.proj.explore_compare{ options } - %input.pull-right.btn.btn-primary.btn-small#compare_btn{ type: 'submit', value: 'Compare' } - - %p   - %p   - %h3= t('.similar') - %form.autocomplete-submit.similar_projects.margin_left_10{ rel: 'similar_project_jump' } - %input.autocompletable.explore_search#project{ type: 'text', placeholder: t('.enter_project'), - autocomplete: 'off', data: { source: '/autocompletes/project', select: 'submitFormWithId' } } - .icon-search.search.submit - %span.error.hidden= t('.invalid_select') - - %p   - %h3= t('.add') - %p.justify.margin_left_10.margin_top_20{ style: 'line-height: 20px' } - = t('.community') - = t('.dont_see_a_project') - .text-center.add_new_project_btn_bottom - = link_to t('.add_new_project'), new_project_path, class: 'btn btn-primary btn-small' + = link_to h(tag[0]), tags_path(names: tag[0]), class: 'tag-item', data: { weight: tag[1] } + - else + .tag-item 3d + .tag-item C++ + .tag-item collection + .tag-item cross-platform + .tag-item development + .tag-item framework + .tag-item graphics + .tag-item linux + .tag-item php + .tag-item python + .tag-item web + .tag-item windows + + / Custom Tag Input with icon inside + .tag-input-wrapper + = text_field_tag :custom_tag, '', placeholder: t('.enter_different_tag', default: 'Enter different tag'), class: 'tag-input' + %button.tag-search-btn{ type: 'button' } + %i.fa.fa-search + + / Compare Section + .sidebar-card.compare-card + %button.card-header.collapsible{ type: 'button', data: { toggle: 'compare' } } + %h3.card-title= t('.compare', default: 'Compare:') + + .card-body.compare-body + %form.compare-form{ action: '/p/_compare', method: :get } + .compare-inputs + = text_field_tag 'project1', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + = text_field_tag 'project2', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + = text_field_tag 'project3', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + + %button.btn-compare{ type: 'submit' }= t('.compare_btn', default: 'Compare') + + / Similar Projects Section + .sidebar-card.similar-card + %button.card-header.collapsible{ type: 'button', data: { toggle: 'similar' } } + %h3.card-title= t('.similar_projects_to', default: 'Similar Projects to:') + + .card-body.similar-body + %form.similar-form.autocomplete-submit{ rel: 'similar_project_jump' } + .search-input-wrapper + = text_field_tag :project, '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'sidebar-search-input autocompletable', autocomplete: 'off', data: { source: '/autocompletes/project', select: 'submitFormWithId' } + %button.search-icon-btn{ type: 'button' } + %i.fa.fa-search + %span.error.hidden= t('.invalid_select', default: 'Invalid selection') + + / Add a Project Section + .sidebar-card.add-project-card + .card-header + %h3.card-title= t('.add_a_project', default: 'Add a Project:') + + .card-body + %p.add-project-text + = t('.community_driven', default: 'Open Hub is community driven web site that thrives when members tell us about new FOSS projects. If you don\'t see a project for which you are looking:') + + = link_to new_project_path, class: 'btn-add-project-full' do + %i.fa.fa-plus + = t('.add_new_project', default: 'Add New Project') diff --git a/app/views/explore/projects.html.haml b/app/views/explore/projects.html.haml index 9e84daa46..ed948b425 100644 --- a/app/views/explore/projects.html.haml +++ b/app/views/explore/projects.html.haml @@ -2,6 +2,4 @@ - content_for :html_title do = t('.page_title') -%h1.margin_top_15= t('.title') - = cache_projects_explore_page diff --git a/config/charting/demographic.yml b/config/charting/demographic.yml index 93f389597..ab2a56f54 100644 --- a/config/charting/demographic.yml +++ b/config/charting/demographic.yml @@ -20,16 +20,28 @@ plotOptions: pie: allowPointSelect: true cursor: 'pointer' - innerSize: 80 - slicedOffset: 10 - borderColor: '#fff' - borderWidth: 0 + showInLegend: true + innerSize: '55%' + slicedOffset: 0 + borderWidth: 2 dataLabels: enabled: true minSize: 1 color: '#000000' - connectorColor: '#000000' - format: '{point.name}: {point.y} %' + connectorColor: '#94a3b8' + format: '{point.name}: {point.y}%' +legend: + enabled: true + align: 'center' + verticalAlign: 'bottom' + layout: 'horizontal' + symbolRadius: 6 + symbolHeight: 12 + symbolWidth: 12 + labelFormat: '{name} ({y}%)' + itemStyle: + fontWeight: 'normal' + fontSize: '12px' credits: enabled: false series: [{type: 'pie'}] From a9eff99b3b5d575693d6279830918a10608c91cf Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Tue, 17 Mar 2026 19:46:36 +0530 Subject: [PATCH 19/53] testcase fix --- .../stylesheets/project_show_redesign.sass | 18 +++++++++++------- app/controllers/explore_controller.rb | 6 +----- app/decorators/project/activity_level_index.rb | 4 ++-- app/decorators/project/demographic_chart.rb | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass index 8509ed849..1a807b036 100644 --- a/app/assets/stylesheets/project_show_redesign.sass +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -2354,6 +2354,7 @@ html.dark #project_container .project-show-redesign .analysis-grid .summary-sect gap: 16px !important margin-top: 8px !important + .language-pie-container width: 90px !important height: 90px !important @@ -2364,17 +2365,18 @@ html.dark #project_container .project-show-redesign .analysis-grid .summary-sect height: 90px !important display: block !important + + .language-legend flex: 1 1 auto !important min-width: 0 !important display: flex !important - flex-direction: column !important - gap: 6px !important + flex-direction: row !important + gap: 12px !important .language-item display: flex !important align-items: center !important - gap: 8px !important min-width: 0 !important .language-name @@ -2466,11 +2468,13 @@ html.dark #project_container .project-show-redesign .analysis-grid .analysis-car height: 100% display: block + .language-legend - flex: 1 - display: flex - flex-direction: column - gap: 6px + flex: 1 1 auto !important + min-width: 0 !important + display: flex !important + flex-direction: row !important + gap: 12px !important .language-item display: flex diff --git a/app/controllers/explore_controller.rb b/app/controllers/explore_controller.rb index a9ee6dfd6..687b3f2f5 100644 --- a/app/controllers/explore_controller.rb +++ b/app/controllers/explore_controller.rb @@ -12,11 +12,7 @@ def index end def demographic_chart - data = Project::DemographicChart.data - render json: data - rescue StandardError => e - Rails.logger.error("DemographicChart failed: #{e.message}") - render json: nil, status: :ok + render json: Project::DemographicChart.data end def orgs diff --git a/app/decorators/project/activity_level_index.rb b/app/decorators/project/activity_level_index.rb index a6f4ddfa0..c22d0703e 100644 --- a/app/decorators/project/activity_level_index.rb +++ b/app/decorators/project/activity_level_index.rb @@ -30,10 +30,10 @@ def percentage end def sliced - false + @level_index == INACTIVE_INDEX end def selected - false + name == ACTIVITY_LEVEL_INDEX[INACTIVE_INDEX] end end diff --git a/app/decorators/project/demographic_chart.rb b/app/decorators/project/demographic_chart.rb index da7fdb9f2..d240a1e56 100644 --- a/app/decorators/project/demographic_chart.rb +++ b/app/decorators/project/demographic_chart.rb @@ -8,7 +8,7 @@ class << self def data default_options = DEMOGRAPHIC_CHART_DEFAULTS.deep_dup default_options['plotOptions']['pie']['startAngle'] = angle - default_options['series'][0]['data'] = activity_level_data + default_options['series'] << { 'data' => activity_level_data } default_options end @@ -17,8 +17,8 @@ def data def activity_level_data level_data = Project::ActivityLevelIndex::ACTIVITY_LEVEL_INDEX.map do |level, _| count = count_by_activity_level[level].to_i - Project::ActivityLevelIndex.new(level, count, [total_count, 1].max).demographic_chart_data - end + Project::ActivityLevelIndex.new(level, count, [total_count, 1].max).demographic_chart_data if count.positive? + end.compact level_data.sort_by { |d| SORT_ORDER.index(d[:name]) } end @@ -33,7 +33,7 @@ def total_count end def angle - 0 + 90.0 - (360 * (count_by_activity_level[INACTIVE_INDEX].to_f / total_count) / 2) end end end From 6cafc4fa61cb839128badcf00edc76694caafaec Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Wed, 18 Mar 2026 12:41:44 +0530 Subject: [PATCH 20/53] OTWO-7596 logo layout fix --- app/assets/stylesheets/projects.sass | 30 +++++++++++++++---- .../home/_compact_project_card.html.haml | 4 ++- app/views/projects/_project_index.html.haml | 7 +++-- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index da06c6e7b..e2c715a1e 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -726,7 +726,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) text-decoration: underline html.dark & - color: $dark-primary-color + color: $dark-primary-color !important .analyzed-date font-style: italic @@ -761,17 +761,37 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) flex-shrink: 0 width: 48px height: 48px - background-color: $bg-color border-radius: 12px - border: 1px solid $border-color display: flex align-items: center justify-content: center font-size: 24px + overflow: hidden + background-color: $bg-color + border: 1px solid $border-color box-shadow: 0 1px 2px rgba(0,0,0,0.05) html.dark & background-color: $dark-bg-color border-color: $dark-border-color + &.has-logo + background-color: transparent + border: none + box-shadow: none + html.dark & + background-color: transparent + border: none + img + width: 100% + height: 100% + object-fit: contain + .project-icon-letter + font-size: 22px + font-weight: 700 + color: $primary-color + text-transform: uppercase + user-select: none + html.dark & + color: $dark-primary-color @media (min-width: 640px) width: 56px @@ -813,7 +833,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) &:hover text-decoration: underline html.dark & - color: $dark-primary-color + color: $dark-primary-color !important .licenses .license-label @@ -850,7 +870,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) background-color: rgba(255, 185, 26, 0.3) html.dark & background-color: rgba(255, 185, 26, 0.15) - color: $dark-primary-color + color: $dark-primary-color !important border-color: rgba(255, 185, 26, 0.2) &:hover background-color: rgba(255, 185, 26, 0.25) diff --git a/app/views/home/_compact_project_card.html.haml b/app/views/home/_compact_project_card.html.haml index d67b36c4e..a7370974f 100644 --- a/app/views/home/_compact_project_card.html.haml +++ b/app/views/home/_compact_project_card.html.haml @@ -9,7 +9,9 @@ - if is_account = image_tag(avatar_img_path(project), class: 'account-avatar') - elsif project.logo.present? - = image_tag(project.logo.attachment.url(:small), alt: project.name) + = image_tag(project.logo.attachment.url(:small), alt: project.name, onerror: "this.style.display='none'; this.nextElementSibling.style.display='flex';") + .project-icon-placeholder{ style: 'display:none' } + = project.name[0].upcase - else .project-icon-placeholder = project.name[0].upcase diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index 3a3138e86..72a1e1700 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -35,11 +35,12 @@ .project-main / Icon and Description .project-icon-desc - .project-icon + .project-icon{ class: project.logo.present? ? 'has-logo' : nil } - if project.logo.present? - = image_tag project.logo.attachment.url(:med), alt: project.name + = image_tag project.logo.attachment.url(:med), alt: project.name, onerror: "this.style.display='none'; this.nextElementSibling.style.display='flex'; this.parentElement.classList.remove('has-logo');" + %span.project-icon-letter{ style: 'display:none' }= project.name.first.upcase - else - %i.fa.fa-cube + %span.project-icon-letter= project.name.first.upcase .project-description - if project.description && project.description.strip.size > 220 From a657875f1535adabbe7463ecd3eb19b8edf06b55 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 21/53] OTWO-7546 added layout for home page assets precompile issue and test cases fix Css fix for after sign in user --- app/assets/javascripts/dropdown_handler.js | 71 ++ app/assets/javascripts/mobile_menu.js | 86 ++ app/assets/javascripts/rotating_stats.js | 29 + app/assets/javascripts/theme_toggle.js | 75 ++ app/assets/stylesheets/dark_theme.sass | 150 +++ app/assets/stylesheets/footer.sass | 241 +++-- app/assets/stylesheets/home.sass | 995 +++++++++++++++--- app/assets/stylesheets/oh-styles.sass | 17 +- app/assets/stylesheets/page.sass | 888 +++++++++++++--- app/views/home/_join_now_home.html.haml | 44 +- app/views/home/_top_contributors.html.haml | 33 + app/views/home/_user_journeys.html.haml | 272 +++++ app/views/home/_whats_new.html.haml | 11 +- app/views/home/index.html.haml | 102 +- app/views/layouts/partials/_footer.html.haml | 81 +- app/views/layouts/partials/_mast.html.haml | 131 ++- config/initializers/assets.rb | 2 +- config/locales/home.en.yml | 11 + test/controllers/home_controller_test.rb | 8 +- test/controllers/licenses_controller_test.rb | 2 +- .../organizations_controller_test.rb | 2 +- test/controllers/projects_controller_test.rb | 2 +- 22 files changed, 2764 insertions(+), 489 deletions(-) create mode 100644 app/assets/javascripts/dropdown_handler.js create mode 100644 app/assets/javascripts/mobile_menu.js create mode 100644 app/assets/javascripts/rotating_stats.js create mode 100644 app/assets/javascripts/theme_toggle.js create mode 100644 app/assets/stylesheets/dark_theme.sass create mode 100644 app/views/home/_top_contributors.html.haml create mode 100644 app/views/home/_user_journeys.html.haml diff --git a/app/assets/javascripts/dropdown_handler.js b/app/assets/javascripts/dropdown_handler.js new file mode 100644 index 000000000..f9aea4967 --- /dev/null +++ b/app/assets/javascripts/dropdown_handler.js @@ -0,0 +1,71 @@ +// Dropdown menu handler for logged user menu +(function($) { + 'use strict'; + + function initDropdown() { + var $dropdown = $('#logged_user_menu'); + + if ($dropdown.length === 0) { + return; // Dropdown not found (user not logged in) + } + + var $toggle = $dropdown.find('.dropdown-toggle'); + var $menu = $dropdown.find('.dropdown-menu'); + + // Remove any existing event handlers + $toggle.off('click.userDropdown'); + $(document).off('click.userDropdown keydown.userDropdown'); + $menu.find('a').off('click.userDropdown'); + + // Toggle dropdown on click + $toggle.on('click.userDropdown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var wasOpen = $dropdown.hasClass('open'); + + // Close all dropdowns first + $('.dropdown').removeClass('open'); + + // Toggle this dropdown + if (!wasOpen) { + $dropdown.addClass('open'); + } + + return false; + }); + + // Close dropdown when clicking outside + $(document).on('click.userDropdown', function(e) { + var $target = $(e.target); + if (!$dropdown.is($target) && $dropdown.has($target).length === 0) { + $dropdown.removeClass('open'); + } + }); + + // Close dropdown when clicking a menu item + $menu.find('a').on('click.userDropdown', function() { + setTimeout(function() { + $dropdown.removeClass('open'); + }, 150); + }); + + // Close dropdown on ESC key + $(document).on('keydown.userDropdown', function(e) { + if (e.keyCode === 27) { + $dropdown.removeClass('open'); + } + }); + } + + // Initialize when DOM is ready + $(document).ready(function() { + initDropdown(); + }); + + // Re-initialize on Turbolinks page change + $(document).on('page:change', function() { + initDropdown(); + }); + +})(jQuery); diff --git a/app/assets/javascripts/mobile_menu.js b/app/assets/javascripts/mobile_menu.js new file mode 100644 index 000000000..d57090864 --- /dev/null +++ b/app/assets/javascripts/mobile_menu.js @@ -0,0 +1,86 @@ +// Mobile Menu Toggle Functionality +var MobileMenu = { + init: function() { + console.log('MobileMenu: Initializing...'); + this.bindEvents(); + }, + + toggleMenu: function() { + console.log('MobileMenu: Toggle clicked!'); + var mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenu) { + if (mobileMenu.classList.contains('show')) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } else { + mobileMenu.classList.add('show'); + console.log('MobileMenu: Menu opened'); + } + } else { + console.log('MobileMenu: WARNING - Mobile menu element not found!'); + } + }, + + closeMenu: function() { + var mobileMenu = document.getElementById('mobile-menu'); + if (mobileMenu) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } + }, + + bindEvents: function() { + var self = this; + var toggleBtn = document.getElementById('mobile-menu-toggle'); + + console.log('MobileMenu: Toggle button found?', !!toggleBtn); + + if (toggleBtn) { + toggleBtn.onclick = function(e) { + e.preventDefault(); + self.toggleMenu(); + return false; + }; + console.log('MobileMenu: Click handler attached'); + } else { + console.log('MobileMenu: WARNING - Mobile menu toggle button not found!'); + } + + // Close menu when clicking on a link + var mobileMenuLinks = document.querySelectorAll('.mobile-menu-items a, .mobile-menu-user a, .mobile-menu-signin a'); + console.log('MobileMenu: Found', mobileMenuLinks.length, 'menu links'); + + for (var i = 0; i < mobileMenuLinks.length; i++) { + mobileMenuLinks[i].onclick = function() { + self.closeMenu(); + }; + } + + // Close menu when window is resized to desktop size + window.addEventListener('resize', function() { + if (window.innerWidth >= 1024) { + self.closeMenu(); + } + }); + } +}; + +// Initialize on DOM ready +if (typeof $ !== 'undefined') { + $(document).on('page:change', function() { + console.log('MobileMenu: page:change event fired'); + MobileMenu.init(); + }); + + $(document).ready(function() { + console.log('MobileMenu: document.ready event fired'); + MobileMenu.init(); + }); +} else { + // Fallback if jQuery is not available + document.addEventListener('DOMContentLoaded', function() { + console.log('MobileMenu: DOMContentLoaded event fired'); + MobileMenu.init(); + }); +} diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js new file mode 100644 index 000000000..39a368b7b --- /dev/null +++ b/app/assets/javascripts/rotating_stats.js @@ -0,0 +1,29 @@ +// Rotating Stats Animation for Homepage +document.addEventListener('DOMContentLoaded', function() { + var globalStats = document.getElementById('global_statistics'); + + if (!globalStats) return; + + var statElements = globalStats.querySelectorAll('p'); + + if (statElements.length === 0) return; + + var currentIndex = 0; + + // Show first stat initially + statElements[0].classList.remove('hide'); + + // Rotate stats every 2 seconds + setInterval(function() { + // Hide current stat + statElements[currentIndex].classList.add('hide'); + + // Move to next stat + currentIndex = (currentIndex + 1) % statElements.length; + + // Show next stat after a brief delay + setTimeout(function() { + statElements[currentIndex].classList.remove('hide'); + }, 300); + }, 2000); +}); diff --git a/app/assets/javascripts/theme_toggle.js b/app/assets/javascripts/theme_toggle.js new file mode 100644 index 000000000..44c98daaf --- /dev/null +++ b/app/assets/javascripts/theme_toggle.js @@ -0,0 +1,75 @@ +// Theme Toggle Functionality +var ThemeToggle = { + init: function() { + console.log('ThemeToggle: Initializing...'); + var savedTheme = this.getSavedTheme(); + console.log('ThemeToggle: Saved theme is', savedTheme); + this.applyTheme(savedTheme); + this.bindEvents(); + }, + + getSavedTheme: function() { + try { + return localStorage.getItem('theme') || 'light'; + } catch (e) { + return 'light'; + } + }, + + applyTheme: function(theme) { + var html = document.documentElement; + var moonIcon = document.getElementById('theme-icon-moon'); + var sunIcon = document.getElementById('theme-icon-sun'); + + if (theme === 'dark') { + html.classList.add('dark'); + if (moonIcon) moonIcon.classList.add('hidden'); + if (sunIcon) sunIcon.classList.remove('hidden'); + } else { + html.classList.remove('dark'); + if (moonIcon) moonIcon.classList.remove('hidden'); + if (sunIcon) sunIcon.classList.add('hidden'); + } + + try { + localStorage.setItem('theme', theme); + } catch (e) { + console.log('Could not save theme preference'); + } + }, + + toggleTheme: function() { + var currentTheme = this.getSavedTheme(); + var newTheme = currentTheme === 'light' ? 'dark' : 'light'; + this.applyTheme(newTheme); + }, + + bindEvents: function() { + var self = this; + var themeToggleBtn = document.getElementById('theme-toggle'); + + console.log('ThemeToggle: Theme toggle button found?', !!themeToggleBtn); + + if (themeToggleBtn) { + themeToggleBtn.onclick = function(e) { + e.preventDefault(); + console.log('ThemeToggle: Toggle clicked!'); + self.toggleTheme(); + return false; + }; + console.log('ThemeToggle: Click handler attached'); + } else { + console.log('ThemeToggle: WARNING - Theme toggle button not found!'); + } + } +}; + +// Initialize on DOM ready +$(document).on('page:change', function() { + ThemeToggle.init(); +}); + +// Also initialize immediately if DOM is already loaded +$(document).ready(function() { + ThemeToggle.init(); +}); diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass new file mode 100644 index 000000000..e240d718d --- /dev/null +++ b/app/assets/stylesheets/dark_theme.sass @@ -0,0 +1,150 @@ +// Dark Theme Styles +// Applied when .dark class is added to html element + +html.dark + // Body and background colors + body + background-color: #0f172a !important + color: #e2e8f0 !important + + // Page container + #page, .container#page + background-color: #0f172a !important + + // Header stays with purple gradient (no change needed) + // Footer stays with purple gradient (no change needed) + + // Content areas + #page-contents, #page_contents + background-color: #1e293b + color: #e2e8f0 + + // Cards and wells + .well + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Tables + table + background-color: #1e293b + color: #e2e8f0 + td, th + border-color: #334155 !important + color: #e2e8f0 !important + + // Links + a + color: #60a5fa !important + &:hover + color: #93c5fd !important + + // Buttons (keep primary yellow button, update others) + .btn + &:not(.btn-primary):not(.btn-success) + background-color: #334155 !important + color: #e2e8f0 !important + &:hover + background-color: #475569 !important + + // Forms + input, textarea, select + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + &::placeholder + color: #94a3b8 !important + + // Billboard/Showcase + .billboard + background-color: #1e293b !important + color: #e2e8f0 !important + + .showcase + background-color: #0f172a !important + + // Project container + #project_container + background-color: #0f172a !important + color: #e2e8f0 !important + + // Headings + h1, h2, h3, h4, h5, h6 + color: #f1f5f9 !important + + // Alerts + .alert + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Dropdown menus + .dropdown-menu + background-color: #1e293b !important + border-color: #334155 !important + li a + color: #e2e8f0 !important + &:hover + background-color: #334155 !important + + // Home page sections + .top_ten, .top_ten.middle, .top_ten.last + background-color: #1e293b !important + color: #e2e8f0 !important + + .home_page_row .col-md-4 + background-color: #1e293b !important + + // Navigation menu - keep white text on purple gradient + .navbar, #navbar-inner + .new_main_menu li a + color: white !important + + // Search inputs + .for_search_all_code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Badges + .badge + background-color: #334155 !important + color: #e2e8f0 !important + + // Panels + .panel + background-color: #1e293b !important + border-color: #334155 !important + .panel-heading + background-color: #334155 !important + color: #e2e8f0 !important + .panel-body + background-color: #1e293b !important + color: #e2e8f0 !important + + // Code blocks + pre, code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Borders + .mezzo + border-top-color: #334155 !important + + .right_border + border-right-color: #334155 !important + + // Text colors + .signature_color + color: #60a5fa !important + + // Keep error colors visible + .error + color: #ef4444 !important + + .bad + color: #f87171 !important + + .good + color: #4ade80 !important diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index dccca4602..1baf71240 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -1,81 +1,186 @@ footer - @include footer-colors + // Figma design: purple gradient background + position: relative + overflow: hidden + background: linear-gradient(to right, #1D0631, #5A2A82) !important + color: white !important + margin: 0 + padding: 0 + width: 100% + max-width: 100% + .footer-container + position: relative + max-width: 1280px + margin: 0 auto + padding: 48px 32px 24px 32px - margin: 20px 0 0 0 - min-height: 250px - padding-top: 10px - width: 960px - - @media (min-width: 320px) and (max-width: 480px) - width: auto - min-height: 194px !important - padding-top: 0px !important - margin: 0px 0 0 0 !important + // Mobile: < 640px + @media (max-width: 639px) + .footer-container + padding: 32px 16px 16px 16px + .footer-grid + gap: 32px + margin-bottom: 24px .footer-left - margin-left: 5px !important - margin-top: 20px !important - min-height: 150px !important - .footer-mid - line-height: 15px - margin-left: 30px !important - font-size: 8px !important - width: 32% !important - .footer-right - font-size: 8px !important - padding-right: 2px !important - width: 31% !important + .logo_img + height: 28px + .footer-mid, .footer-right + h3 + font-size: 10px + margin-bottom: 12px + a + font-size: 10px + li + margin-bottom: 8px .footer-bottom - margin-left: 0px !important - margin-top: 0px !important - @media (min-width: 540px) and (max-width: 1024px) - width: auto + .footer-bottom-content + gap: 8px + .copyright + font-size: 9px + text-align: center + .follow-us + span + font-size: 10px + a + width: 28px + height: 28px + i + font-size: 14px + + // Tablet: 640px - 767px + @media (min-width: 640px) and (max-width: 767px) + .footer-container + padding: 40px 24px 20px 24px + .footer-grid + gap: 40px + margin-bottom: 28px + + // Tablet landscape: 768px - 1023px + @media (min-width: 768px) and (max-width: 1023px) + .footer-container + padding: 44px 32px 22px 32px + .footer-grid + gap: 36px + + .footer-grid + display: grid + grid-template-columns: 1fr + gap: 48px + margin-bottom: 32px + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 48px .footer-left - float: left - margin-top: 20px - margin-left: 30px - width: 15% + display: flex + align-items: flex-start + justify-content: center + @media (min-width: 768px) + justify-content: flex-start a .logo_img - width: 100% - height: 100% - - .footer-mid - float: left - margin-top: 20px - margin-left: 120px - margin-right: 40px - width: 20% - font-size: 12px - a - @include footer-colors - padding: 4px 4px + width: auto + height: 32px + object-fit: contain + @media (min-width: 640px) + height: 40px - .footer-right - float: left - margin-top: 20px - margin-bottom: 20px - width: 30% - font-size: 12px - padding-right: 60px - text-align: left + .footer-mid, .footer-right + h3 + font-size: 11px + font-weight: 600 + color: white !important + margin-bottom: 16px + text-transform: uppercase + letter-spacing: 0.05em + @media (min-width: 640px) + font-size: 13px + margin-bottom: 12px + @media (min-width: 768px) + margin-bottom: 16px + ul + list-style: none + margin: 0 + padding: 0 + li + margin-bottom: 10px + @media (min-width: 640px) + margin-bottom: 8px + @media (min-width: 768px) + margin-bottom: 10px + p + margin: 0 0 10px 0 + @media (min-width: 640px) + margin: 0 0 8px 0 + @media (min-width: 768px) + margin: 0 0 10px 0 a - @include footer-colors - padding: 4px 6px + color: #d1d5db !important + text-decoration: none + font-size: 11px + font-weight: 400 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + &:hover + color: white !important .footer-bottom - @include footer-copyright-colors - - float: left - margin-top: 10px - margin-left: 30px - margin-bottom: 2em - bottom: 0 - a - @include footer-copyright-colors + border-top: 1px solid rgba(255, 255, 255, 0.1) + padding: 12px 0 + @media (min-width: 640px) + padding: 16px 0 + @media (min-width: 768px) + padding: 24px 0 + .footer-bottom-content + display: flex + flex-direction: column + align-items: center + justify-content: space-between + gap: 12px + @media (min-width: 640px) + flex-direction: row + .copyright + color: #9ca3af !important + font-size: 10px + order: 2 + @media (min-width: 640px) + font-size: 12px + order: 1 + .follow-us + display: flex + align-items: center + gap: 8px + order: 1 + @media (min-width: 640px) + order: 2 + span + color: #d1d5db !important + font-size: 12px + font-weight: 500 + @media (min-width: 640px) + font-size: 14px + a + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + transition: background-color 0.3s ease + &:hover + background-color: rgba(255, 255, 255, 0.2) + i + color: #d1d5db + font-size: 16px + transition: color 0.3s ease + &:hover i + color: white footer.fluid_footer width: auto + background: linear-gradient(to right, #1D0631, #5A2A82) !important .footer-left width: 12% ul @@ -83,16 +188,22 @@ footer.fluid_footer a padding: 1rem 2rem font-weight: bold + color: rgba(209, 213, 219, 1) !important + &:hover + color: white !important .footer-mid width: 25% text-align: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-right width: 40% float: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-bottom float: left margin-botton: 2em !important + color: rgba(156, 163, 175, 1) !important diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4c18ddd8c..0422db618 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,153 +1,161 @@ -.showcase - padding-top: 20px - padding-bottom: 30px - width: 980px - margin-left: -15px - padding-left: 5px - padding-right: 5px -@media only all and (min-width: 320px) and (max-width: 480px) - .showcase - width: auto - margin-left: 0px - .billboard - height: 185px !important - -.billboard - @include billboard-background-color - color: white - height: 160px - padding-top: 30px - padding-bottom: 30px +// Hero Section - Updated to match Figma exactly +.hero_section + padding-top: 60px + padding-bottom: 40px + padding-left: 16px + padding-right: 16px + background: #fff !important + transition: background 0.3s ease + @media (min-width: 640px) + padding-top: 72px + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-top: 88px + padding-left: 32px + padding-right: 32px + // Dark mode support + html.dark & + background: #1D0631 !important + +.hero_container + max-width: 80rem + margin: 0 auto + +// Hero Header - Figma Specs +.hero_header text-align: center + margin-bottom: 24px + +.hero_title + font-size: 1.875rem !important + font-weight: 700 + color: #5A2A82 + margin-bottom: 8px + line-height: 2.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 2.25rem !important + line-height: 2.5rem !important + @media (min-width: 768px) + font-size: 3rem !important + line-height: 1 !important + // Dark mode support + html.dark & + color: #ffffff + +.hero_subtitle + font-size: 0.875rem !important + color: #6b7280 + max-width: 42rem + margin: 0 auto + line-height: 1.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 1rem !important + line-height: 1.5rem !important + // Dark mode support + html.dark & + color: #9ca3af + +// Search Container - Figma Specs +.search_container + max-width: 48rem + margin: 0 auto 20px + +.search_form + margin-bottom: 0 + position: relative + +.search_input_wrapper + position: relative + +.search_icon + position: absolute + top: 50% + transform: translateY(-50%) + left: 8px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 1 + transition: color 0.3s ease + @media (min-width: 640px) + left: 24px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + +.search_input width: 100% - z-index: 2 - .billboard_search - // margin-top: 80px - .discover - padding: 0 - .discover_msg - font-size: 2.1em !important - font-weight: 400!important - margin-top: -10px - margin-bottom: 24px - h1 - line-height: 1.2em !important -@media only all and (min-width: 320px) and (max-width: 480px) - .billboard - .discover_msg - font-size: 13px !important - #global_statistics - p - font-size: 10px !important -@media (min-width: 540px) and (max-width: 1024px) - .showcase - width: auto - margin-left: 0px -.global_stats - margin-top: 25px -#icon_text - font-size: 1.6em - color: #646E81 - margin-left: -27px - cursor: pointer + padding: 10px 12px 10px 32px + font-size: 11px !important + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #fff + color: #111827 + font-family: inherit + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.2s ease + &::placeholder + color: #9ca3af + &:hover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + &:focus + border-color: #0E4B7A + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + &::placeholder + color: #d1d5db + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff + border-color: #374151 + &::placeholder + color: #6b7280 + &:focus + border-color: #2E8B9E + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// Rotating Stats - Figma Animation +.rotating_stats + text-align: center + margin-top: 12px + height: 20px #global_statistics - padding-top: 1px p - margin-top: -20px - text-align: center - font-size: 14px -.join_now - @include join-now-header-colors - padding-bottom: 5px - padding-left: 12px -.join_now_home - padding-top: 10px - font-size: 1.4em - p - font-size: 14px !important - .join_now_color - @include join-now-section-colors - li - font-size: 0.5em !important - height: 30px - width: 30px -.home_page_row - .whats_new - margin-right: -27px !important - .col-md-8 - h2 - font-weight: 500 !important - margin-right: -40px !important - margin-left: 30px !important - text-indent: -10px !important - .col-md-4 - @include site-well-color - padding-bottom: 20px - h2 - font-weight: 500 !important - margin-left: 5px !important - text-indent: -10px !important - -@media only all and (min-width: 320px) and (max-width: 480px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important - .for_search_all_code - width: auto !important - .join_now_home - padding-top: 5px - font-size: 12px - li - height: 10px - width: 10px -@media (min-width: 540px) and (max-width: 1024px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important -.btn.btn_join_now - @include join-now-button-colors - font-family: 'Roboto' !important - font-size: 22px !important - font-weight: bold !important - margin-left: 60px !important - width: 161px !important -::-webkit-input-placeholder - @include site-placeholder-color -\:-moz-placeholder - @include site-placeholder-color - opacity: 1 -::-moz-placeholder - @include site-placeholder-color - opacity: 1 -\:-ms-input-placeholder - @include site-placeholder-color - -input:focus::-webkit-input-placeholder - color: transparent -input:focus::-moz-placeholder - color: transparent -input:focus::-ms-input-placeholder - color: transparent -@media only all and (min-width: 320px) and (max-width: 480px) - .btn.btn_join_now - margin-left: 0px !important - width: 135px !important - font-size: 15px !important + font-size: 12px !important + font-weight: 500 + background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) + -webkit-background-clip: text + -webkit-text-fill-color: transparent + background-clip: text + margin: 0 + line-height: 1.5 + transition: opacity 0.3s ease + &.hide + opacity: 0 + display: block + &:not(.hide) + opacity: 1 + display: block + // Dark mode support + html.dark & + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) +// Old join_now and home_page_row styles removed - using new card-based design +// Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px - margin-left: -14px + margin-left: 0 + padding: 0 20px + width: 100% .top_ten_main padding-left: 40px !important padding-bottom: 6px !important @@ -191,30 +199,11 @@ input:focus::-ms-input-placeholder a.top_ten_icon p opacity: .75 background-color: #999 -.for_search_all_code - @include site-placeholder-color - height: 23px !important - // margin-top: -108px - width: 413px +// Old .for_search_all_code removed - using new .search_input instead .top_ten_link a border: 0 -.icon_search - font-size: 14px - font-weight: 300 - margin-top: -37px - margin-left: -71px - text-align: center -.wh_new - @include whats-new-header-colors - width: 100% - padding-bottom: 5px -.common - padding-left: 6px - padding-bottom: 3px - font-size: 1.4em - font-weight: normal - color: #000000 +// Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors .most_active_projects @@ -239,4 +228,684 @@ button #no-background background-color: white !important +// Feature Cards Section - Figma Specs +.features_section + max-width: 64rem + margin: 0 auto 32px + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 20px + +.feature_card + text-align: center + margin-bottom: 16px + @media (min-width: 768px) + margin-bottom: 0 + +.feature_icon + width: 40px + height: 40px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin: 0 auto 12px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.feature_icon_yellow + background: linear-gradient(135deg, #facc15 0%, #f97316 100%) + +.feature_icon_blue + background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) + +.feature_icon_green + background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%) + +.feature_title + font-size: 12px !important + font-weight: 600 + color: #111827 + margin-bottom: 4px + line-height: 1.3 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #ffffff + +.feature_description + font-size: 10px !important + color: #6b7280 + line-height: 1.5 + padding: 0 8px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af + +// Quick Stats Bar - Figma Specs (grid-cols-2 md:flex) +.quick_stats_bar + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + max-width: 48rem + margin: 0 auto + padding: 16px + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) + backdrop-filter: blur(4px) + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.6) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + @media (min-width: 768px) + display: flex + align-items: center + justify-content: center + gap: 24px + padding: 12px 24px + // Dark mode support - Yellow background with purple text + html.dark & + background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) + border-color: #ffb91a + +.stat_item + text-align: center + +.stat_value + font-size: 16px !important + font-weight: 700 + color: #5A2A82 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_label + font-size: 8px !important + color: #6b7280 + text-transform: uppercase + font-weight: 500 + letter-spacing: 0.05em + margin-top: 2px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 9px !important + @media (min-width: 768px) + font-size: 10px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_divider + display: none + transition: background 0.3s ease + @media (min-width: 768px) + display: block + width: 1px + height: 32px + background: #d1d5db + opacity: 0.3 + // Dark mode - Purple divider on yellow background + html.dark & + @media (min-width: 768px) + background: #5A2A82 + opacity: 0.3 + +// Content Section +.content_section + padding: 32px 0 + background: white !important + transition: background 0.3s ease + // Dark mode support + html.dark & + background: #1D0631 !important + +.contributors_column, .whats_new_column + @media (max-width: 768px) + margin-bottom: 24px + +.whats_new_join_stack + display: flex + flex-direction: column + gap: 24px + +// Top Contributors Card +.top_contributors_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + > div + background: white + border-radius: 14px + height: 100% + +.card_header + display: flex + align-items: flex-start + gap: 16px + padding: 24px + padding-bottom: 16px + background: white + +.header_icon + width: 48px + height: 48px + background: rgba(90, 42, 130, 0.1) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: #5A2A82 + +.header_content + flex: 1 + +.card_title + font-size: 18px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.card_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + line-height: 1.5 + +.contributors_list + padding: 16px + flex: 1 + overflow-y: auto + +.contributor_item + background: linear-gradient(90deg, #f9fafb 0%, #f3f4f6 100%) + border-radius: 12px + padding: 12px + border: 1px solid #e5e7eb + cursor: pointer + transition: all 0.3s + margin-bottom: 8px + display: flex + align-items: center + gap: 12px + &:hover + background: linear-gradient(90deg, #f5f3ff 0%, #ede9fe 100%) + border-color: #5A2A82 + +.contributor_avatar + flex-shrink: 0 + .avatar_image + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + border: 2px solid #9ca3af + +.contributor_info + flex: 1 + min-width: 0 + +.contributor_name_rank + display: flex + align-items: center + gap: 8px + margin-bottom: 2px + +.contributor_name + font-size: 14px + font-weight: 600 + color: #5A2A82 + margin: 0 + .contributor_link + color: inherit + text-decoration: none + &:hover + text-decoration: underline + +.contributor_rank + font-size: 9px + color: rgba(90, 42, 130, 0.6) + text-transform: uppercase + letter-spacing: 0.05em + flex-shrink: 0 + +.contributor_stats + display: flex + align-items: center + gap: 12px + font-size: 10px + color: rgba(90, 42, 130, 0.7) + .stat_item + display: flex + align-items: center + gap: 4px + i + font-size: 10px + +.contributor_arrow + flex-shrink: 0 + color: rgba(90, 42, 130, 0.4) + transition: color 0.3s + i + font-size: 16px + +.contributor_item:hover .contributor_arrow + color: #5A2A82 + +.view_all_link + padding: 16px + .btn_view_all + width: 100% + display: flex + align-items: center + justify-center + gap: 8px + font-size: 14px + font-weight: 600 + color: #5A2A82 + transition: color 0.3s + padding: 12px + border-top: 1px solid rgba(90, 42, 130, 0.2) + text-decoration: none + i + font-size: 16px + &:hover + color: #1D0631 + +// What's New Card +.whats_new_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.card_header_section + padding: 24px + padding-bottom: 16px + background: white + +.whats_new_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.whats_new_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + +.featured_image_section + padding: 0 24px 24px + +.featured_image_link + display: block + position: relative + border-radius: 12px + overflow: hidden + transition: transform 0.3s + &:hover + transform: scale(1.02) + .featured_image + transform: scale(1.05) + +.featured_image + width: 100% + height: auto + transition: transform 0.3s + +// Join Now Card +.join_now_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.join_header + display: flex + align-items: center + gap: 12px + padding: 24px + padding-bottom: 16px + +.join_icon + width: 48px + height: 48px + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: white + +.join_header_text + flex: 1 + +.join_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 2px + +.join_subtitle + font-size: 12px + color: #6b7280 + +.benefits_list + padding: 0 24px + margin-bottom: 24px + +.benefit_item + display: flex + align-items: flex-start + gap: 12px + margin-bottom: 12px + +.benefit_icon + width: 32px + height: 32px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + margin-top: 2px + +.benefit_icon_award + background: rgba(90, 42, 130, 0.1) + i + font-size: 16px + color: #5A2A82 + +.benefit_icon_trending + background: rgba(29, 6, 49, 0.1) + i + font-size: 16px + color: #1D0631 + +.benefit_content + flex: 1 + +.benefit_title + font-size: 13px + font-weight: 600 + color: #111827 + margin-bottom: 2px + +.benefit_description + font-size: 11px + color: #6b7280 + line-height: 1.5 + +.join_cta + padding: 0 24px 24px + +.btn_join_cta + width: 100% + padding: 12px 24px + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) + color: white + border-radius: 8px + font-weight: 600 + transition: all 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + display: flex + align-items: center + justify-content: center + gap: 8px + font-size: 16px + text-decoration: none + border: none + cursor: pointer + &:hover + background: linear-gradient(90deg, #1D0631dd 0%, #5A2A82dd 100%) + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) + color: white + text-decoration: none + i + font-size: 16px + +// User Journeys Section +.user_journeys_section + background: #1D0631 + padding: 64px 0 + @media (max-width: 768px) + padding: 48px 0 + +.journeys_header + text-align: center + margin-bottom: 48px + +.journeys_title + font-size: 30px + font-weight: 700 + color: white + margin-bottom: 12px + @media (max-width: 768px) + font-size: 24px + +.journeys_subtitle + font-size: 18px + color: #d1d5db + max-width: 768px + margin: 0 auto + @media (max-width: 768px) + font-size: 16px + +// Mobile View - Collapsible +.journeys_mobile + display: block + @media (min-width: 1024px) + display: none + +.journey_card + position: relative + padding: 3px + border-radius: 12px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 16px + transition: all 0.3s + +.journey_card_header + width: 100% + display: flex + align-items: center + gap: 16px + padding: 16px + text-align: left + transition: background 0.3s + background: #1D0631 + border: none + cursor: pointer + border-radius: 10px + &:hover + background: rgba(90, 42, 130, 0.2) + +.journey_header_icon + width: 40px + height: 40px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 20px + color: white + +.journey_header_title + flex: 1 + font-size: 14px + font-weight: 600 + color: white + +.journey_toggle_icon + flex-shrink: 0 + i + font-size: 20px + color: white + +.journey_card_content + display: none + padding: 0 16px 16px + background: #1D0631 + border-radius: 0 0 10px 10px + +.journey_card.expanded .journey_card_content + display: block + animation: slideDown 0.3s ease-out + +@keyframes slideDown + from + opacity: 0 + transform: translateY(-10px) + to + opacity: 1 + transform: translateY(0) + +.journey_description + font-size: 14px + color: #d1d5db + line-height: 1.5 + margin-bottom: 16px + +.journey_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +.steps_title + font-size: 12px + font-weight: 600 + color: #e5e7eb + text-transform: uppercase + letter-spacing: 0.05em + margin-bottom: 8px + +.steps_list + list-style: none + padding: 0 + margin: 0 + li + display: flex + align-items: flex-start + gap: 8px + font-size: 14px + color: #d1d5db + margin-bottom: 6px + &:last-child + margin-bottom: 0 + +.step_bullet + color: #ffb91a + font-weight: 700 + +// Desktop View - Grid +.journeys_desktop + display: none + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + +.journey_grid_card + border-radius: 12px + padding: 24px + border: 1px solid rgba(90, 42, 130, 0.4) + background: #1D0631 + transition: all 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) + border-color: #ffb91a + +.grid_card_icon + width: 56px + height: 56px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin-bottom: 16px + transition: transform 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 28px + color: white + +.journey_grid_card:hover .grid_card_icon + transform: scale(1.1) + +.grid_card_title + font-size: 20px + font-weight: 700 + color: white + margin-bottom: 8px + +.grid_card_description + font-size: 14px + color: #d1d5db + margin-bottom: 16px + line-height: 1.5 + +.grid_card_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index bda95eea6..8efad3364 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -55,13 +55,16 @@ @mixin top-navbar-colors color: white - background-color: #211e1e !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important @mixin top-navbar-menu-colors - color: white + color: white !important + font-weight: 500 + transition: color 0.3s ease &:hover padding-bottom: 0px - color: color-gradient($SECONDARY_YELLOW, 60) + color: rgba(255, 255, 255, 0.7) !important + text-decoration: none @mixin twitter-background-color background-color: #5A2A82 @@ -119,15 +122,15 @@ border-left: 3px solid color-gradient($PRIMARY_DARK_GRAY, 100) @mixin footer-colors - color: color-gradient($PRIMARY_PURPLE, 120) !important - background-color: color-gradient($FOOTER_BACKGROUND, 0) !important + color: white !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important a &:hover - color: color-gradient($FOOTER_BACKGROUND, 0) + color: white !important border-radius: 0 @mixin footer-copyright-colors - color: color-gradient($LIGHT_GRAY, 100) !important + color: rgba(156, 163, 175, 1) !important //////////////////////////////////////////////////////////////////// // diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cc813f4f1..dd1cf9d9d 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -1,21 +1,41 @@ @import "base" +// Mobile menu slide down animation +@keyframes slideDown + 0% + opacity: 0 + transform: translateY(-10px) + 100% + opacity: 1 + transform: translateY(0) + body - background-color: #211e1e !important - font-family: Roboto + background-color: white !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" font-size: 1.3rem !important margin: 0 + padding: 0 color: #000 + width: 100% + overflow-x: hidden -#page - width: 980px +.container#page, #page + width: 100% !important + max-width: 100% !important background-color: white - margin: 0 auto - padding: 10px 10px 0 10px + margin: 0 !important + padding: 0 !important header - background-color: white - margin: -10px -10px 0px -10px + // Figma design: gradient background for entire header + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-size: 16px + margin: 0 + padding: 0 + width: 100% + position: relative + z-index: 1 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -27,60 +47,94 @@ header overflow-x: hidden !important @media (min-width: 540px) and (max-width: 1024px) - #page - margin: 0 auto - padding: 10px 10px 0 10px - width: auto + #page, .container#page + margin: 0 !important + padding: 0 !important + width: 100% !important + max-width: 100% !important #nav-top-bar - margin: 11px 11px 0 11px + width: 100% + max-width: 1280px + margin: 0 auto + padding: 0 32px + display: flex + align-items: center .new_main_menu - list-style: none - margin: 0 - padding: 0 + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + gap: 18px !important li - display: inline-block - margin-right: 32px + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important a - @include top-navbar-menu-colors - font-size: 11pt - text-decoration: none + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: color 0.3s ease !important + white-space: nowrap !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + padding: 6px 0 !important + margin: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important #navbar-inner - @include top-navbar-colors - padding-top: 1px - margin-left: 4px - margin-right: 5px - height: 42px + // Navigation moved to main navbar - hide this section + display: none -@media (min-width: 320px) and (max-width: 340px) +@media (min-width: 320px) and (max-width: 480px) #nav-top-bar - margin: 11px 4px 0 0px !important + padding: 0 16px .new_main_menu + gap: 16px + .mobile-menu + .mobile-menu-content + padding: 0 8px + .mobile-menu-items li - margin-right: 2px !important a - font-size: 10px !important - -@media (min-width: 341px) and (max-width: 480px) + padding: 10px 12px + font-size: 13px + +@media (min-width: 481px) and (max-width: 768px) #nav-top-bar - margin: 11px 10px 0 11px !important - .new_main_menu - li - margin-right: 6px !important - a - font-size: 10px !important -#nav-top-bar .new_main_menu li a:hover - color: #fff - text-decoration: underline + padding: 0 24px + .new_main_menu + gap: 24px #page_contents + max-width: 100% + padding: 0 20px .separator-div @include site-separator-color #project_container background-color: white margin: 0 auto - padding: 10px 10px 0 10px + padding: 10px 20px 0 20px + width: 100% + max-width: 100% #page-contents width: auto #project_masthead @@ -99,132 +153,662 @@ header .quick_reference_heading, .nutshell_heading padding-left: 2rem -@media (min-width: 320px) and (max-width: 480px) +// sm: 640px+ +@media (min-width: 640px) .navbar - margin-right: 0px !important - .btn - font-size: 10px !important - .company-div - float: none !important - .navbar_large_text - font-size: 18px !important + .navbar-container + padding: 0 16px + gap: 12px + .logo-and-brand + gap: 6px + .logo-div + .logo_img + height: 28px + .company-div + .navbar_large_text + font-size: 16px + .separator-div + display: block + height: 24px + .actions-div + gap: 8px + .theme-toggle-btn + padding: 8px + .theme-icon + font-size: 20px + .mobile-menu-toggle + padding: 8px + i + font-size: 20px + .mobile-menu + .mobile-menu-content + padding: 0 12px + .mobile-menu-items + li + a + padding: 12px 16px + +// md: 768px+ +@media (min-width: 768px) + .navbar + .navbar-container + padding: 0 24px + gap: 16px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 32px + .separator-div + display: block + height: 26px + .logo-and-brand + gap: 10px + .mobile-menu + .mobile-menu-content + padding: 0 24px + .mobile-menu-items + li + a + padding: 14px 20px + font-size: 15px + +// lg: 1024px+ (Desktop) +@media (min-width: 1024px) + .navbar + min-height: 72px + .navbar-container + padding: 0 32px + min-height: 72px + .logo-and-brand + gap: 12px + .logo-div + .logo_img + height: 40px + .company-div + .navbar_large_text + font-size: 20px + .separator-div + height: 28px + .actions-div + gap: 16px + .nav-menu + display: flex !important + .new_main_menu + gap: 18px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 14px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + .desktop-actions + display: flex !important + .mobile-menu-toggle + display: none !important + .mobile-menu + display: none + +// xl: 1280px+ +@media (min-width: 1280px) + .navbar + min-height: 80px + .navbar-container + min-height: 80px + .logo-div + .logo_img + height: 48px + .company-div + .navbar_large_text + font-size: 24px + .nav-menu + .new_main_menu + gap: 28px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// 2xl: 1536px+ +@media (min-width: 1536px) + .navbar + min-height: 88px + .navbar-container + min-height: 88px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 30px + .nav-menu + .new_main_menu + gap: 36px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// Mobile/Tablet: < 1024px (show hamburger menu) +@media (max-width: 1023px) + .navbar + .nav-menu + display: none !important + .desktop-actions + display: none !important + .mobile-menu-toggle + display: flex !important .navbar + // Figma design: gradient background with purple theme + position: relative + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) border-width: 0 - margin-top: 15px - margin-right: 15px - margin-bottom: 5px !important - margin-left: 15px - padding: 0 0 5px 0 - min-height: 30px + border-bottom: 1px solid rgba(31, 41, 55, 0.8) + margin: 0 + padding: 0 + min-height: 64px + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2) + width: 100% + display: flex + align-items: flex-start + flex-wrap: wrap + overflow: visible + z-index: 100 + // Override global button styles for navbar + .btn + border: none !important + border-width: 0 !important + box-shadow: none !important + &:active + top: 0 !important + left: 0 !important + .navbar-container + max-width: 100% + margin: 0 auto + padding: 0 16px + width: 100% + min-height: 64px + display: flex !important + flex-direction: row !important + flex-wrap: nowrap !important + align-items: center + justify-content: space-between + gap: 12px + position: relative + .hidden + display: none !important + position: absolute !important + width: 0 !important + height: 0 !important + overflow: hidden !important + .logo-and-brand + display: flex + align-items: center + gap: 8px + flex-shrink: 0 + min-width: 0 .logo-div - height: 35px - float: left + display: flex + align-items: center + flex-shrink: 0 .logo_link text-decoration: none - margin-top: 3px - .logo_img - height: 100% + display: flex + align-items: center .company-div - @include top-navigation-colors - float: left + color: white !important + display: flex + align-items: center + min-width: 0 .navbar_small_text font-size: 10px !important .navbar_large_text - font-size: 24px + color: white !important + font-weight: 700 + line-height: 1.25 + letter-spacing: 0 + white-space: nowrap a - @include top-navigation-colors + color: white !important text-decoration: none - .spacing-div - float: left - width: 2px - height: 30px - background-color: white - margin: 0 auto + &:hover + color: #d1d5db !important + transition: color 0.3s ease .separator-div - @include site-separator-color - @include site-separator-background-color - float: left + background-color: rgba(255, 255, 255, 0.2) width: 1px - height: 30px - margin: 0 2px 0 2px + height: 20px + margin: 0 + display: none + .nav-menu + display: none + position: absolute + left: 50% + transform: translateX(-50%) + .new_main_menu + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + li + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important + a + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: all 0.3s ease !important + white-space: nowrap !important + position: relative !important + padding: 6px 0 !important + margin: 0 !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + &::after + content: '' !important + position: absolute !important + bottom: 0 !important + left: 0 !important + right: 0 !important + height: 2px !important + background-color: #FFB91A !important + border-radius: 2px !important .actions-div - float: right - .twitter-text - @include twitter-background-color - color: white - font-weight: bold - font-size: 0.80em + display: flex + align-items: center + gap: 12px + flex-shrink: 0 + flex-grow: 0 + .theme-toggle-btn + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + .theme-icon + font-size: 18px + color: white + transition: all 0.3s ease + .hidden + display: none + .mobile-menu-toggle + display: none + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + i + font-size: 18px + color: white .btn border: 0 - display: inline-block - font-size: 15px - line-height: 25px + display: inline-flex + align-items: center + justify-content: center + font-size: 14px + line-height: 1.5 margin: 0 - padding: 1px 6px 0 7px - .follow_btn - @include follow-button-colors - display: inline-block - padding: 0 0 0 4px - height: 24px - i, p, img - color: white - display: inline-block - vertical-align: top - i - color: #555 - vertical-align: middle - &:before - padding-right: 0 - p - font-weight: normal - line-height: 24px - text-transform: none - margin: 0 - border-radius: 0 4px 4px 0 - img - margin-right: -1px + padding: 8px 16px + border-radius: 8px + font-weight: 600 + transition: all 0.3s ease + cursor: pointer + text-decoration: none + white-space: nowrap ul list-style: none - margin: 3px 0 0 0 + margin: 0 + padding: 0 + display: flex + align-items: center + gap: 12px li - float: left - padding: 0 0 0 8px + padding: 0 + list-style: none #top_nav_actions - float: right - margin-right: 5px + display: flex + align-items: center + gap: 12px + margin: 0 + padding: 0 li - padding-top: 5px + padding: 0 + list-style: none + .btn-primary, .btn-success + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-width: 0 !important + font-weight: 600 !important + padding: 10px 20px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: transparent !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) !important + i + font-size: 14px + margin: 0 + .btn-outline + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + font-weight: 600 !important + padding: 8px 18px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + border-color: #FFB91A !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important @media (min-width: 320px) and (max-width: 340px) #top_nav_actions margin-right: 0px !important -#logged_user_menu - a - color: black !important +// Mobile Menu +.mobile-menu + display: none + width: 100% + flex-basis: 100% + background: linear-gradient(to right, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + border-top: 1px solid rgba(255, 255, 255, 0.15) + padding: 20px 0 + position: relative + z-index: 100 + overflow: visible + order: 2 + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) + &.show + display: block !important + visibility: visible !important + opacity: 1 !important + animation: slideDown 0.3s ease-out + .mobile-menu-content + max-width: 1280px + margin: 0 auto + padding: 0 16px + width: 100% + display: block + visibility: visible + .mobile-menu-items + list-style: none + margin: 0 + padding: 0 + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-weight: 500 + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + &.select + color: #FFB91A !important + font-weight: 600 + background-color: rgba(255, 185, 26, 0.15) + .mobile-menu-user + list-style: none + margin: 16px 0 0 0 + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.15) + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + .mobile-menu-signin + margin-top: 16px + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.1) + display: block + visibility: visible + .btn-mobile-signin + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + margin-bottom: 12px + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) + &:active + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) + .btn-mobile-join-now + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) + &:active + transform: translateY(0) + box-shadow: none + +.dropdown#logged_user_menu + position: relative + z-index: 1000 + .dropdown-toggle + color: white !important text-transform: none - text-decoration: none - margin: 0 0px 15px - padding-right: 10px - padding-top: 3px - ul - margin-top: 40px - border-radius: 0 + text-decoration: none !important + margin: 0 + padding: 10px 16px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + display: inline-flex !important + align-items: center + gap: 8px + font-size: 14px + font-weight: 500 + transition: all 0.3s ease + border: 1px solid transparent + cursor: pointer !important + position: relative + z-index: 1001 + pointer-events: auto !important + &:hover + background-color: rgba(255, 255, 255, 0.15) + border-color: rgba(255, 255, 255, 0.2) + text-decoration: none !important + transform: translateY(-1px) + &:active + transform: translateY(0) + i + font-size: 14px + pointer-events: none + ul.dropdown-menu + display: none + position: absolute + right: 0 + top: calc(100% + 24px) + margin-top: 8px + border-radius: 12px + border: 1px solid rgba(255, 255, 255, 0.15) + background-color: rgba(29, 6, 49, 0.97) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 4px 10px rgba(0, 0, 0, 0.2) + z-index: 1050 + min-width: 200px + padding: 8px 0 li - width: 150px + width: 100% a - color: black + color: white !important margin: 0 + padding: 12px 16px + transition: all 0.3s ease + border-radius: 8px + display: block + cursor: pointer &:hover - background-color: #0082C6 !important - color: #fff !important + background-color: rgba(255, 185, 26, 0.2) !important + color: #FFB91A !important + transform: translateX(4px) .divider width: 100% height: 1px - margin: 13px 0 + margin: 8px 0 overflow: hidden - border-bottom: 1px solid #e5e5e5 + border-bottom: 1px solid rgba(255, 255, 255, 0.15) + &.open + ul.dropdown-menu + display: block + +// Override Bootstrap's default dropdown styling +.navbar .dropdown.open .dropdown-menu + display: block !important + +#top_nav_actions + position: relative + z-index: 1000 #flash-msg .alert @@ -533,3 +1117,47 @@ fieldset width: 14rem .language_percentage_indicator width: 3rem + +// Full-width layout styles +.page-content-wrapper + width: 100% + max-width: 100% + margin: 0 auto + padding: 20px + +// Ensure home page content uses full width +.home-content, .explore-content, .billboard + width: 100% + max-width: 100% + margin: 0 auto + padding: 0 20px + +// Override Bootstrap navigation styles +.navbar .nav-menu .new_main_menu li.menu_item a, +.navbar ul.new_main_menu li.menu_item a, +ul.new_main_menu li.menu_item a + color: white !important + font-size: 14px !important + font-weight: 500 !important + text-decoration: none !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + padding: 6px 0 !important + margin: 0 !important + background: none !important + border: none !important + box-shadow: none !important + letter-spacing: 0 !important + &:hover, &:focus, &:active, &:visited + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + +@media (min-width: 1280px) + .navbar .nav-menu .new_main_menu li.menu_item a, + .navbar ul.new_main_menu li.menu_item a, + ul.new_main_menu li.menu_item a + font-size: 16px !important diff --git a/app/views/home/_join_now_home.html.haml b/app/views/home/_join_now_home.html.haml index 2fe5d5a3c..b2a24f233 100644 --- a/app/views/home/_join_now_home.html.haml +++ b/app/views/home/_join_now_home.html.haml @@ -1,16 +1,28 @@ -.join_now_home - %ul - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.claim') - %span.join_now_color= t('.contributions') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.manage') - %span.join_now_color= t('.projects_data') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.highlight') - %span.join_now_color= t('.use') - %br - %p - = link_to t('.join_now'), h(new_account_path(invite: params[:invite], ref: 'homepage')), class: 'btn btn_join_now' +.join_now_card + .card_inner + .join_header + .join_icon + %i.fa.fa-user-plus + .join_header_text + %h3.join_title= t('.join_now') + %p.join_subtitle= t('.become_part_community') + + .benefits_list + .benefit_item + .benefit_icon.benefit_icon_award + %i.fa.fa-trophy + .benefit_content + %h4.benefit_title= t('.track_contributions_title') + %p.benefit_description= t('.track_contributions_desc') + + .benefit_item + .benefit_icon.benefit_icon_trending + %i.fa.fa-line-chart + .benefit_content + %h4.benefit_title= t('.gain_recognition_title') + %p.benefit_description= t('.gain_recognition_desc') + + .join_cta + = link_to new_account_path(invite: params[:invite], ref: 'homepage'), class: 'btn btn_join_cta' do + %i.fa.fa-user-plus + = t('.create_account') diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml new file mode 100644 index 000000000..4f4cb2895 --- /dev/null +++ b/app/views/home/_top_contributors.html.haml @@ -0,0 +1,33 @@ +.top_contributors_card + .card_header + .header_icon + %i.fa.fa-users + .header_content + %h3.card_title= t('.top_contributors') + %p.card_subtitle= t('.most_active_developers') + + .contributors_list + - @top_contributors&.each_with_index do |contributor, index| + .contributor_item{class: "contributor-rank-#{index + 1}"} + .contributor_avatar + = link_to account_path(contributor) do + = gravatar_tag(contributor.email, size: 40, class: 'avatar_image', alt: contributor.name) + .contributor_info + .contributor_name_rank + %h4.contributor_name + = link_to contributor.name, account_path(contributor), class: 'contributor_link' + %span.contributor_rank= "##{index + 1}" + .contributor_stats + %span.stat_item + %i.fa.fa-code-fork + = number_with_delimiter(contributor.commits_count || 0) + %span.stat_item + %i.fa.fa-folder + = "#{contributor.projects_count || 0} projects" + .contributor_arrow + %i.fa.fa-chevron-right + + .view_all_link + = link_to accounts_path, class: 'btn btn_view_all' do + %i.fa.fa-users + = t('.view_all_contributors') diff --git a/app/views/home/_user_journeys.html.haml b/app/views/home/_user_journeys.html.haml new file mode 100644 index 000000000..a5d6cc56e --- /dev/null +++ b/app/views/home/_user_journeys.html.haml @@ -0,0 +1,272 @@ +.user_journeys_section + .container + .journeys_header + %h2.journeys_title= t('.your_journey_with_openhub') + %p.journeys_subtitle= t('.explore_ways_description') + + / Mobile View - Collapsible Cards + .journeys_mobile + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '0'} + .journey_header_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.journey_header_title= t('.discover_compare_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.discover_compare_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '1'} + .journey_header_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.journey_header_title= t('.assess_health_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.assess_health_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '2'} + .journey_header_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.journey_header_title= t('.manage_portfolio_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.manage_portfolio_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '3'} + .journey_header_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.journey_header_title= t('.analyze_code_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.analyze_code_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '4'} + .journey_header_icon.journey_icon_security + %i.fa.fa-shield + %h3.journey_header_title= t('.check_security_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.check_security_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '5'} + .journey_header_icon.journey_icon_contribute + %i.fa.fa-search + %h3.journey_header_title= t('.identify_opportunities_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.identify_opportunities_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + + / Desktop View - Grid Cards + .journeys_desktop + .journey_grid_card + .grid_card_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.grid_card_title= t('.discover_compare_title') + %p.grid_card_description= t('.discover_compare_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.grid_card_title= t('.assess_health_title') + %p.grid_card_description= t('.assess_health_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.grid_card_title= t('.manage_portfolio_title') + %p.grid_card_description= t('.manage_portfolio_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.grid_card_title= t('.analyze_code_title') + %p.grid_card_description= t('.analyze_code_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_security + %i.fa.fa-shield + %h3.grid_card_title= t('.check_security_title') + %p.grid_card_description= t('.check_security_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_contribute + %i.fa.fa-search + %h3.grid_card_title= t('.identify_opportunities_title') + %p.grid_card_description= t('.identify_opportunities_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + +:javascript + document.addEventListener('DOMContentLoaded', function() { + const toggleButtons = document.querySelectorAll('.journey_toggle'); + + toggleButtons.forEach(function(button) { + button.addEventListener('click', function() { + const journeyCard = this.closest('.journey_card'); + const content = journeyCard.querySelector('.journey_card_content'); + const icon = this.querySelector('.journey_toggle_icon i'); + + journeyCard.classList.toggle('expanded'); + + if (journeyCard.classList.contains('expanded')) { + icon.classList.remove('fa-plus'); + icon.classList.add('fa-minus'); + } else { + icon.classList.remove('fa-minus'); + icon.classList.add('fa-plus'); + } + }); + }); + }); diff --git a/app/views/home/_whats_new.html.haml b/app/views/home/_whats_new.html.haml index 130643baf..c045b66e9 100644 --- a/app/views/home/_whats_new.html.haml +++ b/app/views/home/_whats_new.html.haml @@ -1,2 +1,9 @@ -%a{ href: 'https://www.blackduck.com/resources/analyst-reports/open-source-security-risk-analysis.html', target: '_blank' } - %img{src: image_path('home/OSSRA-OH-banner.png'), style: 'margin-left: 25px; margin-top: 15px'} +.whats_new_card + .card_inner + .card_header_section + %h3.whats_new_title= t('.whats_new') + %p.whats_new_subtitle= t('.latest_updates_reports') + + .featured_image_section + %a{ href: 'https://www.blackduck.com/resources/analyst-reports/ossra-report.html', target: '_blank', class: 'featured_image_link' } + %img.featured_image{src: image_path('home/OSSRA-OH-banner.png'), alt: t('.ossra_report_alt')} diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index c47b3002b..473afba49 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,31 +1,81 @@ - content_for(:html_title) { t('.page_title') } -.showcase - .billboard - .row - .col-md-12 - %h2.discover_msg - = t('.discover_message') - .row - .col-md-12.billboard_search - %form.search#search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'homepage' } - %input.for_search_all_code{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text' } - %i#icon_text.fa.fa-search +- content_for(:javascript) { javascript_include_tag 'rotating_stats' } + +/ Hero Section +.hero_section + .hero_container + / Hero Header - Centered + .hero_header + %h1.hero_title= t('.discover_message') + %p.hero_subtitle= t('.worlds_largest_directory') + + / Search Bar - Centered & Compact + .search_container + %form.search_form#search_form{ action: projects_path } + %input{ type: :hidden, name: 'ref', value: 'homepage' } + .search_input_wrapper + %i.fa.fa-search.search_icon + %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } + + / Rotating Stats + .rotating_stats + #global_statistics + %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) + %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) + %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) + %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) + / Feature Cards - Moved to Top + .features_section + .feature_card + .feature_icon.feature_icon_yellow + %i.fa.fa-database + %h3.feature_title= t('.feature_directory_title') + %p.feature_description= t('.feature_directory_desc') + .feature_card + .feature_icon.feature_icon_blue + %i.fa.fa-heartbeat + %h3.feature_title= t('.feature_health_title') + %p.feature_description= t('.feature_health_desc') + .feature_card + .feature_icon.feature_icon_green + %i.fa.fa-user-circle + %h3.feature_title= t('.feature_portfolio_title') + %p.feature_description= t('.feature_portfolio_desc') + + / Quick Stats Bar - Ultra Compact & Elegant + .quick_stats_bar + .stat_item + .stat_value= "2.5M+" + .stat_label= t('.projects_label') + .stat_divider + .stat_item + .stat_value= number_with_delimiter(@home.person_count / 1000) + "K" + .stat_label= t('.contributors_label') + .stat_divider + .stat_item + .stat_value= "450+" + .stat_label= t('.languages_label') + .stat_divider + .stat_item + .stat_value= "12K" + .stat_label= t('.organizations_label') +/ Top Contributors and What's New Section +.content_section + .container .row - .col-md-12 - .global_stats - #global_statistics - %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) - %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) - %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) - %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) -.home_page_row - .col-md-4.col-xs-7.col-sm-6 - %h2.join_now= t('.join_now') - = render partial: '/home/join_now_home' - .col-md-8.col-xs-5.col-sm-6 - %h2.wh_new= t('.whats_new') - = render partial: '/home/whats_new' + / Top Contributors - 35% + .col-md-4.col-sm-12.contributors_column + = render partial: '/home/top_contributors' + + / What's New and Join Now - 65% + .col-md-8.col-sm-12.whats_new_column + .whats_new_join_stack + = render partial: '/home/whats_new' + = render partial: '/home/join_now_home' + +/ User Journeys Section += render partial: '/home/user_journeys' + .landing.col-md-12.col-sm-12.col-xs-12.last_section = home_top_lists diff --git a/app/views/layouts/partials/_footer.html.haml b/app/views/layouts/partials/_footer.html.haml index 53b3e40d7..d16f5375d 100644 --- a/app/views/layouts/partials/_footer.html.haml +++ b/app/views/layouts/partials/_footer.html.haml @@ -1,40 +1,51 @@ - raw_email = '%69%6Efo%40O%70en%48u%62.%6E%65t' -.footer-left - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } +.footer-container + .footer-grid + .footer-left + %a.logo_link{ href: 'https://www.blackduck.com' } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck' } -.footer-mid - = "ABOUT BLACK DUCK" - %p - %p - %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' - %p - %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' - %p - %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' - %p - %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' + .footer-mid + %h3 ABOUT BLACK DUCK + %ul + %li + %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' + %li + %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' + %li + %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' + %li + %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' -.footer-right - = "ABOUT OPEN HUB" - %p - %p - %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog - %p - %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') - %p - %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us + .footer-right + %h3 ABOUT OPEN HUB + %ul + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog + %li + %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') + %li + %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us - -.footer-bottom - %sup © - = Time.current.year - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span{ itemprop: 'publisher' } - %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." - = t('copyright.rights_text') + .footer-bottom + .footer-bottom-content + .copyright + %sup © + = Time.current.year + %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span{ itemprop: 'publisher' } + %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." + = t('copyright.rights_text') + .follow-us + %span Follow us: + :ruby + twitter_url = 'https://x.com/intent/follow?original_referer=' + twitter_url += CGI.escape request.url + twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' + %a{ href: twitter_url, target: '_blank', 'aria-label' => 'Follow us on Twitter' } + %i.icon-twitter diff --git a/app/views/layouts/partials/_mast.html.haml b/app/views/layouts/partials/_mast.html.haml index f28f75c58..e67c7f71c 100644 --- a/app/views/layouts/partials/_mast.html.haml +++ b/app/views/layouts/partials/_mast.html.haml @@ -1,52 +1,103 @@ +:ruby + explore_projects_class = params[:action] == 'explore/projects' ? 'select' : '' + explore_orgs_class = params[:action] == 'explore/orgs' ? 'select' : '' + tools_select = params[:action] == 'tools' ? 'select' : '' + .navbar - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span.hidden{ itemprop: 'author' } openhub.net - %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. - %span + .navbar-container + %span.hidden{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span.hidden{ itemprop: 'author' } openhub.net + %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. + .logo-and-brand %div.logo-div - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } - %div.spacing-div + %a.logo_link{ href: root_path } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck Open Hub' } %div.separator-div - %div.spacing-div %div.company-div %a{ href: root_path } %span.navbar_large_text Open Hub - %div.actions-div - %ul#top_nav_actions - %li.twitter_follow - :ruby - twitter_url = 'https://twitter.com/intent/follow?original_referer=' - twitter_url += CGI.escape request.url - twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' - %a.btn.follow_btn{ href: twitter_url, target: '_blank' } - %i.icon-twitter - %p.follow! #{t(:follow)} @ - %p.twitter-text  OH  - - if logged_in? - %li - .dropdown#logged_user_menu - %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } - %i.icon-user - = h(current_user.name) - %i.icon-caret-down - %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer - %li - = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %nav.nav-menu + %ul.new_main_menu{ class: page_context[:select_top_menu_nav] } + %li.menu_item.projects + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li.menu_item.people + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li.menu_item.organizations + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li.menu_item.tools + = link_to t(:tools_menu), tools_path, class: tools_select + %li.menu_item.blog + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li.menu_item.bdsa + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + %div.actions-div + %button#theme-toggle.theme-toggle-btn{ 'aria-label' => 'Toggle theme' } + %i#theme-icon-moon.icon-moon.theme-icon + %i#theme-icon-sun.icon-sun.theme-icon.hidden + %ul#top_nav_actions.desktop-actions + - if logged_in? + %li + .dropdown#logged_user_menu + %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } + %i.icon-user + = h(current_user.name) + %i.icon-caret-down + %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) - - if current_user_is_admin? - %li - = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) - %li.divider - %li - = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) - - else - %li - %a.btn.btn-mini.btn-primary.btn-header{ href: new_session_path }= t :sign_in + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li.divider + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + %li + %a.btn.btn-primary.btn-header{ href: new_session_path } + %i.icon-user + = t :sign_in + %li + %a.btn.btn-outline.btn-join-now{ href: new_account_path } + Join Now + %button#mobile-menu-toggle.mobile-menu-toggle{ 'aria-label' => 'Menu' } + %i#mobile-menu-icon.icon-reorder + #mobile-menu.mobile-menu + .mobile-menu-content + %ul.mobile-menu-items + %li + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li + = link_to t(:tools_menu), tools_path, class: tools_select + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + = render 'shared/search.html.haml' + - if logged_in? + %ul.mobile-menu-user + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - %a.btn.btn-mini.btn-success.btn-header{ href: new_account_path }= t :join_now + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + .mobile-menu-signin + %a.btn.btn-primary.btn-mobile-signin{ href: new_session_path } + %i.icon-user + = t :sign_in + %a.btn.btn-outline.btn-mobile-join-now{ href: new_account_path } + Join Now - if read_only_mode? %div diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e3160c2b9..3fe5a83df 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,5 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js] + api/vulnerability.sass api/vulnerability.js rotating_stats.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 9e5497ce0..075a8dbc6 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -3,6 +3,7 @@ en: index: page_title: 'Open Hub, the open source network' discover_message: 'Discover, Track and Compare Open Source' + worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' whats_new: "What's New" counting: 'Counting %{count} lines of code' @@ -10,6 +11,16 @@ en: connecting: 'Connecting %{count} open source contributors' tracking: 'Tracking %{count} source control repositories' search_projects: 'Search Projects...' + feature_directory_title: 'Massive Directory' + feature_directory_desc: 'Indexing over 2.5 million open source projects.' + feature_health_title: 'Assess Health' + feature_health_desc: 'Check activity levels and risk before you adopt.' + feature_portfolio_title: 'Manage Portfolio' + feature_portfolio_desc: 'Claim your commits and track your developer rankings.' + projects_label: 'Projects' + contributors_label: 'Contributors' + languages_label: 'Languages' + organizations_label: 'Organizations' top_lists: popular_projects: 'Most Popular Projects' active_projects: 'Most Active Projects' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb index 04d2523cc..3e27b7716 100644 --- a/test/controllers/home_controller_test.rb +++ b/test/controllers/home_controller_test.rb @@ -9,7 +9,13 @@ class HomeControllerTest < ActionController::TestCase best_account_analysis.account.update(best_vita_id: best_account_analysis.id, created_at: 4.days.ago) account_analysis_fact = best_account_analysis.account_analysis_fact account_analysis_fact.update(last_checkin: Time.current) - Rails.cache.stubs(:fetch).returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-recently_active_accounts-cache').returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-person_count-cache').returns(1_000_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-active_project_count-cache').returns(2_500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-repository_count-cache').returns(500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-vita_count-cache').returns([]) + Rails.cache.stubs(:fetch).with('HomeDecorator-most_active_projects-cache').returns([]) + Rails.cache.stubs(:fetch).with('homepage_top_lists').returns([]) get :index assert_response :success diff --git a/test/controllers/licenses_controller_test.rb b/test/controllers/licenses_controller_test.rb index b2ac3ce27..e04bba48b 100644 --- a/test/controllers/licenses_controller_test.rb +++ b/test/controllers/licenses_controller_test.rb @@ -52,7 +52,7 @@ class LicensesControllerTest < ActionController::TestCase get :show, params: { id: @license.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e59b18bb3..fc1e35bab 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -83,7 +83,7 @@ class OrganizationsControllerTest < ActionController::TestCase get :show, params: { id: @organization.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'should support show page via xml api' do diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index def7e2c23..0c4a958ea 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -292,7 +292,7 @@ class ProjectsControllerTest < ActionController::TestCase get :show, params: { id: project.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'show accepts being called via api' do From 81fd60bd3847ce0e1df4ca918fc668206d67eac0 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 22/53] Added layout for home page --- app/assets/javascripts/project_swimlanes.js | 30 + app/assets/stylesheets/base.sass | 11 +- app/assets/stylesheets/buttons.sass | 45 +- app/assets/stylesheets/dark_theme.sass | 64 +- app/assets/stylesheets/footer.sass | 84 +- app/assets/stylesheets/home.sass | 1154 +++++++++++++---- app/assets/stylesheets/home_search.sass | 145 +++ app/assets/stylesheets/oh-styles.sass | 20 + app/assets/stylesheets/page.sass | 17 +- app/assets/stylesheets/search-dingus.sass | 83 +- .../home/_compact_project_card.html.haml | 37 + app/views/home/_top_contributors.html.haml | 41 +- app/views/home/_top_lists.html.haml | 141 +- app/views/home/index.html.haml | 14 +- .../search_dingus/_search_bar.html.haml | 10 +- config/initializers/assets.rb | 3 +- config/locales/home.en.yml | 68 +- 17 files changed, 1510 insertions(+), 457 deletions(-) create mode 100644 app/assets/javascripts/project_swimlanes.js create mode 100644 app/assets/stylesheets/home_search.sass create mode 100644 app/views/home/_compact_project_card.html.haml diff --git a/app/assets/javascripts/project_swimlanes.js b/app/assets/javascripts/project_swimlanes.js new file mode 100644 index 000000000..270f4e96f --- /dev/null +++ b/app/assets/javascripts/project_swimlanes.js @@ -0,0 +1,30 @@ +// Project Swimlanes - Show More/Less functionality +document.addEventListener('DOMContentLoaded', function() { + var buttons = document.querySelectorAll('.show_more_btn'); + + for (var i = 0; i < buttons.length; i++) { + buttons[i].addEventListener('click', function() { + var swimlane = this.getAttribute('data-swimlane'); + var swimlaneContent = document.querySelector('.swimlane_content[data-swimlane="' + swimlane + '"]'); + var hiddenCards = swimlaneContent.querySelectorAll('.hidden_card'); + var isExpanded = this.textContent.trim() === 'Show Less'; + + // Toggle visibility of hidden cards + for (var j = 0; j < hiddenCards.length; j++) { + if (isExpanded) { + hiddenCards[j].style.display = 'none'; + } else { + hiddenCards[j].style.display = 'block'; + } + console.log('Card', j, 'classes:', hiddenCards[j].className, 'display:', window.getComputedStyle(hiddenCards[j]).display); + } + + // Toggle button text + if (isExpanded) { + this.textContent = 'Show More'; + } else { + this.textContent = 'Show Less'; + } + }); + } +}); diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 2e50269cc..43c4cbed2 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -2,13 +2,13 @@ // base mixins =sans_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =serif_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =arial_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =lucida_font - font-family: 'Roboto' + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =small_font font-size: .91667em =tiny_font @@ -21,10 +21,11 @@ #commits_summary_page, #mini_account_row [class^="icon-"], [class*=" icon-"] - font-family: Roboto !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important body text-rendering: optimizeLegibility + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" a @include site-link-color diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 276577616..8baec979b 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -90,14 +90,49 @@ button.btn:active border-color: #005481 .btn-refresh - @include secondary-button-colors + color: white !important + background-color: #5A2A82 !important + border-color: #5A2A82 !important + border-radius: 0 12px 12px 0 + margin-left: -1px + padding: 10px 18px + min-width: 48px + transition: all 0.15s ease-in-out + &:hover + background-color: rgba(90, 42, 130, 0.9) !important + border-color: #5A2A82 !important + transform: none + &:active + background-color: #4A1A72 !important + border-color: #4A1A72 !important &.no-hover:hover - background-color: #0082C6 !important + background-color: #5A2A82 !important &.active, &.no-border.active - background-color: #66B7E8 !important - border-color: #66B7E8 + background-color: #4A1A72 !important + border-color: #4A1A72 &.disabled, &[disabled] - background-color: #66CEF6 !important + background-color: #8A5AAA !important + border-color: #8A5AAA !important + opacity: 0.6 + cursor: not-allowed + i + font-size: 16px + line-height: 1 + + // Dark mode support + .dark & + background-color: #ffb91a !important + border-color: #ffb91a !important + color: #1D0631 !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: #ffb91a !important + &:active + background-color: #e6a617 !important + border-color: #e6a617 !important + &.active, &.no-border.active + background-color: #e6a617 !important + border-color: #e6a617 .btn-success @include join-now-button-colors diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index e240d718d..9a7f3d844 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -2,35 +2,35 @@ // Applied when .dark class is added to html element html.dark - // Body and background colors + // Body and background colors - matches home page sections body - background-color: #0f172a !important + background-color: #1D0631 !important color: #e2e8f0 !important // Page container #page, .container#page - background-color: #0f172a !important + background-color: #1D0631 !important // Header stays with purple gradient (no change needed) // Footer stays with purple gradient (no change needed) // Content areas #page-contents, #page_contents - background-color: #1e293b - color: #e2e8f0 + background-color: #1D0631 !important + color: #e2e8f0 !important // Cards and wells .well - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #334155 !important + border-color: #4b5563 !important // Tables table - background-color: #1e293b + background-color: #1D0631 color: #e2e8f0 td, th - border-color: #334155 !important + border-color: #4b5563 !important color: #e2e8f0 !important // Links @@ -49,23 +49,23 @@ html.dark // Forms input, textarea, select - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #334155 !important + border-color: #4b5563 !important &::placeholder color: #94a3b8 !important // Billboard/Showcase .billboard - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important .showcase - background-color: #0f172a !important + background-color: #1D0631 !important // Project container #project_container - background-color: #0f172a !important + background-color: #1D0631 !important color: #e2e8f0 !important // Headings @@ -74,26 +74,26 @@ html.dark // Alerts .alert - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #334155 !important + border-color: #4b5563 !important // Dropdown menus .dropdown-menu - background-color: #1e293b !important - border-color: #334155 !important + background-color: #1D0631 !important + border-color: #4b5563 !important li a color: #e2e8f0 !important &:hover - background-color: #334155 !important + background-color: rgba(90, 42, 130, 0.5) !important // Home page sections .top_ten, .top_ten.middle, .top_ten.last - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important .home_page_row .col-md-4 - background-color: #1e293b !important + background-color: #1D0631 !important // Navigation menu - keep white text on purple gradient .navbar, #navbar-inner @@ -102,38 +102,38 @@ html.dark // Search inputs .for_search_all_code - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #334155 !important + border-color: #4b5563 !important // Badges .badge - background-color: #334155 !important + background-color: rgba(90, 42, 130, 0.5) !important color: #e2e8f0 !important // Panels .panel - background-color: #1e293b !important - border-color: #334155 !important + background-color: #1D0631 !important + border-color: #4b5563 !important .panel-heading - background-color: #334155 !important + background-color: rgba(90, 42, 130, 0.5) !important color: #e2e8f0 !important .panel-body - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important // Code blocks pre, code - background-color: #1e293b !important + background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #334155 !important + border-color: #4b5563 !important // Borders .mezzo - border-top-color: #334155 !important + border-top-color: #4b5563 !important .right_border - border-right-color: #334155 !important + border-right-color: #4b5563 !important // Text colors .signature_color diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index 1baf71240..61f45a71a 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -8,69 +8,32 @@ footer padding: 0 width: 100% max-width: 100% + // Matches Figma: max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 .footer-container position: relative max-width: 1280px margin: 0 auto - padding: 48px 32px 24px 32px - - // Mobile: < 640px - @media (max-width: 639px) - .footer-container - padding: 32px 16px 16px 16px - .footer-grid - gap: 32px - margin-bottom: 24px - .footer-left - .logo_img - height: 28px - .footer-mid, .footer-right - h3 - font-size: 10px - margin-bottom: 12px - a - font-size: 10px - li - margin-bottom: 8px - .footer-bottom - .footer-bottom-content - gap: 8px - .copyright - font-size: 9px - text-align: center - .follow-us - span - font-size: 10px - a - width: 28px - height: 28px - i - font-size: 14px - - // Tablet: 640px - 767px - @media (min-width: 640px) and (max-width: 767px) - .footer-container - padding: 40px 24px 20px 24px - .footer-grid - gap: 40px - margin-bottom: 28px - - // Tablet landscape: 768px - 1023px - @media (min-width: 768px) and (max-width: 1023px) - .footer-container - padding: 44px 32px 22px 32px - .footer-grid - gap: 36px + padding: 24px 16px + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 48px 32px + // Matches Figma: grid-cols-1 md:grid-cols-3 gap-6 sm:gap-8 md:gap-12 .footer-grid display: grid grid-template-columns: 1fr - gap: 48px - margin-bottom: 32px + gap: 24px + margin-bottom: 24px + @media (min-width: 640px) + gap: 32px + margin-bottom: 32px @media (min-width: 768px) grid-template-columns: repeat(3, 1fr) gap: 48px + margin-bottom: 48px + // Matches Figma: h-8 sm:h-10 .footer-left display: flex align-items: flex-start @@ -85,16 +48,17 @@ footer @media (min-width: 640px) height: 40px + // Matches Figma: text-xs sm:text-sm for headers and links .footer-mid, .footer-right h3 - font-size: 11px + font-size: 12px font-weight: 600 color: white !important - margin-bottom: 16px + margin-bottom: 8px text-transform: uppercase letter-spacing: 0.05em @media (min-width: 640px) - font-size: 13px + font-size: 14px margin-bottom: 12px @media (min-width: 768px) margin-bottom: 16px @@ -103,13 +67,13 @@ footer margin: 0 padding: 0 li - margin-bottom: 10px + margin-bottom: 6px @media (min-width: 640px) margin-bottom: 8px @media (min-width: 768px) margin-bottom: 10px p - margin: 0 0 10px 0 + margin: 0 0 6px 0 @media (min-width: 640px) margin: 0 0 8px 0 @media (min-width: 768px) @@ -117,14 +81,15 @@ footer a color: #d1d5db !important text-decoration: none - font-size: 11px + font-size: 12px font-weight: 400 transition: color 0.3s ease @media (min-width: 640px) - font-size: 13px + font-size: 14px &:hover color: white !important + // Matches Figma: py-3 sm:py-4 md:py-6 .footer-bottom border-top: 1px solid rgba(255, 255, 255, 0.1) padding: 12px 0 @@ -140,6 +105,7 @@ footer gap: 12px @media (min-width: 640px) flex-direction: row + // Matches Figma: text-[10px] sm:text-xs .copyright color: #9ca3af !important font-size: 10px @@ -154,12 +120,14 @@ footer order: 1 @media (min-width: 640px) order: 2 + // Matches Figma: text-xs sm:text-sm span color: #d1d5db !important font-size: 12px font-weight: 500 @media (min-width: 640px) font-size: 14px + // Matches Figma: w-8 h-8 a display: flex align-items: center diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 0422db618..4978e69e3 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,136 +1,88 @@ -// Hero Section - Updated to match Figma exactly +// ======================================== +// Hero Section - Figma Design System +// ======================================== +// Main container with consistent 1280px max-width throughout +// Isolated from global body font-size: 1.3rem override + .hero_section - padding-top: 60px - padding-bottom: 40px - padding-left: 16px - padding-right: 16px + padding: 0 16px 40px background: #fff !important transition: background 0.3s ease + box-sizing: border-box @media (min-width: 640px) - padding-top: 72px - padding-left: 24px - padding-right: 24px + padding: 0 24px 40px @media (min-width: 1024px) - padding-top: 88px - padding-left: 32px - padding-right: 32px + padding: 0 32px 40px // Dark mode support html.dark & background: #1D0631 !important + // Ensure all child elements use border-box + *, *::before, *::after + box-sizing: border-box +// Hero Container - Controls all child widths consistently (1280px) .hero_container - max-width: 80rem + max-width: 1280px margin: 0 auto + width: 100% + box-sizing: border-box // Hero Header - Figma Specs .hero_header text-align: center margin-bottom: 24px + padding: 0 + @media (min-width: 768px) + margin-bottom: 24px .hero_title - font-size: 1.875rem !important + font-size: 1.875rem font-weight: 700 color: #5A2A82 margin-bottom: 8px - line-height: 2.25rem !important + line-height: 2.25rem transition: color 0.3s ease @media (min-width: 640px) - font-size: 2.25rem !important - line-height: 2.5rem !important + font-size: 2.25rem + line-height: 2.5rem @media (min-width: 768px) - font-size: 3rem !important - line-height: 1 !important + font-size: 48px + line-height: 1 // Dark mode support html.dark & color: #ffffff .hero_subtitle - font-size: 0.875rem !important + font-size: 0.875rem color: #6b7280 - max-width: 42rem margin: 0 auto - line-height: 1.25rem !important + max-width: 672px + line-height: 1.25rem transition: color 0.3s ease @media (min-width: 640px) - font-size: 1rem !important - line-height: 1.5rem !important + font-size: 16px + line-height: 1.5rem // Dark mode support html.dark & color: #9ca3af -// Search Container - Figma Specs +// Search Container - Narrower for focused search UX (768px) .search_container - max-width: 48rem + max-width: 768px margin: 0 auto 20px - -.search_form - margin-bottom: 0 - position: relative - -.search_input_wrapper - position: relative - -.search_icon - position: absolute - top: 50% - transform: translateY(-50%) - left: 8px - font-size: 14px - color: #9ca3af - pointer-events: none - z-index: 1 - transition: color 0.3s ease - @media (min-width: 640px) - left: 24px - font-size: 20px - // Dark mode support - html.dark & - color: #6b7280 - -.search_input width: 100% - padding: 10px 12px 10px 32px - font-size: 11px !important - border: 2px solid #e5e7eb - border-radius: 16px - outline: none - background: #fff - color: #111827 - font-family: inherit - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) - transition: all 0.2s ease - &::placeholder - color: #9ca3af - &:hover - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - &:focus - border-color: #0E4B7A - box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) - &::placeholder - color: #d1d5db - @media (min-width: 640px) - padding: 16px 16px 16px 56px - font-size: 16px !important - // Dark mode support - html.dark & - background: #1D0631 !important - color: #ffffff - border-color: #374151 - &::placeholder - color: #6b7280 - &:focus - border-color: #2E8B9E - box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + box-sizing: border-box // Rotating Stats - Figma Animation .rotating_stats text-align: center margin-top: 12px + margin-bottom: 24px height: 20px #global_statistics p - font-size: 12px !important + font-size: 12px font-weight: 500 background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) -webkit-background-clip: text @@ -147,21 +99,40 @@ display: block // Dark mode support html.dark & - background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) !important + -webkit-background-clip: text !important + -webkit-text-fill-color: transparent !important + background-clip: text !important // Old join_now and home_page_row styles removed - using new card-based design // Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px margin-left: 0 - padding: 0 20px + padding: 32px 16px width: 100% + background: #f9fafb !important + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + // Dark mode support + html.dark & + background: #1D0631 !important .top_ten_main padding-left: 40px !important padding-bottom: 6px !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" .top_ten_row padding-left: 9px !important padding-bottom: 15px + transition: all 0.3s ease + border-radius: 8px + margin-bottom: 12px + &:hover + background: rgba(90, 42, 130, 0.05) + transform: translateX(4px) .top_ten_bar line-height: 0.6em width: 30% @@ -170,11 +141,31 @@ .top_ten_label line-height: 1.5em padding-left: 8px - color: #555 + color: #6b7280 + font-size: 12px + font-weight: 500 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + @media (min-width: 768px) + font-size: 14px + // Dark mode support + html.dark & + color: #9ca3af .top_ten h2 - font-weight: 500 !important + font-weight: 600 !important font-size: 16px !important + color: #111827 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode support + html.dark & + color: #ffffff .top_ten.middle @include site-well-color .top_ten.last @@ -182,8 +173,8 @@ margin-right: -25px !important .top_ten_icon img - background-color: #EEE - color: #000 + background-color: #f3f4f6 + color: #111827 font-size: 4px line-height: 32px width: 32px @@ -192,10 +183,21 @@ float: left margin-bottom: 0 margin-top: 0 - margin-right: 2px + margin-right: 8px + border-radius: 8px + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) -webkit-filter:grayscale(1) filter: grayscale(1) - opacity: .75 + opacity: .85 + transition: all 0.3s ease + // Dark mode support + html.dark & + background-color: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + &:hover img + opacity: 1 + transform: scale(1.05) a.top_ten_icon p opacity: .75 background-color: #999 @@ -203,6 +205,23 @@ a.top_ten_icon p .top_ten_link a border: 0 + color: #5A2A82 + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 15px + @media (min-width: 768px) + font-size: 16px + &:hover + color: #1D0631 + text-decoration: none + // Dark mode support + html.dark & + color: #ffffff + &:hover + color: #ffb91a // Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors @@ -228,10 +247,11 @@ button #no-background background-color: white !important -// Feature Cards Section - Figma Specs +// Feature Cards Section - Figma: 1024px width .features_section - max-width: 64rem - margin: 0 auto 32px + max-width: 1024px + margin: 0 auto 24px + box-sizing: border-box @media (min-width: 768px) display: grid grid-template-columns: repeat(3, 1fr) @@ -286,7 +306,7 @@ button .feature_description font-size: 10px !important color: #6b7280 - line-height: 1.5 + line-height: 1.625 padding: 0 8px transition: color 0.3s ease @media (min-width: 640px) @@ -295,12 +315,12 @@ button html.dark & color: #9ca3af -// Quick Stats Bar - Figma Specs (grid-cols-2 md:flex) +// Quick Stats Bar - Compact and centered (768px) .quick_stats_bar display: grid grid-template-columns: repeat(2, 1fr) gap: 16px - max-width: 48rem + max-width: 768px margin: 0 auto padding: 16px background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) @@ -309,6 +329,7 @@ button border: 1px solid rgba(229, 231, 235, 0.6) box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) transition: all 0.3s ease + box-sizing: border-box @media (min-width: 768px) display: flex align-items: center @@ -324,21 +345,21 @@ button text-align: center .stat_value - font-size: 16px !important + font-size: 16px font-weight: 700 color: #5A2A82 line-height: 1.2 transition: color 0.3s ease @media (min-width: 640px) - font-size: 18px !important + font-size: 18px @media (min-width: 768px) - font-size: 20px !important + font-size: 20px // Dark mode - Purple text on yellow background html.dark & color: #5A2A82 .stat_label - font-size: 8px !important + font-size: 8px color: #6b7280 text-transform: uppercase font-weight: 500 @@ -346,9 +367,9 @@ button margin-top: 2px transition: color 0.3s ease @media (min-width: 640px) - font-size: 9px !important + font-size: 9px @media (min-width: 768px) - font-size: 10px !important + font-size: 10px // Dark mode - Purple text on yellow background html.dark & color: #5A2A82 @@ -360,8 +381,8 @@ button display: block width: 1px height: 32px - background: #d1d5db - opacity: 0.3 + background: #9ca3af + opacity: 1 // Dark mode - Purple divider on yellow background html.dark & @media (min-width: 768px) @@ -377,169 +398,297 @@ button html.dark & background: #1D0631 !important -.contributors_column, .whats_new_column - @media (max-width: 768px) - margin-bottom: 24px + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + +// Content Grid - matches Figma design: grid md:grid-cols-[35%_65%] gap-6 +.content_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: 35% 65% + +.contributors_column + width: 100% + +.whats_new_column + width: 100% + display: block .whats_new_join_stack display: flex flex-direction: column gap: 24px -// Top Contributors Card +// Top Contributors Card - Matches ContributorJourney.tsx .top_contributors_card position: relative padding: 3px border-radius: 16px - overflow: hidden background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + margin-bottom: 24px + overflow: hidden transition: box-shadow 0.3s &:hover - box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) - > div + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) + html.dark & + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + + > * background: white - border-radius: 14px - height: 100% + border-radius: 16px + overflow: hidden + html.dark & + background: #1D0631 -.card_header +// Card Header +.top_contributors_card .card_header display: flex align-items: flex-start gap: 16px - padding: 24px - padding-bottom: 16px + padding: 24px 24px 16px 24px background: white + html.dark & + background: #1D0631 -.header_icon - width: 48px - height: 48px - background: rgba(90, 42, 130, 0.1) - border-radius: 12px +.top_contributors_card .header_icon display: flex align-items: center justify-content: center - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + width: 40px + height: 40px + border-radius: 12px + background: rgba(90, 42, 130, 0.1) flex-shrink: 0 + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + @media (min-width: 640px) + width: 48px + height: 48px + html.dark & + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + i - font-size: 24px color: #5A2A82 + font-size: 20px + @media (min-width: 640px) + font-size: 24px + html.dark & + color: white -.header_content +.top_contributors_card .header_content flex: 1 -.card_title - font-size: 18px +.top_contributors_card .card_title + font-size: 16px font-weight: 700 color: #5A2A82 - margin-bottom: 4px + margin: 0 0 6px 0 + line-height: 1.4 + @media (min-width: 640px) + font-size: 18px + html.dark & + color: white -.card_subtitle - font-size: 12px +.top_contributors_card .card_subtitle + font-size: 10px color: rgba(90, 42, 130, 0.7) + margin: 0 line-height: 1.5 + @media (min-width: 640px) + font-size: 12px + html.dark & + color: #9CA3AF -.contributors_list +// Contributors List +.top_contributors_card .contributors_list padding: 16px - flex: 1 - overflow-y: auto + display: flex + flex-direction: column + gap: 8px -.contributor_item - background: linear-gradient(90deg, #f9fafb 0%, #f3f4f6 100%) - border-radius: 12px - padding: 12px - border: 1px solid #e5e7eb - cursor: pointer - transition: all 0.3s - margin-bottom: 8px +.top_contributors_card .contributor_item display: flex align-items: center gap: 12px + padding: 12px + background: linear-gradient(to right, #f9fafb 0%, #f1f5f9 100%) + border: 1px solid #e5e7eb + border-radius: 12px + transition: all 0.2s + text-decoration: none + cursor: pointer + &:hover - background: linear-gradient(90deg, #f5f3ff 0%, #ede9fe 100%) + background: linear-gradient(to right, #f5f3ff 0%, #ede9fe 100%) border-color: #5A2A82 -.contributor_avatar + html.dark & + background: linear-gradient(to right, rgba(55, 65, 81, 0.5) 0%, rgba(71, 85, 105, 0.5) 100%) + border-color: #4B5563 + &:hover + background: linear-gradient(to right, rgba(109, 40, 217, 0.3) 0%, rgba(124, 58, 237, 0.3) 100%) + border-color: #5A2A82 + +// Show only top 4 contributors on mobile +.top_contributors_card .contributor_item:nth-child(n+5) + @media (max-width: 767px) + display: none + +// Avatar +.top_contributors_card .contributor_avatar flex-shrink: 0 - .avatar_image - width: 40px - height: 40px - border-radius: 12px - object-fit: cover - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) - border: 2px solid #9ca3af + width: 40px + height: 40px -.contributor_info +.top_contributors_card .avatar_img, +.top_contributors_card .avatar_placeholder + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + display: block + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) + border: 2px solid #9CA3AF + +// Contributor Info +.top_contributors_card .contributor_info flex: 1 min-width: 0 + display: flex + flex-direction: column + gap: 2px -.contributor_name_rank +.top_contributors_card .contributor_name_row display: flex align-items: center gap: 8px margin-bottom: 2px -.contributor_name +.top_contributors_card .contributor_name font-size: 14px font-weight: 600 color: #5A2A82 - margin: 0 - .contributor_link - color: inherit - text-decoration: none - &:hover - text-decoration: underline + line-height: 1.3 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + html.dark & + color: white -.contributor_rank +.top_contributors_card .contributor_rank font-size: 9px + font-weight: 400 color: rgba(90, 42, 130, 0.6) text-transform: uppercase letter-spacing: 0.05em + white-space: nowrap flex-shrink: 0 + html.dark & + color: #9CA3AF -.contributor_stats +.top_contributors_card .contributor_stats display: flex align-items: center gap: 12px font-size: 10px color: rgba(90, 42, 130, 0.7) - .stat_item + + html.dark & + color: #9CA3AF + + span display: flex align-items: center gap: 4px - i - font-size: 10px -.contributor_arrow + i + flex-shrink: 0 + display: inline-flex + align-items: center + justify-content: center + font-size: 9px + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_commits, +.top_contributors_card .stat_projects + font-weight: 400 + color: rgba(90, 42, 130, 0.7) + html.dark & + color: #9CA3AF + +.top_contributors_card .stat_separator + display: none + +// Arrow +.top_contributors_card .contributor_arrow flex-shrink: 0 color: rgba(90, 42, 130, 0.4) - transition: color 0.3s + display: flex + align-items: center + transition: color 0.2s + i font-size: 16px -.contributor_item:hover .contributor_arrow + html.dark & + color: rgba(255, 255, 255, 0.3) + +.top_contributors_card .contributor_item:hover .contributor_arrow color: #5A2A82 + html.dark & + color: white -.view_all_link +// View All Footer +.top_contributors_card .view_all_footer padding: 16px - .btn_view_all - width: 100% - display: flex - align-items: center - justify-center - gap: 8px - font-size: 14px - font-weight: 600 - color: #5A2A82 - transition: color 0.3s - padding: 12px - border-top: 1px solid rgba(90, 42, 130, 0.2) - text-decoration: none - i - font-size: 16px + +.top_contributors_card .view_all_link + display: flex + width: 100% + align-items: center + justify-content: center + gap: 8px + padding: 12px + font-size: 14px + font-weight: 600 + color: #5A2A82 + text-decoration: none + border-top: 1px solid rgba(90, 42, 130, 0.2) + transition: color 0.2s + cursor: pointer + + span + border-bottom: 1px solid #5A2A82 + + &:hover + color: #1D0631 + span + border-bottom-color: #1D0631 + + i + font-size: 16px + + html.dark & + color: white !important + border-top-color: #ffb91a + border-top-width: 2px + border-radius: 12px + margin-top: 8px + span + border-bottom-color: white &:hover - color: #1D0631 + color: white !important -// What's New Card +// What's New Card - Figma Design .whats_new_card position: relative padding: 3px @@ -555,24 +704,49 @@ button border-radius: 14px overflow: hidden height: 100% + // Dark mode support + html.dark & + background: #1D0631 .card_header_section - padding: 24px - padding-bottom: 16px + padding: 16px 16px 12px 16px !important background: white + @media (min-width: 640px) + padding: 24px 24px 16px 24px !important + // Dark mode support + html.dark & + background: #1D0631 .whats_new_title - font-size: 20px - font-weight: 700 - color: #5A2A82 - margin-bottom: 4px + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important .whats_new_subtitle - font-size: 12px - color: rgba(90, 42, 130, 0.7) + font-size: 12px !important + color: rgba(90, 42, 130, 0.7) !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important .featured_image_section - padding: 0 24px 24px + padding: 0 16px 16px !important + display: block + @media (min-width: 640px) + padding: 0 24px 24px !important .featured_image_link display: block @@ -588,9 +762,11 @@ button .featured_image width: 100% height: auto + display: block + max-width: 100% transition: transform 0.3s -// Join Now Card +// Join Now Card - Figma Design (p-6 wrapper) .join_now_card position: relative padding: 3px @@ -606,17 +782,21 @@ button border-radius: 14px overflow: hidden height: 100% + padding: 24px !important + // Dark mode support + html.dark & + background: #1D0631 .join_header display: flex align-items: center - gap: 12px - padding: 24px - padding-bottom: 16px + gap: 12px !important + margin-bottom: 16px !important + padding: 0 !important .join_icon - width: 48px - height: 48px + width: 40px + height: 40px background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) border-radius: 12px display: flex @@ -624,96 +804,151 @@ button justify-content: center box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) flex-shrink: 0 + @media (min-width: 640px) + width: 48px + height: 48px i - font-size: 24px + font-size: 20px color: white + @media (min-width: 640px) + font-size: 24px .join_header_text flex: 1 .join_title - font-size: 20px - font-weight: 700 - color: #5A2A82 - margin-bottom: 2px + font-size: 18px !important + font-weight: 700 !important + color: #5A2A82 !important + margin: 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 20px !important + @media (min-width: 768px) + font-size: 24px !important + // Dark mode support + html.dark & + color: white !important .join_subtitle - font-size: 12px - color: #6b7280 + font-size: 12px !important + color: #6b7280 !important + line-height: 1.5 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #9ca3af !important .benefits_list - padding: 0 24px - margin-bottom: 24px + padding: 0 !important + margin-bottom: 24px !important + // Figma: space-y-3 (12px gap) + > * + * + margin-top: 12px !important .benefit_item display: flex align-items: flex-start gap: 12px - margin-bottom: 12px .benefit_icon - width: 32px - height: 32px + width: 28px + height: 28px border-radius: 8px display: flex align-items: center justify-content: center flex-shrink: 0 margin-top: 2px + @media (min-width: 640px) + width: 32px + height: 32px .benefit_icon_award background: rgba(90, 42, 130, 0.1) i - font-size: 16px + font-size: 14px color: #5A2A82 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #5A2A82 .benefit_icon_trending background: rgba(29, 6, 49, 0.1) i - font-size: 16px + font-size: 14px color: #1D0631 + @media (min-width: 640px) + font-size: 16px + // Dark mode support + html.dark & + background: rgba(90, 42, 130, 0.2) + i + color: #1D0631 .benefit_content flex: 1 .benefit_title - font-size: 13px - font-weight: 600 - color: #111827 - margin-bottom: 2px + font-size: 12px !important + font-weight: 600 !important + color: #111827 !important + margin: 0 0 4px 0 !important + line-height: 1.2 !important + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: white !important .benefit_description - font-size: 11px - color: #6b7280 - line-height: 1.5 + font-size: 10px !important + color: #6b7280 !important + line-height: 1.625 !important + margin: 0 !important + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af !important .join_cta - padding: 0 24px 24px + padding: 0 !important .btn_join_cta width: 100% - padding: 12px 24px - background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) - color: white - border-radius: 8px - font-weight: 600 + padding: 12px 24px !important + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) !important + color: white !important + border-radius: 8px !important + font-weight: 600 !important transition: all 0.3s box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) display: flex align-items: center justify-content: center gap: 8px - font-size: 16px - text-decoration: none + font-size: 14px !important + text-decoration: none !important border: none cursor: pointer + @media (min-width: 640px) + font-size: 16px !important &:hover - background: linear-gradient(90deg, #1D0631dd 0%, #5A2A82dd 100%) - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) - color: white - text-decoration: none + background: linear-gradient(90deg, rgba(29, 6, 49, 0.9) 0%, rgba(90, 42, 130, 0.9) 100%) !important + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important + color: white !important + text-decoration: none !important i - font-size: 16px + font-size: 14px + @media (min-width: 640px) + font-size: 16px // User Journeys Section .user_journeys_section @@ -722,30 +957,43 @@ button @media (max-width: 768px) padding: 48px 0 + // Matches Figma: max-w-7xl mx-auto + .container + max-width: 1280px + margin: 0 auto + padding: 0 16px + @media (min-width: 640px) + padding: 0 24px + @media (min-width: 1024px) + padding: 0 32px + .journeys_header text-align: center margin-bottom: 48px +// Matches Figma: text-2xl sm:text-3xl .journeys_title - font-size: 30px - font-weight: 700 + font-size: 24px + font-weight: 700 !important color: white margin-bottom: 12px - @media (max-width: 768px) - font-size: 24px + @media (min-width: 640px) + font-size: 30px +// Matches Figma: text-base sm:text-lg max-w-2xl .journeys_subtitle - font-size: 18px + font-size: 16px color: #d1d5db - max-width: 768px + max-width: 672px margin: 0 auto - @media (max-width: 768px) - font-size: 16px + @media (min-width: 640px) + font-size: 18px // Mobile View - Collapsible +// Matches Figma: lg:hidden (shows below 768px, then desktop grid takes over) .journeys_mobile display: block - @media (min-width: 1024px) + @media (min-width: 768px) display: none .journey_card @@ -757,6 +1005,7 @@ button margin-bottom: 16px transition: all 0.3s +// Matches Figma: rounded-xl p-4 .journey_card_header width: 100% display: flex @@ -768,7 +1017,7 @@ button background: #1D0631 border: none cursor: pointer - border-radius: 10px + border-radius: 12px &:hover background: rgba(90, 42, 130, 0.2) @@ -789,8 +1038,9 @@ button .journey_header_title flex: 1 font-size: 14px - font-weight: 600 + font-weight: 600 !important color: white + overflow-wrap: break-word .journey_toggle_icon flex-shrink: 0 @@ -798,11 +1048,12 @@ button font-size: 20px color: white +// Matches Figma: px-4 pb-4 .journey_card_content display: none padding: 0 16px 16px background: #1D0631 - border-radius: 0 0 10px 10px + border-radius: 0 0 12px 12px .journey_card.expanded .journey_card_content display: block @@ -821,6 +1072,7 @@ button color: #d1d5db line-height: 1.5 margin-bottom: 16px + overflow-wrap: break-word .journey_steps background: rgba(90, 42, 130, 0.2) @@ -855,19 +1107,25 @@ button font-weight: 700 // Desktop View - Grid +// Matches Figma: hidden lg:grid md:grid-cols-2 lg:grid-cols-3 gap-6 .journeys_desktop display: none - @media (min-width: 1024px) + @media (min-width: 768px) display: grid - grid-template-columns: repeat(3, 1fr) + grid-template-columns: repeat(2, 1fr) gap: 24px + min-width: 0 + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) +// Matches Figma: rounded-xl p-6 .journey_grid_card border-radius: 12px padding: 24px border: 1px solid rgba(90, 42, 130, 0.4) background: #1D0631 transition: all 0.3s + min-width: 0 &:hover box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) border-color: #ffb91a @@ -891,16 +1149,19 @@ button transform: scale(1.1) .grid_card_title - font-size: 20px - font-weight: 700 + font-size: 17px + font-weight: 700 !important color: white margin-bottom: 8px + line-height: 1.2 + overflow-wrap: break-word .grid_card_description font-size: 14px color: #d1d5db margin-bottom: 16px line-height: 1.5 + overflow-wrap: break-word .grid_card_steps background: rgba(90, 42, 130, 0.2) @@ -908,4 +1169,393 @@ button padding: 16px border: 1px solid rgba(90, 42, 130, 0.4) +// Project Swimlanes Section - Matches Figma: py-8 px-4 sm:px-6 lg:px-8 bg-gray-50 +.project_swimlanes_section + padding: 32px 16px + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding: 32px 24px + @media (min-width: 1024px) + padding: 32px 32px + html.dark & + background: #1D0631 + +// Matches Figma: max-w-7xl mx-auto +.swimlanes_container + max-width: 1280px + margin: 0 auto + +// Matches Figma: grid lg:grid-cols-3 md:grid-cols-2 gap-6 +.swimlanes_grid + display: grid + grid-template-columns: 1fr + gap: 24px + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + @media (min-width: 1024px) + grid-template-columns: repeat(3, 1fr) + +.swimlane_column + display: flex + flex-direction: column + +// Matches Figma: mb-4 +.swimlane_header + margin-bottom: 16px + +// Matches Figma: text-base sm:text-lg md:text-xl font-bold +.swimlane_title + font-size: 16px + font-weight: 700 !important + color: #111827 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px + @media (min-width: 768px) + font-size: 20px + html.dark & + color: #ffffff + +// Matches Figma: space-y-3 +.swimlane_content + display: flex + flex-direction: column + gap: 12px + +// Compact Project Card Styles +.compact_project_card + position: relative + padding: 3px + border-radius: 8px + overflow: hidden + transition: all 0.3s ease + background: transparent + cursor: pointer + &:hover + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + .compact_card_inner + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + +.compact_project_card .compact_card_inner + background: white + border-radius: 8px + padding: 12px + transition: all 0.3s ease + height: 100% + border: 1px solid #e5e7eb + @media (min-width: 640px) + padding: 16px + html.dark & + background: #1D0631 + border-color: #4b5563 + +.compact_project_card .compact_card_header + display: flex + align-items: flex-start + gap: 8px + margin-bottom: 8px + @media (min-width: 640px) + gap: 12px + +.compact_project_card .project_logo + flex-shrink: 0 + width: 32px + height: 32px + @media (min-width: 640px) + width: 40px + height: 40px + img, p + width: 100% !important + height: 100% !important + border-radius: 8px !important + object-fit: cover + border: 1px solid #e5e7eb !important + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important + html.dark & + border-color: #4b5563 !important + p + display: flex !important + align-items: center !important + justify-content: center !important + margin: 0 !important + font-size: 16px !important + font-weight: 600 !important + +.compact_project_card .project-icon-placeholder + width: 100% + height: 100% + display: flex + align-items: center + justify-content: center + background: #f3f4f6 + border-radius: 8px + font-size: 16px + font-weight: 600 + color: #6b7280 + border: 1px solid #e5e7eb + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: #9ca3af + +.compact_project_card .account-avatar + width: 100% + height: 100% + border-radius: 8px + object-fit: cover + border: 1px solid #e5e7eb + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + html.dark & + border-color: #4b5563 + +.compact_project_card .project_info + flex: 1 + min-width: 0 + +.compact_project_card .project_name_row + display: flex + align-items: center + gap: 8px + +.compact_project_card .project_name + color: #111827 + font-size: 14px + font-weight: 600 + text-decoration: none + transition: color 0.3s ease + display: inline-block + line-height: 1.4 + @media (min-width: 640px) + font-size: 16px + &:hover + color: #5A2A82 + html.dark & + color: #ffffff !important + &:hover + color: #ffb91a !important + span + display: inline + +.compact_project_card .trending_icon + flex-shrink: 0 + i + color: #ff6b35 + font-size: 12px + @media (min-width: 640px) + font-size: 14px + +.compact_project_card .compact_card_description + margin-bottom: 12px + p + font-size: 12px + color: #6b7280 + line-height: 1.5 + margin: 0 + overflow: hidden + text-overflow: ellipsis + display: -webkit-box + -webkit-line-clamp: 2 + -webkit-box-orient: vertical + @media (min-width: 640px) + font-size: 14px + html.dark & + color: #9ca3af + +.compact_project_card .compact_card_footer + display: flex + align-items: center + justify-content: space-between + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_left + display: flex + align-items: center + gap: 12px + +.compact_project_card .contributor_info + display: flex + align-items: center + gap: 4px + color: #6b7280 + html.dark & + color: #9ca3af + i + font-size: 10px + @media (min-width: 640px) + font-size: 12px + span + font-size: 10px + @media (min-width: 640px) + font-size: 12px + +.compact_project_card .footer_right + display: flex + align-items: center + +.compact_project_card .language_badge + display: inline-flex + align-items: center + justify-content: center + padding: 2px 8px + border-radius: 9999px + font-size: 10px + font-weight: 500 + line-height: 1.5 + white-space: nowrap + text-align: center + min-width: 40px + background: rgba(255, 185, 26, 0.3) + color: #1D0631 + transition: all 0.3s ease + @media (min-width: 640px) + font-size: 12px + html.dark & + background: #ffb91a + color: #000000 + +// Responsive visibility helpers +.desktop_only + display: none !important + @media (min-width: 768px) + display: block !important + +.mobile_only + display: block + @media (min-width: 768px) + display: none !important + +.mobile_card + width: 100% + +.hidden_card + width: 100% + margin-bottom: 16px + +// Show More Button +.show_more_btn + width: 100% + font-size: 14px + background: white + color: #5A2A82 + border: 2px solid #5A2A82 + padding: 12px 16px + border-radius: 8px + font-weight: 600 + cursor: pointer + transition: all 0.3s ease + margin-top: 8px + display: block + &:hover + background: #5A2A82 + color: white + html.dark & + background: transparent + color: white + border-color: white + &:hover + background: white + color: #5A2A82 + +// Pagination Controls +.pagination_controls + display: flex + align-items: center + justify-content: center + gap: 16px + margin-top: 16px + padding: 12px +.pagination_btn + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background: white + border: 1px solid #e5e7eb + border-radius: 8px + cursor: pointer + transition: all 0.3s ease + color: #5A2A82 + &:hover:not([disabled]) + background: #5A2A82 + color: white + border-color: #5A2A82 + &[disabled] + opacity: 0.4 + cursor: not-allowed + i + font-size: 14px + html.dark & + background: rgba(255, 255, 255, 0.1) + border-color: #4b5563 + color: white + &:hover:not([disabled]) + background: #5A2A82 + border-color: #5A2A82 + +.pagination_info + font-size: 14px + color: #6b7280 + font-weight: 500 + html.dark & + color: #9ca3af + .current_page + color: #5A2A82 + font-weight: 700 + html.dark & + color: #ffb91a + +.swimlane_project_item + display: none + &[data-page="1"] + display: block + +// View All Projects Button - Matches Figma: pt-4 pb-8 +.view_all_container + padding-top: 16px + padding-bottom: 32px + padding-left: 16px + padding-right: 16px + display: flex + justify-content: center + background: #f9fafb + transition: background 0.3s ease + @media (min-width: 640px) + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-left: 32px + padding-right: 32px + html.dark & + background: #1D0631 + +// Matches Figma: flex items-center gap-2 px-6 py-3 bg-[#ffb91a] rounded-lg +.btn_view_all_projects + display: inline-flex + align-items: center + gap: 8px + padding: 12px 24px + background: #ffb91a + color: #000000 + border-radius: 8px + font-weight: 600 + font-size: 14px + text-decoration: none + transition: all 0.3s ease + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + font-size: 16px + &:hover + background: rgba(255, 185, 26, 0.9) + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1) + color: #000000 + html.dark & + color: #ffffff !important + &:hover + color: #ffffff !important + i + font-size: 16px diff --git a/app/assets/stylesheets/home_search.sass b/app/assets/stylesheets/home_search.sass new file mode 100644 index 000000000..b1c0d45e8 --- /dev/null +++ b/app/assets/stylesheets/home_search.sass @@ -0,0 +1,145 @@ +// ======================================== +// Home Page Search Bar - Figma Design +// ======================================== +// Dedicated styles for search input with higher specificity +// to override Bootstrap and other base styles + +// Hero Section Search Container - Nested under .hero_section +.hero_section + .search_form#search_form + margin-bottom: 0 + position: relative + width: 100% + + .search_input_wrapper + position: relative + width: 100% + display: block + + // Search Icon - Figma: w-3.5 h-3.5 (14px) sm:w-5 sm:h-5 (20px) + // Positioned to align with input text baseline + .search_icon + position: absolute + top: 50% + left: 8px + transform: translateY(-60%) + display: block + width: 14px + height: 14px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 10 + transition: color 0.3s ease + text-align: center + font-style: normal + @media (min-width: 640px) + left: 24px + width: 20px + height: 20px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + // Ensure Font Awesome icon centers properly + &::before + display: block + + // Search Input - Figma: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 + input.search_input + width: 100% + padding: 10px 12px 10px 32px + font-size: 11px + font-weight: 400 + line-height: 1.5 + height: auto + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #ffffff + background-clip: padding-box + color: #111827 + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + appearance: none + -webkit-appearance: none + -moz-appearance: none + vertical-align: middle + box-sizing: border-box + + // Placeholder styling - Figma specs + &::placeholder + color: #9ca3af + opacity: 1 + + // Hover state - Figma specs + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) + border-color: #d1d5db + + // Focus state - Figma specs + &:focus + border-color: #0E4B7A + outline: none + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + background: #ffffff + &::placeholder + color: #d1d5db + + // Desktop - Figma: sm:pl-14 sm:pr-4 sm:py-4 + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px + border-radius: 16px + + // Dark mode support + html.dark & + background: #1D0631 + color: #ffffff + border-color: #4b5563 + &::placeholder + color: #6b7280 + opacity: 1 + &:hover + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.1) + border-color: #6b7280 + &:focus + border-color: #2E8B9E + background: #1D0631 + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// ======================================== +// Bootstrap Override - Global Search Form +// ======================================== +// High specificity overrides for Bootstrap and other frameworks +// Ensures Figma padding is applied: pl-8 pr-3 py-2.5, sm:pl-14 sm:pr-4 sm:py-4 +form#search_form + input[type="text"].search_input, + input.search_input + width: 100% !important + padding: 10px 12px 10px 32px !important + font-size: 11px !important + height: auto !important + border: 2px solid #e5e7eb !important + border-radius: 16px !important + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) !important + background: #ffffff !important + color: #111827 !important + box-sizing: border-box !important + @media (min-width: 640px) + padding: 16px 16px 16px 56px !important + font-size: 16px !important + &:focus + border-color: #0E4B7A !important + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) !important + outline: none !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff !important + border-color: #4b5563 !important + &:focus + border-color: #2E8B9E !important + background: #1D0631 !important + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) !important diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index 8efad3364..db0833582 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -267,6 +267,26 @@ &.no-border:hover border-color: color-gradient($LIGHT_GRAY, 80) !important +@mixin purple-theme-button-colors + color: white + background-color: #5A2A82 !important + border-color: #5A2A82 !important + &:hover + background-color: #4A1A72 !important + border-color: #4A1A72 !important + &.no-border:hover + border-color: #4A1A72 !important + +@mixin search-input-colors + background-color: #ffffff + color: #202020 + border-color: #d1d5db + &:focus + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + &::placeholder + color: #9ca3af + @mixin join-now-button-colors color: white background-color: color-gradient($SECONDARY_FOREST_GREEN, 100) !important diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index dd1cf9d9d..cb5fd0334 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -34,8 +34,9 @@ header margin: 0 padding: 0 width: 100% - position: relative - z-index: 1 + position: sticky + top: 0 + z-index: 1000 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -225,7 +226,7 @@ header gap: 12px .logo-div .logo_img - height: 40px + height: 32px !important .company-div .navbar_large_text font-size: 20px @@ -327,7 +328,7 @@ header align-items: flex-start flex-wrap: wrap overflow: visible - z-index: 100 + z-index: 1001 // Override global button styles for navbar .btn border: none !important @@ -369,6 +370,11 @@ header text-decoration: none display: flex align-items: center + .logo_img + height: 20px + width: auto + display: block + object-fit: contain .company-div color: white !important display: flex @@ -604,7 +610,7 @@ header border-top: 1px solid rgba(255, 255, 255, 0.15) padding: 20px 0 position: relative - z-index: 100 + z-index: 1002 overflow: visible order: 2 box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) @@ -861,7 +867,6 @@ header @media (min-width: 320px) and (max-width: 480px) h2 - font-size: 12px !important margin-top: 5px !important .navbar .follow_btn diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index f75db9c1d..6164b1c78 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -1,15 +1,19 @@ #search-dingus @include site-well-color - padding: 7px 0px + padding: 10px 15px line-height: 29px - min-height: 33px + min-height: 50px margin-bottom: 1em + border-radius: 8px #value_select display: inline !important select width: 400px .col-md-5 - width: 43% + width: 50% + display: flex + align-items: stretch + padding: 0 .col-md-4 width: 32% @@ -19,12 +23,75 @@ label.paginate font-size: 13px + + label.sr-only + position: absolute + width: 1px + height: 1px + padding: 0 + margin: -1px + overflow: hidden + clip: rect(0, 0, 0, 0) + white-space: nowrap + border-width: 0 + + .search-input-group + display: flex + align-items: stretch + width: 100% + max-width: 500px + button.btn - height: 34px - border-top: 10px - input - width: 180px - color: #000 + height: auto + border: 1px solid #5A2A82 + line-height: 1 + padding: 8px 16px + display: flex + align-items: center + justify-content: center + + input[type="text"] + flex: 1 + min-width: 250px + max-width: 100% + font-size: 14px + color: #1f2937 + background-color: #ffffff + border: 1px solid #d1d5db + padding: 10px 14px + transition: all 0.15s ease-in-out + border-radius: 12px 0 0 12px + margin: 0 + height: auto + line-height: 1.5 + box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05) + &:hover + border-color: #9ca3af + &:focus + outline: none + border-color: #5A2A82 + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.1) + background-color: #ffffff + &::placeholder + color: #9ca3af + font-size: 14px + + // Dark mode support + .dark & + background-color: rgba(29, 6, 49, 0.5) + input[type="text"] + background-color: #1D0631 + color: #ffffff + border-color: #4b5563 + &:hover + border-color: #6b7280 + &:focus + border-color: #ffb91a + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.1) + background-color: #1D0631 + &::placeholder + color: #6b7280 + select width: 150px label.checkbox diff --git a/app/views/home/_compact_project_card.html.haml b/app/views/home/_compact_project_card.html.haml new file mode 100644 index 000000000..d67b36c4e --- /dev/null +++ b/app/views/home/_compact_project_card.html.haml @@ -0,0 +1,37 @@ +- is_account = project.is_a?(Account) +- project_url = is_account ? account_path(project) : project_path(project) + +.compact_project_card{ data: { project_id: project.id } } + .compact_card_inner + .compact_card_header + .project_logo + = link_to project_url do + - if is_account + = image_tag(avatar_img_path(project), class: 'account-avatar') + - elsif project.logo.present? + = image_tag(project.logo.attachment.url(:small), alt: project.name) + - else + .project-icon-placeholder + = project.name[0].upcase + .project_info + .project_name_row + = link_to project_url, class: 'project_name' do + %span= h(truncate(project.name, length: 30)) + - if !is_account && project.updated_at > 7.days.ago + %span.trending_icon + %i.fa.fa-arrow-trend-up + .compact_card_description + - description = is_account ? (project.try(:about) || '') : (project.description || '') + %p= h(truncate(description, length: 80)) + .compact_card_footer + .footer_left + .contributor_info + %i.fa.fa-users + - count = is_account ? (project.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0) : (project.best_analysis&.headcount || 0) + %span= count + .footer_right + - if is_account + - language_name = project.best_account_analysis&.account_analysis_fact&.primary_language&.nice_name || 'N/A' + - else + - language_name = project.best_analysis&.main_language&.nice_name || 'NA' + %span.language_badge= language_name diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml index 4f4cb2895..d87f62121 100644 --- a/app/views/home/_top_contributors.html.haml +++ b/app/views/home/_top_contributors.html.haml @@ -7,27 +7,34 @@ %p.card_subtitle= t('.most_active_developers') .contributors_list - - @top_contributors&.each_with_index do |contributor, index| - .contributor_item{class: "contributor-rank-#{index + 1}"} + - @home.most_active_contributors&.each_with_index do |contributor, index| + = link_to account_path(contributor), class: 'contributor_item' do .contributor_avatar - = link_to account_path(contributor) do - = gravatar_tag(contributor.email, size: 40, class: 'avatar_image', alt: contributor.name) + - begin + = image_tag avatar_img_path(contributor, 40), class: 'avatar_img', alt: contributor.name, onerror: "this.onerror=null; this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2240%22 height=%2240%22 viewBox=%220 0 40 40%22%3E%3Crect width=%2240%22 height=%2240%22 rx=%226%22 fill=%22%239CA3AF%22/%3E%3Ctext x=%2250%25%22 y=%2250%25%22 dominant-baseline=%22middle%22 text-anchor=%22middle%22 font-family=%22Arial%22 font-size=%2218%22 font-weight=%22600%22 fill=%22white%22%3E#{h(contributor.name[0].upcase)}%3C/text%3E%3C/svg%3E';" + - rescue + %div.avatar_placeholder{style: "width: 40px; height: 40px; border-radius: 12px; background: #9CA3AF; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 18px;"} + = contributor.name[0].upcase + .contributor_info - .contributor_name_rank - %h4.contributor_name - = link_to contributor.name, account_path(contributor), class: 'contributor_link' - %span.contributor_rank= "##{index + 1}" + .contributor_name_row + %span.contributor_name= h(contributor.name) + %span.contributor_rank= "Rank ##{index + 1}" .contributor_stats - %span.stat_item - %i.fa.fa-code-fork - = number_with_delimiter(contributor.commits_count || 0) - %span.stat_item - %i.fa.fa-folder - = "#{contributor.projects_count || 0} projects" + - commits = contributor.best_account_analysis&.account_analysis_fact&.thirty_day_commits || 0 + - commits_by_project = contributor.best_account_analysis&.account_analysis_fact&.commits_by_project + - project_count = commits_by_project.present? ? commits_by_project.split(',').length : 0 + %span.stat_commits + %i.fa.fa-dot-circle-o + = number_with_delimiter(commits) + %span.stat_projects + %i.fa.fa-code + = "#{project_count} projects" + .contributor_arrow %i.fa.fa-chevron-right - .view_all_link - = link_to accounts_path, class: 'btn btn_view_all' do + .view_all_footer + = link_to accounts_path, class: 'view_all_link' do %i.fa.fa-users - = t('.view_all_contributors') + %span= t('.view_all_contributors') diff --git a/app/views/home/_top_lists.html.haml b/app/views/home/_top_lists.html.haml index 6630636ee..e0d18cc2b 100644 --- a/app/views/home/_top_lists.html.haml +++ b/app/views/home/_top_lists.html.haml @@ -1,56 +1,85 @@ -.col-md-3.top_ten - - if device? - %h2.most_popular_projects - %button.collapsed#no-background{"aria-controls" => "collapseOne", "aria-expanded" => "false", "data-target" => "#collapseOne", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase1 - =t('.popular_projects') - .collapse#collapseOne - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } - - else - %h2.most_popular_projects= t('.popular_projects') - = render partial: 'content', locals: { popular_items: @home.most_popular_projects, - commit: 'user', colors: 'popular', required: 'most_popular_projects', - max: @home.most_popular_projects.map(&:user_count).max } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_projects - %button.collapsed{"aria-controls" => "collapseTwo", "aria-expanded" => "false", "data-target" => "#collapseTwo", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase2 - =t('.active_projects') - .collapse#collapseTwo - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } - - else - %h2.most_active_projects= t('.active_projects') - = render partial: 'content', locals: { popular_items: @home.most_active_projects, - commit: 'commit', colors: 'active', required: 'most_active_projects', max: @home.commit_count.max } -.col-md-3.top_ten.last - - if device? - %h2.most_recent_projects - %button.collapsed#no-background{"aria-controls" => "collapseThree", "aria-expanded" => "false", "data-target" => "#collapseThree", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase3 - =t('.recent_projects') - .collapse#collapseThree - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } - - else - %h2.most_recent_projects=t('.recent_projects') - = render partial: 'content', locals: { popular_items: @home.most_recent_projects, - commit: 'since', colors: 'recent', required: 'most_recent_projects', - max: @home.most_recent_projects.size } -.col-md-3.top_ten.middle - - if device? - %h2.most_active_contributors - %button.collapsed{"aria-controls" => "collapseFour", "aria-expanded" => "false", "data-target" => "#collapseFour", "data-toggle" => "collapse"} - %span.toggle-icon.glyphicon.glyphicon-chevron-down.pull-right#collpase4 - =t('.active_contributors') - .collapse#collapseFour - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } - - else - %h2.most_active_contributors=t('.active_contributors') - = render partial: 'content', locals: { popular_items: @home.most_active_contributors, - commit: 'commit', colors: 'contributor', required: 'most_active_contributors', max: @home.account_analysis_count.max } \ No newline at end of file +/ Project Swimlanes Section - New Design +.project_swimlanes_section + .swimlanes_container + .swimlanes_grid + / Most Active Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.active_projects') + .swimlane_content{ data: { swimlane: 'active' } } + / Desktop: Show all projects + - @home.most_active_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_active_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_active_projects.size > 2 + - @home.most_active_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'active', target: 'active', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Most Popular Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.popular_projects') + .swimlane_content{ data: { swimlane: 'popular' } } + / Desktop: Show all projects + - @home.most_popular_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_popular_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_popular_projects.size > 2 + - @home.most_popular_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'popular', target: 'popular', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + / Recently Added Projects Column + .swimlane_column + .swimlane_header + %h2.swimlane_title= t('.recent_projects') + .swimlane_content{ data: { swimlane: 'recent' } } + / Desktop: Show all projects + - @home.most_recent_projects.each_with_index do |project, index| + .desktop_only + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Show first 2 projects initially + - @home.most_recent_projects.first(2).each do |project| + .mobile_only.mobile_card + = render partial: 'compact_project_card', locals: { project: project } + + / Mobile: Hidden cards (to be shown on click) + - if @home.most_recent_projects.size > 2 + - @home.most_recent_projects.drop(2).each do |project| + .mobile_only.mobile_card.hidden_card{ style: 'display: none;' } + = render partial: 'compact_project_card', locals: { project: project } + + / Show More Button (mobile only) + %button.show_more_btn.mobile_only{ type: 'button', data: { swimlane: 'recent', target: 'recent', expanded: 'false', show_text: t('.show_more'), hide_text: t('.show_less') } } + = t('.show_more') + + + / View All Projects Button + .view_all_container + = link_to projects_path, class: 'btn_view_all_projects' do + = t('.view_all_projects') + %i.fa.fa-arrow-right diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 473afba49..a9cfb02b6 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,5 +1,7 @@ - content_for(:html_title) { t('.page_title') } -- content_for(:javascript) { javascript_include_tag 'rotating_stats' } +- content_for(:javascript) do + = javascript_include_tag 'rotating_stats' + = javascript_include_tag 'project_swimlanes' / Hero Section .hero_section @@ -63,13 +65,13 @@ / Top Contributors and What's New Section .content_section .container - .row + .content_grid / Top Contributors - 35% - .col-md-4.col-sm-12.contributors_column + .contributors_column = render partial: '/home/top_contributors' / What's New and Join Now - 65% - .col-md-8.col-sm-12.whats_new_column + .whats_new_column .whats_new_join_stack = render partial: '/home/whats_new' = render partial: '/home/join_now_home' @@ -77,5 +79,5 @@ / User Journeys Section = render partial: '/home/user_journeys' -.landing.col-md-12.col-sm-12.col-xs-12.last_section - = home_top_lists +/ Project Swimlanes Section += home_top_lists diff --git a/app/views/shared/search_dingus/_search_bar.html.haml b/app/views/shared/search_dingus/_search_bar.html.haml index acc25067e..ad48ad78a 100644 --- a/app/views/shared/search_dingus/_search_bar.html.haml +++ b/app/views/shared/search_dingus/_search_bar.html.haml @@ -8,8 +8,10 @@ - else .col-md-5.col-sm-5.col-xs-5.no_padding - %label #{t('.search_text')}   - = text_field_tag :query, params[:query] + %label.sr-only= t('.search_text') + .search-input-group + = text_field_tag :query, params[:query], + placeholder: t('.search_text') - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-refresh + %button.btn.btn-refresh{ type: 'Submit' } + %i.icon-search diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 3fe5a83df..b35d3c3ee 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,6 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js rotating_stats.js] + api/vulnerability.sass api/vulnerability.js + rotating_stats.js project_swimlanes.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 075a8dbc6..3607a7749 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -5,7 +5,6 @@ en: discover_message: 'Discover, Track and Compare Open Source' worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' - whats_new: "What's New" counting: 'Counting %{count} lines of code' indexing: 'Indexing %{count} open source projects' connecting: 'Connecting %{count} open source contributors' @@ -26,12 +25,67 @@ en: active_projects: 'Most Active Projects' active_contributors: 'Most Active Contributors' recent_projects: 'Recently Added Projects' + show_more: 'Show More' + show_less: 'Show Less' + view_all_projects: 'View All Projects' join_now_home: - claim: 'Claim' - contributions: 'your contributions' - manage: 'Manage' - projects_data: "your project's data" - highlight: 'Highlight' - use: 'your use of FOSS' join_now: 'Join Now' + become_part_community: 'Become part of the community' + track_contributions_title: 'Track Your Contributions' + track_contributions_desc: 'Showcase your open source portfolio and contributions' + gain_recognition_title: 'Gain Recognition' + gain_recognition_desc: 'Build your developer profile with verified achievements' + create_account: 'Create Your Account' + + whats_new: + whats_new: "What's New" + latest_updates_reports: 'Latest updates and reports' + ossra_report_alt: 'Open Source Security and Risk Analysis Report' + + top_contributors: + top_contributors: 'Top Contributors' + most_active_developers: 'Most active contributors' + view_all_contributors: 'View All Contributors' + view_all: 'View all' + + user_journeys: + your_journey_with_openhub: 'Your Journey with Open Hub' + explore_ways_description: 'Explore the many ways you can discover, track, and contribute to open source projects' + key_steps: 'Key Steps' + + discover_compare_title: 'Discover & Compare Solutions' + discover_compare_desc: 'Find the perfect open source solution by comparing features, project activity, and community engagement side-by-side.' + discover_compare_step1: 'Compare projects side-by-side' + discover_compare_step2: 'Review activity metrics' + discover_compare_step3: 'Analyze community interest' + + assess_health_title: 'Assess Project Health' + assess_health_desc: 'Determine if a project is safe to adopt by analyzing commit frequency, number of contributors, and growth trends.' + assess_health_step1: 'Analyze commit history trends' + assess_health_step2: 'Review year-over-year growth' + assess_health_step3: 'Track number of contributors' + + manage_portfolio_title: 'Manage Your Developer Portfolio' + manage_portfolio_desc: 'Build a unified portfolio of your open source work by claiming your contributions across all repositories.' + manage_portfolio_step1: 'Claim your commits' + manage_portfolio_step2: 'Earn "Kudos" and rankings' + manage_portfolio_step3: 'Track your global rank' + + analyze_code_title: 'Analyze Code Composition' + analyze_code_desc: 'Get deep insights into the codebase architecture, including language breakdown, lines of code, and comment ratios.' + analyze_code_step1: 'View language distribution' + analyze_code_step2: 'Check comment-to-code ratios' + analyze_code_step3: 'Estimate effort (COCOMO model)' + + check_security_title: 'Check Security Vulnerabilities' + check_security_desc: 'Verify the security track record of a project by checking known vulnerabilities and confidence scores before you adopt.' + check_security_step1: 'View Security Confidence Index' + check_security_step2: 'Check reported vulnerabilities (CVEs)' + check_security_step3: 'Assess risk level' + + identify_opportunities_title: 'Identify Contribution Opportunities' + identify_opportunities_desc: 'Find projects that need your skills and identify the best repositories to start your open source journey.' + identify_opportunities_step1: 'Find beginner-friendly projects' + identify_opportunities_step2: 'Locate source repositories (GitHub/GitLab)' + identify_opportunities_step3: 'Track your contribution impact' From 68dd25646fbcae35f1999224fb4efca3f1e36f67 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 16 Mar 2026 17:04:58 +0530 Subject: [PATCH 23/53] Fix testcases --- app/views/projects/show.html.haml | 9 +++++++-- app/views/projects/show/_header_redesign.html.haml | 9 ++++++++- config/locales/projects.en.yml | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 879072ea9..4c94e2bdc 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -52,7 +52,7 @@ .card-content %h3= t('.project_summary') - if @project.description.present? - %p= simple_format(@project.description.strip_tags) + = simple_format(@project.description.strip_tags) - else %p = t('.no_description') @@ -84,6 +84,11 @@ - if @project.edit_authorized? = link_to t('.add_tags'), project_tags_path(@project) + - if @project.project_badges.active.any? + %section#project_badges + = show_badges + = more_badges_link + / In a Nutshell .gradient-card .card-content @@ -213,7 +218,7 @@ - if @project.active_managers.present? = link_to "#{@project.active_managers.count} managers", project_managers_path(@project) - else - = link_to t('.become_first', what: @project.name), new_project_manager_path(@project) + = link_to t('.become_first_manager', what: @project.name), new_project_manager_path(@project) / Mobile: Collapsible accordion cards .mobile-accordion diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml index 6283af5ef..3c7cd197a 100644 --- a/app/views/projects/show/_header_redesign.html.haml +++ b/app/views/projects/show/_header_redesign.html.haml @@ -69,7 +69,14 @@ %line{ x1: "4", y1: "22", x2: "4", y2: "15" } = t('.report_duplicate') - %p.project-description{ itemprop: 'description' } + - if current_user_is_admin? + = link_to oh_admin_project_jobs_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %rect{ x: "3", y: "7", width: "18", height: "13", rx: "2", ry: "2" } + %path{ d: "M16 3v4M8 3v4M3 11h18" } + = t('projects.header.job') + + .project-description{ itemprop: 'description' } - if @project.description.present? = truncate(@project.description.strip_tags, length: 200) - else diff --git a/config/locales/projects.en.yml b/config/locales/projects.en.yml index 06c7de8b9..aaf00da22 100644 --- a/config/locales/projects.en.yml +++ b/config/locales/projects.en.yml @@ -120,7 +120,7 @@ en: add_code_location_link: "Add code location" similar_projects_label: "Similar Projects" managers_label: "Managers" - become_first_manager: "Become the first manager of %{what}" + become_first_manager: "Become the first manager for %{what}" news: "News" sharing_widgets: "Sharing Widgets" related_projects: "Related Projects" From 55ab4172f593857164d5d06d1218d0449657fa55 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Fri, 20 Mar 2026 21:54:52 +0530 Subject: [PATCH 24/53] rubocop fix --- app/decorators/analysis/commit_history_chart.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/decorators/analysis/commit_history_chart.rb b/app/decorators/analysis/commit_history_chart.rb index 6541fb0c5..3094a2c25 100644 --- a/app/decorators/analysis/commit_history_chart.rb +++ b/app/decorators/analysis/commit_history_chart.rb @@ -10,8 +10,8 @@ def initialize(analysis) def data chart_data = series_and_range_data(@defaults) - .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) - .deep_merge(chart_watermark) + .deep_merge(ANALYSIS_CHARTS_OPTIONS['commits_history_auxillaries']) + .deep_merge(chart_watermark) apply_commits_palette(chart_data) end From 427139f9e4e42334f8f4bf2bc6bc6cbea3bd031c Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Tue, 17 Mar 2026 23:31:26 +0530 Subject: [PATCH 25/53] Added UI fix for explore projects --- app/assets/javascripts/explore.js.coffee | 26 +++++- app/assets/javascripts/search_dingus.js | 34 ++++---- app/assets/stylesheets/explore.sass | 83 +++++++++++++++---- app/views/explore/_projects_sidebar.html.haml | 20 ++--- 4 files changed, 110 insertions(+), 53 deletions(-) diff --git a/app/assets/javascripts/explore.js.coffee b/app/assets/javascripts/explore.js.coffee index 530d0b7b4..0e3ee7f1e 100644 --- a/app/assets/javascripts/explore.js.coffee +++ b/app/assets/javascripts/explore.js.coffee @@ -1,3 +1,16 @@ +$(document).on 'click', '.tag-search-btn', -> + tag = $(this).siblings('.tag-input').val() + if tag.length > 0 + window.location.href = '/tags?names=' + encodeURIComponent(tag) + return false + +$(document).on 'keydown', '.tag-input', (e) -> + if e.which == 13 + e.preventDefault() + tag = $(this).val() + if tag.length > 0 + window.location.href = '/tags?names=' + encodeURIComponent(tag) + return false App.Explore = init: () -> return if $('#explore_projects_page').length == 0 && $('.explore-projects-page').length == 0 @@ -13,16 +26,16 @@ App.Explore = $(this).siblings('.icon-search').trigger('click') return false - $('.similar_projects .icon-search').click (e) -> + $('.similar_projects .icon-search, .similar-card .search-icon-btn').click (e) -> $(this).parents('form:first').trigger('submit') $('form[rel=similar_project_jump]').submit (e) -> - projectId = $("#project").val() + projectId = $(this).find('input[name="project"]').val() if projectId != '' e.preventDefault() window.location.href = "/p/#{projectId.toLowerCase()}/similar" else - $('span.error').removeClass('hidden') + $(this).find('span.error').removeClass('hidden') false $('form[rel=sort_filter] select').change () -> @@ -30,6 +43,13 @@ App.Explore = $(this).attr('disabled', 'disabled') $(this).parents('form').attr('action', document.location).submit() + # Discover More collapsible toggle (mobile/tablet) + $(document).on 'click', '.discover-toggle', (e) -> + content = $(this).siblings('.discover-content') + content.toggleClass('show') + chevron = $(this).find('.chevron') + chevron.text(if content.hasClass('show') then '▲' else '▼') + # Language filter dropdown toggle $(document).on 'click', '.language-toggle', (e) -> e.stopPropagation() diff --git a/app/assets/javascripts/search_dingus.js b/app/assets/javascripts/search_dingus.js index 86c7a9120..76f8c255c 100644 --- a/app/assets/javascripts/search_dingus.js +++ b/app/assets/javascripts/search_dingus.js @@ -19,16 +19,13 @@ var ohloh = (function builder($) { ui: { init: function(){ var $scope = $('.ux-dropdown'); - var $search_text_field = $scope.find('.text'); + var $search_text_field = $scope.find('.text').add($scope.siblings('.header-search-input')); var dropdownHead = $('button span.selection, a span.selection', $scope); - var getSelectedValue = function() { - return dropdownHead.first().attr('val'); - }; $('.dropdown-menu .dropdown-item', $scope).click(function() { var selectedText = $(this).attr('val') dropdownHead.attr('val',selectedText) dropdownHead.html( $(this).html() ) - dropdownHead.parents('form').attr('action',selectedText) + // Do NOT set form action here; URL will be built on submit // Update active class $('.dropdown-menu .dropdown-item', $scope).removeClass('active') $(this).addClass('active') @@ -128,34 +125,33 @@ var ohloh = (function builder($) { $search_text_field.keydown(function(e){ if(e.which == 13) { e.preventDefault(); - $(this).siblings('.submit').trigger('click'); + $(this).siblings('.submit, .header-search-btn').trigger('click'); return false; } }); - $search_text_field.siblings('.submit').click(function(e) { - var search_term = $.trim($search_text_field.val()); + $search_text_field.siblings('.submit, .header-search-btn').click(function(e) { + var $btn = $(this); + var $field = $btn.siblings('.text, .header-search-input'); + var $local_scope = $btn.closest('form').find('.ux-dropdown'); + var localDropdownHead = $('button span.selection, a span.selection', $local_scope); + var search_term = $.trim($field.val()); if(search_term.length > 0) { e.preventDefault(); - var section = getSelectedValue(), - url_format = "/#{activator}?"+$search_text_field.attr('name')+"=#{query}"; + var section = localDropdownHead.first().attr('val'); + var url = "/" + section + "?query=" + encodeURIComponent(search_term); - var data = { - activator: section, - query: search_term - }; - - if($search_text_field.attr('name') == 's') - url_format += "&ref=Open%20Hub"; + if($field.attr('name') == 's') + url += "&ref=Open%20Hub"; if(window.location.href.indexOf("sort=") > -1) { sort_value = window.location.href.split('sort=')[1].split('&')[0]; - url_format += "&sort=" + sort_value; + url += "&sort=" + sort_value; } - window.location.href = url_format._f(data); + window.location.href = url; } return false; diff --git a/app/assets/stylesheets/explore.sass b/app/assets/stylesheets/explore.sass index 72dffa6f9..03615a878 100644 --- a/app/assets/stylesheets/explore.sass +++ b/app/assets/stylesheets/explore.sass @@ -37,6 +37,12 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) padding: 0 16px margin-bottom: 24px + @media (min-width: 640px) + padding: 0 24px + + @media (min-width: 1024px) + padding: 0 32px + // Main Layout Container (sidebar + content grid) .explore-main-container max-width: 1280px @@ -46,6 +52,12 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) grid-template-columns: 280px 1fr gap: 32px + @media (min-width: 640px) + padding: 0 24px + + @media (min-width: 1024px) + padding: 0 32px + @media (max-width: 1024px) grid-template-columns: 1fr gap: 24px @@ -402,6 +414,9 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) .explore-header margin-bottom: 32px + @media (max-width: 640px) + margin-bottom: 24px + .explore-header-top display: flex justify-content: space-between @@ -410,7 +425,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) margin-bottom: 24px flex-wrap: wrap - @media (max-width: 768px) + @media (max-width: 1024px) flex-direction: column .explore-title @@ -424,18 +439,27 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) html.dark & color: #ffffff !important - @media (max-width: 768px) + @media (max-width: 640px) font-size: 30px !important .explore-header-actions display: flex - gap: 16px + gap: 12px align-items: center flex-wrap: wrap - @media (max-width: 768px) + @media (max-width: 1024px) width: 100% + @media (max-width: 640px) + flex-direction: column + align-items: stretch + + .header-search-form + @media (max-width: 1024px) + max-width: none + flex: 1 + .btn-add-project display: inline-flex align-items: center @@ -548,7 +572,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) flex-wrap: wrap gap: 16px - @media (max-width: 768px) + @media (max-width: 640px) flex-direction: column align-items: flex-start @@ -669,7 +693,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) .projects-table-header display: grid - grid-template-columns: 50px 50px minmax(80px, 1fr) minmax(80px, 150px) 80px 80px + grid-template-columns: 50px 50px minmax(80px, 3fr) minmax(80px, 3fr) 80px 80px gap: 16px padding: 16px 24px background: $bg-light @@ -680,10 +704,12 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) background: $bg-table-dark border-color: #374151 + // Mobile: proportional 12-unit grid (rank:1 icon:1 name:4 claimed:2 pai:2 hotness:2) + // matches Figma col-span values — name always gets 33% regardless of screen width @media (max-width: 768px) - grid-template-columns: 40px 40px 1fr 80px 60px 60px - gap: 8px - padding: 12px 16px + grid-template-columns: 1fr 1fr 4fr 2fr 2fr 2fr + gap: 6px + padding: 10px 12px .header-cell font-size: 12px @@ -707,7 +733,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) .project-row display: grid - grid-template-columns: 50px 50px minmax(80px, 1fr) minmax(80px, 150px) 80px 80px + grid-template-columns: 50px 50px minmax(80px, 3fr) minmax(80px, 3fr) 80px 80px gap: 16px padding: 16px 24px border-bottom: 1px solid $border-gray @@ -723,15 +749,21 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) html.dark & background: rgba($bg-dark, 0.5) + // Mobile: proportional 12-unit grid (rank:1 icon:1 name:4 claimed:2 pai:2 hotness:2) + // matches Figma col-span values — name always gets 33% regardless of screen width @media (max-width: 768px) - grid-template-columns: 40px 40px 1fr 80px 60px 60px - gap: 8px - padding: 12px 16px + grid-template-columns: 1fr 1fr 4fr 2fr 2fr 2fr + gap: 6px + padding: 10px 12px .table-cell display: flex align-items: center font-size: 14px + min-width: 0 + + @media (max-width: 768px) + font-size: 12px &.rank-col justify-content: center @@ -742,8 +774,14 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) &.name-col min-width: 0 + &.claimed-col + min-width: 0 + &.pai-col justify-content: center + transform-origin: center + @media (max-width: 768px) + transform: scale(0.8) &.hotness-col justify-content: flex-end @@ -757,7 +795,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) color: $primary-yellow @media (max-width: 768px) - font-size: 14px + font-size: 13px .project-icon width: 40px @@ -772,8 +810,8 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) background: linear-gradient(to bottom right, #374151, #1f2937) @media (max-width: 768px) - width: 32px - height: 32px + width: 28px + height: 28px .project-name-link color: #3182ce @@ -783,6 +821,8 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) overflow: hidden text-overflow: ellipsis white-space: nowrap + display: block + width: 100% html.dark & color: #60a5fa !important @@ -790,6 +830,9 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) &:hover text-decoration: underline + @media (max-width: 768px) + font-size: 12px + .org-link color: #3182ce font-size: 14px @@ -797,6 +840,8 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) overflow: hidden text-overflow: ellipsis white-space: nowrap + display: block + width: 100% html.dark & color: #60a5fa !important @@ -804,6 +849,9 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) &:hover text-decoration: underline + @media (max-width: 768px) + font-size: 12px + .no-org color: $text-light font-size: 14px @@ -811,6 +859,9 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) html.dark & color: #9ca3af + @media (max-width: 768px) + font-size: 12px + .hotness-score font-weight: 600 color: $text-dark diff --git a/app/views/explore/_projects_sidebar.html.haml b/app/views/explore/_projects_sidebar.html.haml index f9bcefb2c..8b88904ae 100644 --- a/app/views/explore/_projects_sidebar.html.haml +++ b/app/views/explore/_projects_sidebar.html.haml @@ -23,18 +23,8 @@ - @tags.each do |tag| = link_to h(tag[0]), tags_path(names: tag[0]), class: 'tag-item', data: { weight: tag[1] } - else - .tag-item 3d - .tag-item C++ - .tag-item collection - .tag-item cross-platform - .tag-item development - .tag-item framework - .tag-item graphics - .tag-item linux - .tag-item php - .tag-item python - .tag-item web - .tag-item windows + - %w[3d C++ collection cross-platform development framework graphics linux php python web windows].each do |tag| + = link_to tag, tags_path(names: tag), class: 'tag-item' / Custom Tag Input with icon inside .tag-input-wrapper @@ -50,9 +40,9 @@ .card-body.compare-body %form.compare-form{ action: '/p/_compare', method: :get } .compare-inputs - = text_field_tag 'project1', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' - = text_field_tag 'project2', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' - = text_field_tag 'project3', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + = text_field_tag 'project_0', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + = text_field_tag 'project_1', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' + = text_field_tag 'project_2', '', placeholder: t('.enter_project_name', default: 'Enter a project name'), class: 'compare-input' %button.btn-compare{ type: 'submit' }= t('.compare_btn', default: 'Compare') From 722d298d6278223269290b2244c028ba4c5aed42 Mon Sep 17 00:00:00 2001 From: Vaibhav Goyal Date: Wed, 18 Mar 2026 12:41:44 +0530 Subject: [PATCH 26/53] OTWO-7596 logo layout fix --- app/assets/stylesheets/projects.sass | 30 +++++++++++++++---- .../home/_compact_project_card.html.haml | 4 ++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index da06c6e7b..e2c715a1e 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -726,7 +726,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) text-decoration: underline html.dark & - color: $dark-primary-color + color: $dark-primary-color !important .analyzed-date font-style: italic @@ -761,17 +761,37 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) flex-shrink: 0 width: 48px height: 48px - background-color: $bg-color border-radius: 12px - border: 1px solid $border-color display: flex align-items: center justify-content: center font-size: 24px + overflow: hidden + background-color: $bg-color + border: 1px solid $border-color box-shadow: 0 1px 2px rgba(0,0,0,0.05) html.dark & background-color: $dark-bg-color border-color: $dark-border-color + &.has-logo + background-color: transparent + border: none + box-shadow: none + html.dark & + background-color: transparent + border: none + img + width: 100% + height: 100% + object-fit: contain + .project-icon-letter + font-size: 22px + font-weight: 700 + color: $primary-color + text-transform: uppercase + user-select: none + html.dark & + color: $dark-primary-color @media (min-width: 640px) width: 56px @@ -813,7 +833,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) &:hover text-decoration: underline html.dark & - color: $dark-primary-color + color: $dark-primary-color !important .licenses .license-label @@ -850,7 +870,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) background-color: rgba(255, 185, 26, 0.3) html.dark & background-color: rgba(255, 185, 26, 0.15) - color: $dark-primary-color + color: $dark-primary-color !important border-color: rgba(255, 185, 26, 0.2) &:hover background-color: rgba(255, 185, 26, 0.25) diff --git a/app/views/home/_compact_project_card.html.haml b/app/views/home/_compact_project_card.html.haml index d67b36c4e..a7370974f 100644 --- a/app/views/home/_compact_project_card.html.haml +++ b/app/views/home/_compact_project_card.html.haml @@ -9,7 +9,9 @@ - if is_account = image_tag(avatar_img_path(project), class: 'account-avatar') - elsif project.logo.present? - = image_tag(project.logo.attachment.url(:small), alt: project.name) + = image_tag(project.logo.attachment.url(:small), alt: project.name, onerror: "this.style.display='none'; this.nextElementSibling.style.display='flex';") + .project-icon-placeholder{ style: 'display:none' } + = project.name[0].upcase - else .project-icon-placeholder = project.name[0].upcase From 34aef9d684aee59c84a660028d956317a57e3d68 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Fri, 20 Mar 2026 22:21:34 +0530 Subject: [PATCH 27/53] rotating file deletion --- app/assets/javascripts/rotating_stats.js | 29 ------------------------ 1 file changed, 29 deletions(-) delete mode 100644 app/assets/javascripts/rotating_stats.js diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js deleted file mode 100644 index 39a368b7b..000000000 --- a/app/assets/javascripts/rotating_stats.js +++ /dev/null @@ -1,29 +0,0 @@ -// Rotating Stats Animation for Homepage -document.addEventListener('DOMContentLoaded', function() { - var globalStats = document.getElementById('global_statistics'); - - if (!globalStats) return; - - var statElements = globalStats.querySelectorAll('p'); - - if (statElements.length === 0) return; - - var currentIndex = 0; - - // Show first stat initially - statElements[0].classList.remove('hide'); - - // Rotate stats every 2 seconds - setInterval(function() { - // Hide current stat - statElements[currentIndex].classList.add('hide'); - - // Move to next stat - currentIndex = (currentIndex + 1) % statElements.length; - - // Show next stat after a brief delay - setTimeout(function() { - statElements[currentIndex].classList.remove('hide'); - }, 300); - }, 2000); -}); From 7d8335cc6ba20dcbe19892bf46ae506bd5060d41 Mon Sep 17 00:00:00 2001 From: Kumari Niharika Date: Mon, 2 Feb 2026 14:12:58 +0530 Subject: [PATCH 28/53] OTWO-7546 added layout for home page assets precompile issue and test cases fix Css fix for after sign in user --- app/assets/javascripts/dropdown_handler.js | 71 ++ app/assets/javascripts/mobile_menu.js | 86 ++ app/assets/javascripts/rotating_stats.js | 29 + app/assets/javascripts/theme_toggle.js | 75 ++ app/assets/stylesheets/dark_theme.sass | 150 +++ app/assets/stylesheets/footer.sass | 241 +++-- app/assets/stylesheets/home.sass | 995 +++++++++++++++--- app/assets/stylesheets/oh-styles.sass | 17 +- app/assets/stylesheets/page.sass | 888 +++++++++++++--- app/views/home/_join_now_home.html.haml | 44 +- app/views/home/_top_contributors.html.haml | 33 + app/views/home/_user_journeys.html.haml | 272 +++++ app/views/home/_whats_new.html.haml | 11 +- app/views/home/index.html.haml | 102 +- app/views/layouts/partials/_footer.html.haml | 81 +- app/views/layouts/partials/_mast.html.haml | 131 ++- config/initializers/assets.rb | 2 +- config/locales/home.en.yml | 11 + test/controllers/home_controller_test.rb | 8 +- test/controllers/licenses_controller_test.rb | 2 +- .../organizations_controller_test.rb | 2 +- test/controllers/projects_controller_test.rb | 2 +- 22 files changed, 2764 insertions(+), 489 deletions(-) create mode 100644 app/assets/javascripts/dropdown_handler.js create mode 100644 app/assets/javascripts/mobile_menu.js create mode 100644 app/assets/javascripts/rotating_stats.js create mode 100644 app/assets/javascripts/theme_toggle.js create mode 100644 app/assets/stylesheets/dark_theme.sass create mode 100644 app/views/home/_top_contributors.html.haml create mode 100644 app/views/home/_user_journeys.html.haml diff --git a/app/assets/javascripts/dropdown_handler.js b/app/assets/javascripts/dropdown_handler.js new file mode 100644 index 000000000..f9aea4967 --- /dev/null +++ b/app/assets/javascripts/dropdown_handler.js @@ -0,0 +1,71 @@ +// Dropdown menu handler for logged user menu +(function($) { + 'use strict'; + + function initDropdown() { + var $dropdown = $('#logged_user_menu'); + + if ($dropdown.length === 0) { + return; // Dropdown not found (user not logged in) + } + + var $toggle = $dropdown.find('.dropdown-toggle'); + var $menu = $dropdown.find('.dropdown-menu'); + + // Remove any existing event handlers + $toggle.off('click.userDropdown'); + $(document).off('click.userDropdown keydown.userDropdown'); + $menu.find('a').off('click.userDropdown'); + + // Toggle dropdown on click + $toggle.on('click.userDropdown', function(e) { + e.preventDefault(); + e.stopPropagation(); + + var wasOpen = $dropdown.hasClass('open'); + + // Close all dropdowns first + $('.dropdown').removeClass('open'); + + // Toggle this dropdown + if (!wasOpen) { + $dropdown.addClass('open'); + } + + return false; + }); + + // Close dropdown when clicking outside + $(document).on('click.userDropdown', function(e) { + var $target = $(e.target); + if (!$dropdown.is($target) && $dropdown.has($target).length === 0) { + $dropdown.removeClass('open'); + } + }); + + // Close dropdown when clicking a menu item + $menu.find('a').on('click.userDropdown', function() { + setTimeout(function() { + $dropdown.removeClass('open'); + }, 150); + }); + + // Close dropdown on ESC key + $(document).on('keydown.userDropdown', function(e) { + if (e.keyCode === 27) { + $dropdown.removeClass('open'); + } + }); + } + + // Initialize when DOM is ready + $(document).ready(function() { + initDropdown(); + }); + + // Re-initialize on Turbolinks page change + $(document).on('page:change', function() { + initDropdown(); + }); + +})(jQuery); diff --git a/app/assets/javascripts/mobile_menu.js b/app/assets/javascripts/mobile_menu.js new file mode 100644 index 000000000..d57090864 --- /dev/null +++ b/app/assets/javascripts/mobile_menu.js @@ -0,0 +1,86 @@ +// Mobile Menu Toggle Functionality +var MobileMenu = { + init: function() { + console.log('MobileMenu: Initializing...'); + this.bindEvents(); + }, + + toggleMenu: function() { + console.log('MobileMenu: Toggle clicked!'); + var mobileMenu = document.getElementById('mobile-menu'); + + if (mobileMenu) { + if (mobileMenu.classList.contains('show')) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } else { + mobileMenu.classList.add('show'); + console.log('MobileMenu: Menu opened'); + } + } else { + console.log('MobileMenu: WARNING - Mobile menu element not found!'); + } + }, + + closeMenu: function() { + var mobileMenu = document.getElementById('mobile-menu'); + if (mobileMenu) { + mobileMenu.classList.remove('show'); + console.log('MobileMenu: Menu closed'); + } + }, + + bindEvents: function() { + var self = this; + var toggleBtn = document.getElementById('mobile-menu-toggle'); + + console.log('MobileMenu: Toggle button found?', !!toggleBtn); + + if (toggleBtn) { + toggleBtn.onclick = function(e) { + e.preventDefault(); + self.toggleMenu(); + return false; + }; + console.log('MobileMenu: Click handler attached'); + } else { + console.log('MobileMenu: WARNING - Mobile menu toggle button not found!'); + } + + // Close menu when clicking on a link + var mobileMenuLinks = document.querySelectorAll('.mobile-menu-items a, .mobile-menu-user a, .mobile-menu-signin a'); + console.log('MobileMenu: Found', mobileMenuLinks.length, 'menu links'); + + for (var i = 0; i < mobileMenuLinks.length; i++) { + mobileMenuLinks[i].onclick = function() { + self.closeMenu(); + }; + } + + // Close menu when window is resized to desktop size + window.addEventListener('resize', function() { + if (window.innerWidth >= 1024) { + self.closeMenu(); + } + }); + } +}; + +// Initialize on DOM ready +if (typeof $ !== 'undefined') { + $(document).on('page:change', function() { + console.log('MobileMenu: page:change event fired'); + MobileMenu.init(); + }); + + $(document).ready(function() { + console.log('MobileMenu: document.ready event fired'); + MobileMenu.init(); + }); +} else { + // Fallback if jQuery is not available + document.addEventListener('DOMContentLoaded', function() { + console.log('MobileMenu: DOMContentLoaded event fired'); + MobileMenu.init(); + }); +} diff --git a/app/assets/javascripts/rotating_stats.js b/app/assets/javascripts/rotating_stats.js new file mode 100644 index 000000000..39a368b7b --- /dev/null +++ b/app/assets/javascripts/rotating_stats.js @@ -0,0 +1,29 @@ +// Rotating Stats Animation for Homepage +document.addEventListener('DOMContentLoaded', function() { + var globalStats = document.getElementById('global_statistics'); + + if (!globalStats) return; + + var statElements = globalStats.querySelectorAll('p'); + + if (statElements.length === 0) return; + + var currentIndex = 0; + + // Show first stat initially + statElements[0].classList.remove('hide'); + + // Rotate stats every 2 seconds + setInterval(function() { + // Hide current stat + statElements[currentIndex].classList.add('hide'); + + // Move to next stat + currentIndex = (currentIndex + 1) % statElements.length; + + // Show next stat after a brief delay + setTimeout(function() { + statElements[currentIndex].classList.remove('hide'); + }, 300); + }, 2000); +}); diff --git a/app/assets/javascripts/theme_toggle.js b/app/assets/javascripts/theme_toggle.js new file mode 100644 index 000000000..44c98daaf --- /dev/null +++ b/app/assets/javascripts/theme_toggle.js @@ -0,0 +1,75 @@ +// Theme Toggle Functionality +var ThemeToggle = { + init: function() { + console.log('ThemeToggle: Initializing...'); + var savedTheme = this.getSavedTheme(); + console.log('ThemeToggle: Saved theme is', savedTheme); + this.applyTheme(savedTheme); + this.bindEvents(); + }, + + getSavedTheme: function() { + try { + return localStorage.getItem('theme') || 'light'; + } catch (e) { + return 'light'; + } + }, + + applyTheme: function(theme) { + var html = document.documentElement; + var moonIcon = document.getElementById('theme-icon-moon'); + var sunIcon = document.getElementById('theme-icon-sun'); + + if (theme === 'dark') { + html.classList.add('dark'); + if (moonIcon) moonIcon.classList.add('hidden'); + if (sunIcon) sunIcon.classList.remove('hidden'); + } else { + html.classList.remove('dark'); + if (moonIcon) moonIcon.classList.remove('hidden'); + if (sunIcon) sunIcon.classList.add('hidden'); + } + + try { + localStorage.setItem('theme', theme); + } catch (e) { + console.log('Could not save theme preference'); + } + }, + + toggleTheme: function() { + var currentTheme = this.getSavedTheme(); + var newTheme = currentTheme === 'light' ? 'dark' : 'light'; + this.applyTheme(newTheme); + }, + + bindEvents: function() { + var self = this; + var themeToggleBtn = document.getElementById('theme-toggle'); + + console.log('ThemeToggle: Theme toggle button found?', !!themeToggleBtn); + + if (themeToggleBtn) { + themeToggleBtn.onclick = function(e) { + e.preventDefault(); + console.log('ThemeToggle: Toggle clicked!'); + self.toggleTheme(); + return false; + }; + console.log('ThemeToggle: Click handler attached'); + } else { + console.log('ThemeToggle: WARNING - Theme toggle button not found!'); + } + } +}; + +// Initialize on DOM ready +$(document).on('page:change', function() { + ThemeToggle.init(); +}); + +// Also initialize immediately if DOM is already loaded +$(document).ready(function() { + ThemeToggle.init(); +}); diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass new file mode 100644 index 000000000..e240d718d --- /dev/null +++ b/app/assets/stylesheets/dark_theme.sass @@ -0,0 +1,150 @@ +// Dark Theme Styles +// Applied when .dark class is added to html element + +html.dark + // Body and background colors + body + background-color: #0f172a !important + color: #e2e8f0 !important + + // Page container + #page, .container#page + background-color: #0f172a !important + + // Header stays with purple gradient (no change needed) + // Footer stays with purple gradient (no change needed) + + // Content areas + #page-contents, #page_contents + background-color: #1e293b + color: #e2e8f0 + + // Cards and wells + .well + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Tables + table + background-color: #1e293b + color: #e2e8f0 + td, th + border-color: #334155 !important + color: #e2e8f0 !important + + // Links + a + color: #60a5fa !important + &:hover + color: #93c5fd !important + + // Buttons (keep primary yellow button, update others) + .btn + &:not(.btn-primary):not(.btn-success) + background-color: #334155 !important + color: #e2e8f0 !important + &:hover + background-color: #475569 !important + + // Forms + input, textarea, select + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + &::placeholder + color: #94a3b8 !important + + // Billboard/Showcase + .billboard + background-color: #1e293b !important + color: #e2e8f0 !important + + .showcase + background-color: #0f172a !important + + // Project container + #project_container + background-color: #0f172a !important + color: #e2e8f0 !important + + // Headings + h1, h2, h3, h4, h5, h6 + color: #f1f5f9 !important + + // Alerts + .alert + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Dropdown menus + .dropdown-menu + background-color: #1e293b !important + border-color: #334155 !important + li a + color: #e2e8f0 !important + &:hover + background-color: #334155 !important + + // Home page sections + .top_ten, .top_ten.middle, .top_ten.last + background-color: #1e293b !important + color: #e2e8f0 !important + + .home_page_row .col-md-4 + background-color: #1e293b !important + + // Navigation menu - keep white text on purple gradient + .navbar, #navbar-inner + .new_main_menu li a + color: white !important + + // Search inputs + .for_search_all_code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Badges + .badge + background-color: #334155 !important + color: #e2e8f0 !important + + // Panels + .panel + background-color: #1e293b !important + border-color: #334155 !important + .panel-heading + background-color: #334155 !important + color: #e2e8f0 !important + .panel-body + background-color: #1e293b !important + color: #e2e8f0 !important + + // Code blocks + pre, code + background-color: #1e293b !important + color: #e2e8f0 !important + border-color: #334155 !important + + // Borders + .mezzo + border-top-color: #334155 !important + + .right_border + border-right-color: #334155 !important + + // Text colors + .signature_color + color: #60a5fa !important + + // Keep error colors visible + .error + color: #ef4444 !important + + .bad + color: #f87171 !important + + .good + color: #4ade80 !important diff --git a/app/assets/stylesheets/footer.sass b/app/assets/stylesheets/footer.sass index dccca4602..1baf71240 100644 --- a/app/assets/stylesheets/footer.sass +++ b/app/assets/stylesheets/footer.sass @@ -1,81 +1,186 @@ footer - @include footer-colors + // Figma design: purple gradient background + position: relative + overflow: hidden + background: linear-gradient(to right, #1D0631, #5A2A82) !important + color: white !important + margin: 0 + padding: 0 + width: 100% + max-width: 100% + .footer-container + position: relative + max-width: 1280px + margin: 0 auto + padding: 48px 32px 24px 32px - margin: 20px 0 0 0 - min-height: 250px - padding-top: 10px - width: 960px - - @media (min-width: 320px) and (max-width: 480px) - width: auto - min-height: 194px !important - padding-top: 0px !important - margin: 0px 0 0 0 !important + // Mobile: < 640px + @media (max-width: 639px) + .footer-container + padding: 32px 16px 16px 16px + .footer-grid + gap: 32px + margin-bottom: 24px .footer-left - margin-left: 5px !important - margin-top: 20px !important - min-height: 150px !important - .footer-mid - line-height: 15px - margin-left: 30px !important - font-size: 8px !important - width: 32% !important - .footer-right - font-size: 8px !important - padding-right: 2px !important - width: 31% !important + .logo_img + height: 28px + .footer-mid, .footer-right + h3 + font-size: 10px + margin-bottom: 12px + a + font-size: 10px + li + margin-bottom: 8px .footer-bottom - margin-left: 0px !important - margin-top: 0px !important - @media (min-width: 540px) and (max-width: 1024px) - width: auto + .footer-bottom-content + gap: 8px + .copyright + font-size: 9px + text-align: center + .follow-us + span + font-size: 10px + a + width: 28px + height: 28px + i + font-size: 14px + + // Tablet: 640px - 767px + @media (min-width: 640px) and (max-width: 767px) + .footer-container + padding: 40px 24px 20px 24px + .footer-grid + gap: 40px + margin-bottom: 28px + + // Tablet landscape: 768px - 1023px + @media (min-width: 768px) and (max-width: 1023px) + .footer-container + padding: 44px 32px 22px 32px + .footer-grid + gap: 36px + + .footer-grid + display: grid + grid-template-columns: 1fr + gap: 48px + margin-bottom: 32px + @media (min-width: 768px) + grid-template-columns: repeat(3, 1fr) + gap: 48px .footer-left - float: left - margin-top: 20px - margin-left: 30px - width: 15% + display: flex + align-items: flex-start + justify-content: center + @media (min-width: 768px) + justify-content: flex-start a .logo_img - width: 100% - height: 100% - - .footer-mid - float: left - margin-top: 20px - margin-left: 120px - margin-right: 40px - width: 20% - font-size: 12px - a - @include footer-colors - padding: 4px 4px + width: auto + height: 32px + object-fit: contain + @media (min-width: 640px) + height: 40px - .footer-right - float: left - margin-top: 20px - margin-bottom: 20px - width: 30% - font-size: 12px - padding-right: 60px - text-align: left + .footer-mid, .footer-right + h3 + font-size: 11px + font-weight: 600 + color: white !important + margin-bottom: 16px + text-transform: uppercase + letter-spacing: 0.05em + @media (min-width: 640px) + font-size: 13px + margin-bottom: 12px + @media (min-width: 768px) + margin-bottom: 16px + ul + list-style: none + margin: 0 + padding: 0 + li + margin-bottom: 10px + @media (min-width: 640px) + margin-bottom: 8px + @media (min-width: 768px) + margin-bottom: 10px + p + margin: 0 0 10px 0 + @media (min-width: 640px) + margin: 0 0 8px 0 + @media (min-width: 768px) + margin: 0 0 10px 0 a - @include footer-colors - padding: 4px 6px + color: #d1d5db !important + text-decoration: none + font-size: 11px + font-weight: 400 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 13px + &:hover + color: white !important .footer-bottom - @include footer-copyright-colors - - float: left - margin-top: 10px - margin-left: 30px - margin-bottom: 2em - bottom: 0 - a - @include footer-copyright-colors + border-top: 1px solid rgba(255, 255, 255, 0.1) + padding: 12px 0 + @media (min-width: 640px) + padding: 16px 0 + @media (min-width: 768px) + padding: 24px 0 + .footer-bottom-content + display: flex + flex-direction: column + align-items: center + justify-content: space-between + gap: 12px + @media (min-width: 640px) + flex-direction: row + .copyright + color: #9ca3af !important + font-size: 10px + order: 2 + @media (min-width: 640px) + font-size: 12px + order: 1 + .follow-us + display: flex + align-items: center + gap: 8px + order: 1 + @media (min-width: 640px) + order: 2 + span + color: #d1d5db !important + font-size: 12px + font-weight: 500 + @media (min-width: 640px) + font-size: 14px + a + display: flex + align-items: center + justify-content: center + width: 32px + height: 32px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + transition: background-color 0.3s ease + &:hover + background-color: rgba(255, 255, 255, 0.2) + i + color: #d1d5db + font-size: 16px + transition: color 0.3s ease + &:hover i + color: white footer.fluid_footer width: auto + background: linear-gradient(to right, #1D0631, #5A2A82) !important .footer-left width: 12% ul @@ -83,16 +188,22 @@ footer.fluid_footer a padding: 1rem 2rem font-weight: bold + color: rgba(209, 213, 219, 1) !important + &:hover + color: white !important .footer-mid width: 25% text-align: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-right width: 40% float: left - font-weight: bold + font-weight: 600 font-size: 13px + color: white !important .footer-bottom float: left margin-botton: 2em !important + color: rgba(156, 163, 175, 1) !important diff --git a/app/assets/stylesheets/home.sass b/app/assets/stylesheets/home.sass index 4c18ddd8c..0422db618 100644 --- a/app/assets/stylesheets/home.sass +++ b/app/assets/stylesheets/home.sass @@ -1,153 +1,161 @@ -.showcase - padding-top: 20px - padding-bottom: 30px - width: 980px - margin-left: -15px - padding-left: 5px - padding-right: 5px -@media only all and (min-width: 320px) and (max-width: 480px) - .showcase - width: auto - margin-left: 0px - .billboard - height: 185px !important - -.billboard - @include billboard-background-color - color: white - height: 160px - padding-top: 30px - padding-bottom: 30px +// Hero Section - Updated to match Figma exactly +.hero_section + padding-top: 60px + padding-bottom: 40px + padding-left: 16px + padding-right: 16px + background: #fff !important + transition: background 0.3s ease + @media (min-width: 640px) + padding-top: 72px + padding-left: 24px + padding-right: 24px + @media (min-width: 1024px) + padding-top: 88px + padding-left: 32px + padding-right: 32px + // Dark mode support + html.dark & + background: #1D0631 !important + +.hero_container + max-width: 80rem + margin: 0 auto + +// Hero Header - Figma Specs +.hero_header text-align: center + margin-bottom: 24px + +.hero_title + font-size: 1.875rem !important + font-weight: 700 + color: #5A2A82 + margin-bottom: 8px + line-height: 2.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 2.25rem !important + line-height: 2.5rem !important + @media (min-width: 768px) + font-size: 3rem !important + line-height: 1 !important + // Dark mode support + html.dark & + color: #ffffff + +.hero_subtitle + font-size: 0.875rem !important + color: #6b7280 + max-width: 42rem + margin: 0 auto + line-height: 1.25rem !important + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 1rem !important + line-height: 1.5rem !important + // Dark mode support + html.dark & + color: #9ca3af + +// Search Container - Figma Specs +.search_container + max-width: 48rem + margin: 0 auto 20px + +.search_form + margin-bottom: 0 + position: relative + +.search_input_wrapper + position: relative + +.search_icon + position: absolute + top: 50% + transform: translateY(-50%) + left: 8px + font-size: 14px + color: #9ca3af + pointer-events: none + z-index: 1 + transition: color 0.3s ease + @media (min-width: 640px) + left: 24px + font-size: 20px + // Dark mode support + html.dark & + color: #6b7280 + +.search_input width: 100% - z-index: 2 - .billboard_search - // margin-top: 80px - .discover - padding: 0 - .discover_msg - font-size: 2.1em !important - font-weight: 400!important - margin-top: -10px - margin-bottom: 24px - h1 - line-height: 1.2em !important -@media only all and (min-width: 320px) and (max-width: 480px) - .billboard - .discover_msg - font-size: 13px !important - #global_statistics - p - font-size: 10px !important -@media (min-width: 540px) and (max-width: 1024px) - .showcase - width: auto - margin-left: 0px -.global_stats - margin-top: 25px -#icon_text - font-size: 1.6em - color: #646E81 - margin-left: -27px - cursor: pointer + padding: 10px 12px 10px 32px + font-size: 11px !important + border: 2px solid #e5e7eb + border-radius: 16px + outline: none + background: #fff + color: #111827 + font-family: inherit + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.2s ease + &::placeholder + color: #9ca3af + &:hover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + &:focus + border-color: #0E4B7A + box-shadow: 0 0 0 4px rgba(14, 75, 122, 0.1) + &::placeholder + color: #d1d5db + @media (min-width: 640px) + padding: 16px 16px 16px 56px + font-size: 16px !important + // Dark mode support + html.dark & + background: #1D0631 !important + color: #ffffff + border-color: #374151 + &::placeholder + color: #6b7280 + &:focus + border-color: #2E8B9E + box-shadow: 0 0 0 4px rgba(46, 139, 158, 0.1) + +// Rotating Stats - Figma Animation +.rotating_stats + text-align: center + margin-top: 12px + height: 20px #global_statistics - padding-top: 1px p - margin-top: -20px - text-align: center - font-size: 14px -.join_now - @include join-now-header-colors - padding-bottom: 5px - padding-left: 12px -.join_now_home - padding-top: 10px - font-size: 1.4em - p - font-size: 14px !important - .join_now_color - @include join-now-section-colors - li - font-size: 0.5em !important - height: 30px - width: 30px -.home_page_row - .whats_new - margin-right: -27px !important - .col-md-8 - h2 - font-weight: 500 !important - margin-right: -40px !important - margin-left: 30px !important - text-indent: -10px !important - .col-md-4 - @include site-well-color - padding-bottom: 20px - h2 - font-weight: 500 !important - margin-left: 5px !important - text-indent: -10px !important - -@media only all and (min-width: 320px) and (max-width: 480px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important - .for_search_all_code - width: auto !important - .join_now_home - padding-top: 5px - font-size: 12px - li - height: 10px - width: 10px -@media (min-width: 540px) and (max-width: 1024px) - .home_page_row - .col-md-8 - h2 - margin-left: 0px !important - a - img - width: 100% - margin-left: 0px !important -.btn.btn_join_now - @include join-now-button-colors - font-family: 'Roboto' !important - font-size: 22px !important - font-weight: bold !important - margin-left: 60px !important - width: 161px !important -::-webkit-input-placeholder - @include site-placeholder-color -\:-moz-placeholder - @include site-placeholder-color - opacity: 1 -::-moz-placeholder - @include site-placeholder-color - opacity: 1 -\:-ms-input-placeholder - @include site-placeholder-color - -input:focus::-webkit-input-placeholder - color: transparent -input:focus::-moz-placeholder - color: transparent -input:focus::-ms-input-placeholder - color: transparent -@media only all and (min-width: 320px) and (max-width: 480px) - .btn.btn_join_now - margin-left: 0px !important - width: 135px !important - font-size: 15px !important + font-size: 12px !important + font-weight: 500 + background: linear-gradient(90deg, #5A2A82 0%, #5A2A82 100%) + -webkit-background-clip: text + -webkit-text-fill-color: transparent + background-clip: text + margin: 0 + line-height: 1.5 + transition: opacity 0.3s ease + &.hide + opacity: 0 + display: block + &:not(.hide) + opacity: 1 + display: block + // Dark mode support + html.dark & + background: linear-gradient(90deg, #ffffff 0%, #ffffff 100%) +// Old join_now and home_page_row styles removed - using new card-based design +// Old placeholder and btn_join_now styles removed - handled in new components .landing margin-top: 20px - margin-left: -14px + margin-left: 0 + padding: 0 20px + width: 100% .top_ten_main padding-left: 40px !important padding-bottom: 6px !important @@ -191,30 +199,11 @@ input:focus::-ms-input-placeholder a.top_ten_icon p opacity: .75 background-color: #999 -.for_search_all_code - @include site-placeholder-color - height: 23px !important - // margin-top: -108px - width: 413px +// Old .for_search_all_code removed - using new .search_input instead .top_ten_link a border: 0 -.icon_search - font-size: 14px - font-weight: 300 - margin-top: -37px - margin-left: -71px - text-align: center -.wh_new - @include whats-new-header-colors - width: 100% - padding-bottom: 5px -.common - padding-left: 6px - padding-bottom: 3px - font-size: 1.4em - font-weight: normal - color: #000000 +// Old .icon_search, .wh_new, .common removed - using new card-based design .most_popular_projects @include most-popular-projects-colors .most_active_projects @@ -239,4 +228,684 @@ button #no-background background-color: white !important +// Feature Cards Section - Figma Specs +.features_section + max-width: 64rem + margin: 0 auto 32px + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 20px + +.feature_card + text-align: center + margin-bottom: 16px + @media (min-width: 768px) + margin-bottom: 0 + +.feature_icon + width: 40px + height: 40px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin: 0 auto 12px + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + @media (min-width: 640px) + width: 48px + height: 48px + i + font-size: 20px + color: white + @media (min-width: 640px) + font-size: 24px + +.feature_icon_yellow + background: linear-gradient(135deg, #facc15 0%, #f97316 100%) + +.feature_icon_blue + background: linear-gradient(135deg, #3b82f6 0%, #4f46e5 100%) + +.feature_icon_green + background: linear-gradient(135deg, #10b981 0%, #14b8a6 100%) + +.feature_title + font-size: 12px !important + font-weight: 600 + color: #111827 + margin-bottom: 4px + line-height: 1.3 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 14px !important + // Dark mode support + html.dark & + color: #ffffff + +.feature_description + font-size: 10px !important + color: #6b7280 + line-height: 1.5 + padding: 0 8px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 12px !important + // Dark mode support + html.dark & + color: #9ca3af + +// Quick Stats Bar - Figma Specs (grid-cols-2 md:flex) +.quick_stats_bar + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + max-width: 48rem + margin: 0 auto + padding: 16px + background: linear-gradient(90deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.6) 50%, rgba(255,255,255,0.8) 100%) + backdrop-filter: blur(4px) + border-radius: 12px + border: 1px solid rgba(229, 231, 235, 0.6) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + transition: all 0.3s ease + @media (min-width: 768px) + display: flex + align-items: center + justify-content: center + gap: 24px + padding: 12px 24px + // Dark mode support - Yellow background with purple text + html.dark & + background: linear-gradient(90deg, #ffb91a 0%, #ffb91a 50%, #ffb91a 100%) + border-color: #ffb91a + +.stat_item + text-align: center + +.stat_value + font-size: 16px !important + font-weight: 700 + color: #5A2A82 + line-height: 1.2 + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 18px !important + @media (min-width: 768px) + font-size: 20px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_label + font-size: 8px !important + color: #6b7280 + text-transform: uppercase + font-weight: 500 + letter-spacing: 0.05em + margin-top: 2px + transition: color 0.3s ease + @media (min-width: 640px) + font-size: 9px !important + @media (min-width: 768px) + font-size: 10px !important + // Dark mode - Purple text on yellow background + html.dark & + color: #5A2A82 + +.stat_divider + display: none + transition: background 0.3s ease + @media (min-width: 768px) + display: block + width: 1px + height: 32px + background: #d1d5db + opacity: 0.3 + // Dark mode - Purple divider on yellow background + html.dark & + @media (min-width: 768px) + background: #5A2A82 + opacity: 0.3 + +// Content Section +.content_section + padding: 32px 0 + background: white !important + transition: background 0.3s ease + // Dark mode support + html.dark & + background: #1D0631 !important + +.contributors_column, .whats_new_column + @media (max-width: 768px) + margin-bottom: 24px + +.whats_new_join_stack + display: flex + flex-direction: column + gap: 24px + +// Top Contributors Card +.top_contributors_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + > div + background: white + border-radius: 14px + height: 100% + +.card_header + display: flex + align-items: flex-start + gap: 16px + padding: 24px + padding-bottom: 16px + background: white + +.header_icon + width: 48px + height: 48px + background: rgba(90, 42, 130, 0.1) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: #5A2A82 + +.header_content + flex: 1 + +.card_title + font-size: 18px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.card_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + line-height: 1.5 + +.contributors_list + padding: 16px + flex: 1 + overflow-y: auto + +.contributor_item + background: linear-gradient(90deg, #f9fafb 0%, #f3f4f6 100%) + border-radius: 12px + padding: 12px + border: 1px solid #e5e7eb + cursor: pointer + transition: all 0.3s + margin-bottom: 8px + display: flex + align-items: center + gap: 12px + &:hover + background: linear-gradient(90deg, #f5f3ff 0%, #ede9fe 100%) + border-color: #5A2A82 + +.contributor_avatar + flex-shrink: 0 + .avatar_image + width: 40px + height: 40px + border-radius: 12px + object-fit: cover + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + border: 2px solid #9ca3af + +.contributor_info + flex: 1 + min-width: 0 + +.contributor_name_rank + display: flex + align-items: center + gap: 8px + margin-bottom: 2px + +.contributor_name + font-size: 14px + font-weight: 600 + color: #5A2A82 + margin: 0 + .contributor_link + color: inherit + text-decoration: none + &:hover + text-decoration: underline + +.contributor_rank + font-size: 9px + color: rgba(90, 42, 130, 0.6) + text-transform: uppercase + letter-spacing: 0.05em + flex-shrink: 0 + +.contributor_stats + display: flex + align-items: center + gap: 12px + font-size: 10px + color: rgba(90, 42, 130, 0.7) + .stat_item + display: flex + align-items: center + gap: 4px + i + font-size: 10px + +.contributor_arrow + flex-shrink: 0 + color: rgba(90, 42, 130, 0.4) + transition: color 0.3s + i + font-size: 16px + +.contributor_item:hover .contributor_arrow + color: #5A2A82 + +.view_all_link + padding: 16px + .btn_view_all + width: 100% + display: flex + align-items: center + justify-center + gap: 8px + font-size: 14px + font-weight: 600 + color: #5A2A82 + transition: color 0.3s + padding: 12px + border-top: 1px solid rgba(90, 42, 130, 0.2) + text-decoration: none + i + font-size: 16px + &:hover + color: #1D0631 + +// What's New Card +.whats_new_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.card_header_section + padding: 24px + padding-bottom: 16px + background: white + +.whats_new_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 4px + +.whats_new_subtitle + font-size: 12px + color: rgba(90, 42, 130, 0.7) + +.featured_image_section + padding: 0 24px 24px + +.featured_image_link + display: block + position: relative + border-radius: 12px + overflow: hidden + transition: transform 0.3s + &:hover + transform: scale(1.02) + .featured_image + transform: scale(1.05) + +.featured_image + width: 100% + height: auto + transition: transform 0.3s + +// Join Now Card +.join_now_card + position: relative + padding: 3px + border-radius: 16px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) + transition: box-shadow 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.15) + .card_inner + background: white + border-radius: 14px + overflow: hidden + height: 100% + +.join_header + display: flex + align-items: center + gap: 12px + padding: 24px + padding-bottom: 16px + +.join_icon + width: 48px + height: 48px + background: linear-gradient(135deg, #1D0631 0%, #5A2A82 100%) + border-radius: 12px + display: flex + align-items: center + justify-content: center + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + flex-shrink: 0 + i + font-size: 24px + color: white + +.join_header_text + flex: 1 + +.join_title + font-size: 20px + font-weight: 700 + color: #5A2A82 + margin-bottom: 2px + +.join_subtitle + font-size: 12px + color: #6b7280 + +.benefits_list + padding: 0 24px + margin-bottom: 24px + +.benefit_item + display: flex + align-items: flex-start + gap: 12px + margin-bottom: 12px + +.benefit_icon + width: 32px + height: 32px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + margin-top: 2px + +.benefit_icon_award + background: rgba(90, 42, 130, 0.1) + i + font-size: 16px + color: #5A2A82 + +.benefit_icon_trending + background: rgba(29, 6, 49, 0.1) + i + font-size: 16px + color: #1D0631 + +.benefit_content + flex: 1 + +.benefit_title + font-size: 13px + font-weight: 600 + color: #111827 + margin-bottom: 2px + +.benefit_description + font-size: 11px + color: #6b7280 + line-height: 1.5 + +.join_cta + padding: 0 24px 24px + +.btn_join_cta + width: 100% + padding: 12px 24px + background: linear-gradient(90deg, #1D0631 0%, #5A2A82 100%) + color: white + border-radius: 8px + font-weight: 600 + transition: all 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) + display: flex + align-items: center + justify-content: center + gap: 8px + font-size: 16px + text-decoration: none + border: none + cursor: pointer + &:hover + background: linear-gradient(90deg, #1D0631dd 0%, #5A2A82dd 100%) + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) + color: white + text-decoration: none + i + font-size: 16px + +// User Journeys Section +.user_journeys_section + background: #1D0631 + padding: 64px 0 + @media (max-width: 768px) + padding: 48px 0 + +.journeys_header + text-align: center + margin-bottom: 48px + +.journeys_title + font-size: 30px + font-weight: 700 + color: white + margin-bottom: 12px + @media (max-width: 768px) + font-size: 24px + +.journeys_subtitle + font-size: 18px + color: #d1d5db + max-width: 768px + margin: 0 auto + @media (max-width: 768px) + font-size: 16px + +// Mobile View - Collapsible +.journeys_mobile + display: block + @media (min-width: 1024px) + display: none + +.journey_card + position: relative + padding: 3px + border-radius: 12px + overflow: hidden + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 50%, #5A2A82 100%) + margin-bottom: 16px + transition: all 0.3s + +.journey_card_header + width: 100% + display: flex + align-items: center + gap: 16px + padding: 16px + text-align: left + transition: background 0.3s + background: #1D0631 + border: none + cursor: pointer + border-radius: 10px + &:hover + background: rgba(90, 42, 130, 0.2) + +.journey_header_icon + width: 40px + height: 40px + border-radius: 8px + display: flex + align-items: center + justify-content: center + flex-shrink: 0 + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 20px + color: white + +.journey_header_title + flex: 1 + font-size: 14px + font-weight: 600 + color: white + +.journey_toggle_icon + flex-shrink: 0 + i + font-size: 20px + color: white + +.journey_card_content + display: none + padding: 0 16px 16px + background: #1D0631 + border-radius: 0 0 10px 10px + +.journey_card.expanded .journey_card_content + display: block + animation: slideDown 0.3s ease-out + +@keyframes slideDown + from + opacity: 0 + transform: translateY(-10px) + to + opacity: 1 + transform: translateY(0) + +.journey_description + font-size: 14px + color: #d1d5db + line-height: 1.5 + margin-bottom: 16px + +.journey_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + +.steps_title + font-size: 12px + font-weight: 600 + color: #e5e7eb + text-transform: uppercase + letter-spacing: 0.05em + margin-bottom: 8px + +.steps_list + list-style: none + padding: 0 + margin: 0 + li + display: flex + align-items: flex-start + gap: 8px + font-size: 14px + color: #d1d5db + margin-bottom: 6px + &:last-child + margin-bottom: 0 + +.step_bullet + color: #ffb91a + font-weight: 700 + +// Desktop View - Grid +.journeys_desktop + display: none + @media (min-width: 1024px) + display: grid + grid-template-columns: repeat(3, 1fr) + gap: 24px + +.journey_grid_card + border-radius: 12px + padding: 24px + border: 1px solid rgba(90, 42, 130, 0.4) + background: #1D0631 + transition: all 0.3s + &:hover + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3) + border-color: #ffb91a + +.grid_card_icon + width: 56px + height: 56px + border-radius: 12px + display: flex + align-items: center + justify-content: center + margin-bottom: 16px + transition: transform 0.3s + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2) + background: linear-gradient(135deg, #5A2A82 0%, #ffb91a 100%) + i + font-size: 28px + color: white + +.journey_grid_card:hover .grid_card_icon + transform: scale(1.1) + +.grid_card_title + font-size: 20px + font-weight: 700 + color: white + margin-bottom: 8px + +.grid_card_description + font-size: 14px + color: #d1d5db + margin-bottom: 16px + line-height: 1.5 + +.grid_card_steps + background: rgba(90, 42, 130, 0.2) + border-radius: 8px + padding: 16px + border: 1px solid rgba(90, 42, 130, 0.4) + diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index bda95eea6..8efad3364 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -55,13 +55,16 @@ @mixin top-navbar-colors color: white - background-color: #211e1e !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important @mixin top-navbar-menu-colors - color: white + color: white !important + font-weight: 500 + transition: color 0.3s ease &:hover padding-bottom: 0px - color: color-gradient($SECONDARY_YELLOW, 60) + color: rgba(255, 255, 255, 0.7) !important + text-decoration: none @mixin twitter-background-color background-color: #5A2A82 @@ -119,15 +122,15 @@ border-left: 3px solid color-gradient($PRIMARY_DARK_GRAY, 100) @mixin footer-colors - color: color-gradient($PRIMARY_PURPLE, 120) !important - background-color: color-gradient($FOOTER_BACKGROUND, 0) !important + color: white !important + background: linear-gradient(to right, #1D0631, #5A2A82) !important a &:hover - color: color-gradient($FOOTER_BACKGROUND, 0) + color: white !important border-radius: 0 @mixin footer-copyright-colors - color: color-gradient($LIGHT_GRAY, 100) !important + color: rgba(156, 163, 175, 1) !important //////////////////////////////////////////////////////////////////// // diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index cc813f4f1..dd1cf9d9d 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -1,21 +1,41 @@ @import "base" +// Mobile menu slide down animation +@keyframes slideDown + 0% + opacity: 0 + transform: translateY(-10px) + 100% + opacity: 1 + transform: translateY(0) + body - background-color: #211e1e !important - font-family: Roboto + background-color: white !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" font-size: 1.3rem !important margin: 0 + padding: 0 color: #000 + width: 100% + overflow-x: hidden -#page - width: 980px +.container#page, #page + width: 100% !important + max-width: 100% !important background-color: white - margin: 0 auto - padding: 10px 10px 0 10px + margin: 0 !important + padding: 0 !important header - background-color: white - margin: -10px -10px 0px -10px + // Figma design: gradient background for entire header + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-size: 16px + margin: 0 + padding: 0 + width: 100% + position: relative + z-index: 1 @media only all and (min-width: 320px) and (max-width: 480px) body @@ -27,60 +47,94 @@ header overflow-x: hidden !important @media (min-width: 540px) and (max-width: 1024px) - #page - margin: 0 auto - padding: 10px 10px 0 10px - width: auto + #page, .container#page + margin: 0 !important + padding: 0 !important + width: 100% !important + max-width: 100% !important #nav-top-bar - margin: 11px 11px 0 11px + width: 100% + max-width: 1280px + margin: 0 auto + padding: 0 32px + display: flex + align-items: center .new_main_menu - list-style: none - margin: 0 - padding: 0 + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + gap: 18px !important li - display: inline-block - margin-right: 32px + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important a - @include top-navbar-menu-colors - font-size: 11pt - text-decoration: none + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: color 0.3s ease !important + white-space: nowrap !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + padding: 6px 0 !important + margin: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important #navbar-inner - @include top-navbar-colors - padding-top: 1px - margin-left: 4px - margin-right: 5px - height: 42px + // Navigation moved to main navbar - hide this section + display: none -@media (min-width: 320px) and (max-width: 340px) +@media (min-width: 320px) and (max-width: 480px) #nav-top-bar - margin: 11px 4px 0 0px !important + padding: 0 16px .new_main_menu + gap: 16px + .mobile-menu + .mobile-menu-content + padding: 0 8px + .mobile-menu-items li - margin-right: 2px !important a - font-size: 10px !important - -@media (min-width: 341px) and (max-width: 480px) + padding: 10px 12px + font-size: 13px + +@media (min-width: 481px) and (max-width: 768px) #nav-top-bar - margin: 11px 10px 0 11px !important - .new_main_menu - li - margin-right: 6px !important - a - font-size: 10px !important -#nav-top-bar .new_main_menu li a:hover - color: #fff - text-decoration: underline + padding: 0 24px + .new_main_menu + gap: 24px #page_contents + max-width: 100% + padding: 0 20px .separator-div @include site-separator-color #project_container background-color: white margin: 0 auto - padding: 10px 10px 0 10px + padding: 10px 20px 0 20px + width: 100% + max-width: 100% #page-contents width: auto #project_masthead @@ -99,132 +153,662 @@ header .quick_reference_heading, .nutshell_heading padding-left: 2rem -@media (min-width: 320px) and (max-width: 480px) +// sm: 640px+ +@media (min-width: 640px) .navbar - margin-right: 0px !important - .btn - font-size: 10px !important - .company-div - float: none !important - .navbar_large_text - font-size: 18px !important + .navbar-container + padding: 0 16px + gap: 12px + .logo-and-brand + gap: 6px + .logo-div + .logo_img + height: 28px + .company-div + .navbar_large_text + font-size: 16px + .separator-div + display: block + height: 24px + .actions-div + gap: 8px + .theme-toggle-btn + padding: 8px + .theme-icon + font-size: 20px + .mobile-menu-toggle + padding: 8px + i + font-size: 20px + .mobile-menu + .mobile-menu-content + padding: 0 12px + .mobile-menu-items + li + a + padding: 12px 16px + +// md: 768px+ +@media (min-width: 768px) + .navbar + .navbar-container + padding: 0 24px + gap: 16px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 32px + .separator-div + display: block + height: 26px + .logo-and-brand + gap: 10px + .mobile-menu + .mobile-menu-content + padding: 0 24px + .mobile-menu-items + li + a + padding: 14px 20px + font-size: 15px + +// lg: 1024px+ (Desktop) +@media (min-width: 1024px) + .navbar + min-height: 72px + .navbar-container + padding: 0 32px + min-height: 72px + .logo-and-brand + gap: 12px + .logo-div + .logo_img + height: 40px + .company-div + .navbar_large_text + font-size: 20px + .separator-div + height: 28px + .actions-div + gap: 16px + .nav-menu + display: flex !important + .new_main_menu + gap: 18px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 14px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + .desktop-actions + display: flex !important + .mobile-menu-toggle + display: none !important + .mobile-menu + display: none + +// xl: 1280px+ +@media (min-width: 1280px) + .navbar + min-height: 80px + .navbar-container + min-height: 80px + .logo-div + .logo_img + height: 48px + .company-div + .navbar_large_text + font-size: 24px + .nav-menu + .new_main_menu + gap: 28px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// 2xl: 1536px+ +@media (min-width: 1536px) + .navbar + min-height: 88px + .navbar-container + min-height: 88px + .logo-div + .logo_img + height: 32px + .company-div + .navbar_large_text + font-size: 30px + .nav-menu + .new_main_menu + gap: 36px !important + li + margin: 0 !important + padding: 0 !important + a + font-size: 16px !important + font-weight: 500 !important + padding: 6px 0 !important + margin: 0 !important + +// Mobile/Tablet: < 1024px (show hamburger menu) +@media (max-width: 1023px) + .navbar + .nav-menu + display: none !important + .desktop-actions + display: none !important + .mobile-menu-toggle + display: flex !important .navbar + // Figma design: gradient background with purple theme + position: relative + background: linear-gradient(to right, #000000, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) border-width: 0 - margin-top: 15px - margin-right: 15px - margin-bottom: 5px !important - margin-left: 15px - padding: 0 0 5px 0 - min-height: 30px + border-bottom: 1px solid rgba(31, 41, 55, 0.8) + margin: 0 + padding: 0 + min-height: 64px + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2) + width: 100% + display: flex + align-items: flex-start + flex-wrap: wrap + overflow: visible + z-index: 100 + // Override global button styles for navbar + .btn + border: none !important + border-width: 0 !important + box-shadow: none !important + &:active + top: 0 !important + left: 0 !important + .navbar-container + max-width: 100% + margin: 0 auto + padding: 0 16px + width: 100% + min-height: 64px + display: flex !important + flex-direction: row !important + flex-wrap: nowrap !important + align-items: center + justify-content: space-between + gap: 12px + position: relative + .hidden + display: none !important + position: absolute !important + width: 0 !important + height: 0 !important + overflow: hidden !important + .logo-and-brand + display: flex + align-items: center + gap: 8px + flex-shrink: 0 + min-width: 0 .logo-div - height: 35px - float: left + display: flex + align-items: center + flex-shrink: 0 .logo_link text-decoration: none - margin-top: 3px - .logo_img - height: 100% + display: flex + align-items: center .company-div - @include top-navigation-colors - float: left + color: white !important + display: flex + align-items: center + min-width: 0 .navbar_small_text font-size: 10px !important .navbar_large_text - font-size: 24px + color: white !important + font-weight: 700 + line-height: 1.25 + letter-spacing: 0 + white-space: nowrap a - @include top-navigation-colors + color: white !important text-decoration: none - .spacing-div - float: left - width: 2px - height: 30px - background-color: white - margin: 0 auto + &:hover + color: #d1d5db !important + transition: color 0.3s ease .separator-div - @include site-separator-color - @include site-separator-background-color - float: left + background-color: rgba(255, 255, 255, 0.2) width: 1px - height: 30px - margin: 0 2px 0 2px + height: 20px + margin: 0 + display: none + .nav-menu + display: none + position: absolute + left: 50% + transform: translateX(-50%) + .new_main_menu + list-style: none !important + margin: 0 !important + padding: 0 !important + display: flex !important + align-items: center !important + li + display: inline-block !important + margin: 0 !important + padding: 0 !important + list-style-type: none !important + &.menu_item + display: inline-block !important + margin: 0 !important + padding: 0 !important + a + color: white !important + font-size: 14px !important + text-decoration: none !important + font-weight: 500 !important + transition: all 0.3s ease !important + white-space: nowrap !important + position: relative !important + padding: 6px 0 !important + margin: 0 !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + letter-spacing: 0 !important + display: inline-block !important + background: none !important + border: none !important + box-shadow: none !important + &:hover, &:focus, &:active + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + &::after + content: '' !important + position: absolute !important + bottom: 0 !important + left: 0 !important + right: 0 !important + height: 2px !important + background-color: #FFB91A !important + border-radius: 2px !important .actions-div - float: right - .twitter-text - @include twitter-background-color - color: white - font-weight: bold - font-size: 0.80em + display: flex + align-items: center + gap: 12px + flex-shrink: 0 + flex-grow: 0 + .theme-toggle-btn + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + display: flex + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + .theme-icon + font-size: 18px + color: white + transition: all 0.3s ease + .hidden + display: none + .mobile-menu-toggle + display: none + padding: 8px + background-color: transparent + border: none + border-radius: 8px + cursor: pointer + align-items: center + justify-content: center + transition: all 0.3s ease + flex-shrink: 0 + &:hover + background-color: rgba(255, 255, 255, 0.1) + transform: scale(1.05) + &:active + transform: scale(0.95) + i + font-size: 18px + color: white .btn border: 0 - display: inline-block - font-size: 15px - line-height: 25px + display: inline-flex + align-items: center + justify-content: center + font-size: 14px + line-height: 1.5 margin: 0 - padding: 1px 6px 0 7px - .follow_btn - @include follow-button-colors - display: inline-block - padding: 0 0 0 4px - height: 24px - i, p, img - color: white - display: inline-block - vertical-align: top - i - color: #555 - vertical-align: middle - &:before - padding-right: 0 - p - font-weight: normal - line-height: 24px - text-transform: none - margin: 0 - border-radius: 0 4px 4px 0 - img - margin-right: -1px + padding: 8px 16px + border-radius: 8px + font-weight: 600 + transition: all 0.3s ease + cursor: pointer + text-decoration: none + white-space: nowrap ul list-style: none - margin: 3px 0 0 0 + margin: 0 + padding: 0 + display: flex + align-items: center + gap: 12px li - float: left - padding: 0 0 0 8px + padding: 0 + list-style: none #top_nav_actions - float: right - margin-right: 5px + display: flex + align-items: center + gap: 12px + margin: 0 + padding: 0 li - padding-top: 5px + padding: 0 + list-style: none + .btn-primary, .btn-success + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-width: 0 !important + font-weight: 600 !important + padding: 10px 20px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) !important + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + border-color: transparent !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) !important + i + font-size: 14px + margin: 0 + .btn-outline + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + font-weight: 600 !important + padding: 8px 18px !important + border-radius: 8px !important + display: inline-flex !important + align-items: center + justify-content: center + gap: 6px + transition: all 0.3s ease + font-size: 14px !important + text-decoration: none + line-height: 1.5 !important + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + border-color: #FFB91A !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) !important + &:active + top: 0 !important + left: 0 !important + transform: translateY(0) + box-shadow: 0 0 0 rgba(255, 185, 26, 0) !important @media (min-width: 320px) and (max-width: 340px) #top_nav_actions margin-right: 0px !important -#logged_user_menu - a - color: black !important +// Mobile Menu +.mobile-menu + display: none + width: 100% + flex-basis: 100% + background: linear-gradient(to right, #1D0631, #5A2A82) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + border-top: 1px solid rgba(255, 255, 255, 0.15) + padding: 20px 0 + position: relative + z-index: 100 + overflow: visible + order: 2 + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.2) + &.show + display: block !important + visibility: visible !important + opacity: 1 !important + animation: slideDown 0.3s ease-out + .mobile-menu-content + max-width: 1280px + margin: 0 auto + padding: 0 16px + width: 100% + display: block + visibility: visible + .mobile-menu-items + list-style: none + margin: 0 + padding: 0 + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-weight: 500 + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + &.select + color: #FFB91A !important + font-weight: 600 + background-color: rgba(255, 185, 26, 0.15) + .mobile-menu-user + list-style: none + margin: 16px 0 0 0 + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.15) + display: block + visibility: visible + li + margin-bottom: 4px + display: block + visibility: visible + a + display: block !important + padding: 14px 16px + color: white !important + text-decoration: none + border-radius: 8px + transition: all 0.3s ease + font-size: 15px + visibility: visible !important + &:hover + background-color: rgba(255, 255, 255, 0.12) + text-decoration: none + transform: translateX(4px) + .mobile-menu-signin + margin-top: 16px + padding: 16px 0 0 0 + border-top: 1px solid rgba(255, 255, 255, 0.1) + display: block + visibility: visible + .btn-mobile-signin + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: #FFB91A !important + color: #000 !important + border: none !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + margin-bottom: 12px + box-shadow: 0 2px 8px rgba(255, 185, 26, 0.3) + &:hover + background-color: rgba(255, 185, 26, 0.9) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.4) + &:active + transform: translateY(0) + box-shadow: 0 2px 4px rgba(255, 185, 26, 0.3) + .btn-mobile-join-now + width: 100% + display: flex !important + align-items: center + justify-content: center + gap: 8px + padding: 14px 20px + background-color: transparent !important + color: #FFB91A !important + border: 2px solid #FFB91A !important + border-radius: 8px + font-weight: 600 + font-size: 15px + text-decoration: none + transition: all 0.3s ease + visibility: visible !important + backdrop-filter: blur(4px) + -webkit-backdrop-filter: blur(4px) + &:hover + background-color: rgba(255, 185, 26, 0.15) !important + text-decoration: none + transform: translateY(-2px) + box-shadow: 0 4px 12px rgba(255, 185, 26, 0.25) + &:active + transform: translateY(0) + box-shadow: none + +.dropdown#logged_user_menu + position: relative + z-index: 1000 + .dropdown-toggle + color: white !important text-transform: none - text-decoration: none - margin: 0 0px 15px - padding-right: 10px - padding-top: 3px - ul - margin-top: 40px - border-radius: 0 + text-decoration: none !important + margin: 0 + padding: 10px 16px + background-color: rgba(255, 255, 255, 0.1) + border-radius: 8px + display: inline-flex !important + align-items: center + gap: 8px + font-size: 14px + font-weight: 500 + transition: all 0.3s ease + border: 1px solid transparent + cursor: pointer !important + position: relative + z-index: 1001 + pointer-events: auto !important + &:hover + background-color: rgba(255, 255, 255, 0.15) + border-color: rgba(255, 255, 255, 0.2) + text-decoration: none !important + transform: translateY(-1px) + &:active + transform: translateY(0) + i + font-size: 14px + pointer-events: none + ul.dropdown-menu + display: none + position: absolute + right: 0 + top: calc(100% + 24px) + margin-top: 8px + border-radius: 12px + border: 1px solid rgba(255, 255, 255, 0.15) + background-color: rgba(29, 6, 49, 0.97) + backdrop-filter: blur(12px) + -webkit-backdrop-filter: blur(12px) + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3), 0 4px 10px rgba(0, 0, 0, 0.2) + z-index: 1050 + min-width: 200px + padding: 8px 0 li - width: 150px + width: 100% a - color: black + color: white !important margin: 0 + padding: 12px 16px + transition: all 0.3s ease + border-radius: 8px + display: block + cursor: pointer &:hover - background-color: #0082C6 !important - color: #fff !important + background-color: rgba(255, 185, 26, 0.2) !important + color: #FFB91A !important + transform: translateX(4px) .divider width: 100% height: 1px - margin: 13px 0 + margin: 8px 0 overflow: hidden - border-bottom: 1px solid #e5e5e5 + border-bottom: 1px solid rgba(255, 255, 255, 0.15) + &.open + ul.dropdown-menu + display: block + +// Override Bootstrap's default dropdown styling +.navbar .dropdown.open .dropdown-menu + display: block !important + +#top_nav_actions + position: relative + z-index: 1000 #flash-msg .alert @@ -533,3 +1117,47 @@ fieldset width: 14rem .language_percentage_indicator width: 3rem + +// Full-width layout styles +.page-content-wrapper + width: 100% + max-width: 100% + margin: 0 auto + padding: 20px + +// Ensure home page content uses full width +.home-content, .explore-content, .billboard + width: 100% + max-width: 100% + margin: 0 auto + padding: 0 20px + +// Override Bootstrap navigation styles +.navbar .nav-menu .new_main_menu li.menu_item a, +.navbar ul.new_main_menu li.menu_item a, +ul.new_main_menu li.menu_item a + color: white !important + font-size: 14px !important + font-weight: 500 !important + text-decoration: none !important + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif !important + line-height: 1.5 !important + padding: 6px 0 !important + margin: 0 !important + background: none !important + border: none !important + box-shadow: none !important + letter-spacing: 0 !important + &:hover, &:focus, &:active, &:visited + color: #d1d5db !important + text-decoration: none !important + background: none !important + &.select + color: #FFB91A !important + font-weight: 600 !important + +@media (min-width: 1280px) + .navbar .nav-menu .new_main_menu li.menu_item a, + .navbar ul.new_main_menu li.menu_item a, + ul.new_main_menu li.menu_item a + font-size: 16px !important diff --git a/app/views/home/_join_now_home.html.haml b/app/views/home/_join_now_home.html.haml index 2fe5d5a3c..b2a24f233 100644 --- a/app/views/home/_join_now_home.html.haml +++ b/app/views/home/_join_now_home.html.haml @@ -1,16 +1,28 @@ -.join_now_home - %ul - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.claim') - %span.join_now_color= t('.contributions') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.manage') - %span.join_now_color= t('.projects_data') - %p - %li.fa.fa-stop.fa-rotate-45.join_now_color - %span.join_now_color= t('.highlight') - %span.join_now_color= t('.use') - %br - %p - = link_to t('.join_now'), h(new_account_path(invite: params[:invite], ref: 'homepage')), class: 'btn btn_join_now' +.join_now_card + .card_inner + .join_header + .join_icon + %i.fa.fa-user-plus + .join_header_text + %h3.join_title= t('.join_now') + %p.join_subtitle= t('.become_part_community') + + .benefits_list + .benefit_item + .benefit_icon.benefit_icon_award + %i.fa.fa-trophy + .benefit_content + %h4.benefit_title= t('.track_contributions_title') + %p.benefit_description= t('.track_contributions_desc') + + .benefit_item + .benefit_icon.benefit_icon_trending + %i.fa.fa-line-chart + .benefit_content + %h4.benefit_title= t('.gain_recognition_title') + %p.benefit_description= t('.gain_recognition_desc') + + .join_cta + = link_to new_account_path(invite: params[:invite], ref: 'homepage'), class: 'btn btn_join_cta' do + %i.fa.fa-user-plus + = t('.create_account') diff --git a/app/views/home/_top_contributors.html.haml b/app/views/home/_top_contributors.html.haml new file mode 100644 index 000000000..4f4cb2895 --- /dev/null +++ b/app/views/home/_top_contributors.html.haml @@ -0,0 +1,33 @@ +.top_contributors_card + .card_header + .header_icon + %i.fa.fa-users + .header_content + %h3.card_title= t('.top_contributors') + %p.card_subtitle= t('.most_active_developers') + + .contributors_list + - @top_contributors&.each_with_index do |contributor, index| + .contributor_item{class: "contributor-rank-#{index + 1}"} + .contributor_avatar + = link_to account_path(contributor) do + = gravatar_tag(contributor.email, size: 40, class: 'avatar_image', alt: contributor.name) + .contributor_info + .contributor_name_rank + %h4.contributor_name + = link_to contributor.name, account_path(contributor), class: 'contributor_link' + %span.contributor_rank= "##{index + 1}" + .contributor_stats + %span.stat_item + %i.fa.fa-code-fork + = number_with_delimiter(contributor.commits_count || 0) + %span.stat_item + %i.fa.fa-folder + = "#{contributor.projects_count || 0} projects" + .contributor_arrow + %i.fa.fa-chevron-right + + .view_all_link + = link_to accounts_path, class: 'btn btn_view_all' do + %i.fa.fa-users + = t('.view_all_contributors') diff --git a/app/views/home/_user_journeys.html.haml b/app/views/home/_user_journeys.html.haml new file mode 100644 index 000000000..a5d6cc56e --- /dev/null +++ b/app/views/home/_user_journeys.html.haml @@ -0,0 +1,272 @@ +.user_journeys_section + .container + .journeys_header + %h2.journeys_title= t('.your_journey_with_openhub') + %p.journeys_subtitle= t('.explore_ways_description') + + / Mobile View - Collapsible Cards + .journeys_mobile + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '0'} + .journey_header_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.journey_header_title= t('.discover_compare_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.discover_compare_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '1'} + .journey_header_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.journey_header_title= t('.assess_health_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.assess_health_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '2'} + .journey_header_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.journey_header_title= t('.manage_portfolio_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.manage_portfolio_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '3'} + .journey_header_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.journey_header_title= t('.analyze_code_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.analyze_code_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '4'} + .journey_header_icon.journey_icon_security + %i.fa.fa-shield + %h3.journey_header_title= t('.check_security_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.check_security_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_card + %button.journey_card_header.journey_toggle{'data-journey-index': '5'} + .journey_header_icon.journey_icon_contribute + %i.fa.fa-search + %h3.journey_header_title= t('.identify_opportunities_title') + .journey_toggle_icon + %i.fa.fa-plus + .journey_card_content + %p.journey_description= t('.identify_opportunities_desc') + .journey_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + + / Desktop View - Grid Cards + .journeys_desktop + .journey_grid_card + .grid_card_icon.journey_icon_compare + %i.fa.fa-balance-scale + %h3.grid_card_title= t('.discover_compare_title') + %p.grid_card_description= t('.discover_compare_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.discover_compare_step1') + %li + %span.step_bullet • + %span= t('.discover_compare_step2') + %li + %span.step_bullet • + %span= t('.discover_compare_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_health + %i.fa.fa-heartbeat + %h3.grid_card_title= t('.assess_health_title') + %p.grid_card_description= t('.assess_health_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.assess_health_step1') + %li + %span.step_bullet • + %span= t('.assess_health_step2') + %li + %span.step_bullet • + %span= t('.assess_health_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_portfolio + %i.fa.fa-id-card + %h3.grid_card_title= t('.manage_portfolio_title') + %p.grid_card_description= t('.manage_portfolio_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.manage_portfolio_step1') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step2') + %li + %span.step_bullet • + %span= t('.manage_portfolio_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_analyze + %i.fa.fa-bar-chart + %h3.grid_card_title= t('.analyze_code_title') + %p.grid_card_description= t('.analyze_code_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.analyze_code_step1') + %li + %span.step_bullet • + %span= t('.analyze_code_step2') + %li + %span.step_bullet • + %span= t('.analyze_code_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_security + %i.fa.fa-shield + %h3.grid_card_title= t('.check_security_title') + %p.grid_card_description= t('.check_security_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.check_security_step1') + %li + %span.step_bullet • + %span= t('.check_security_step2') + %li + %span.step_bullet • + %span= t('.check_security_step3') + + .journey_grid_card + .grid_card_icon.journey_icon_contribute + %i.fa.fa-search + %h3.grid_card_title= t('.identify_opportunities_title') + %p.grid_card_description= t('.identify_opportunities_desc') + .grid_card_steps + %h4.steps_title= t('.key_steps') + %ul.steps_list + %li + %span.step_bullet • + %span= t('.identify_opportunities_step1') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step2') + %li + %span.step_bullet • + %span= t('.identify_opportunities_step3') + +:javascript + document.addEventListener('DOMContentLoaded', function() { + const toggleButtons = document.querySelectorAll('.journey_toggle'); + + toggleButtons.forEach(function(button) { + button.addEventListener('click', function() { + const journeyCard = this.closest('.journey_card'); + const content = journeyCard.querySelector('.journey_card_content'); + const icon = this.querySelector('.journey_toggle_icon i'); + + journeyCard.classList.toggle('expanded'); + + if (journeyCard.classList.contains('expanded')) { + icon.classList.remove('fa-plus'); + icon.classList.add('fa-minus'); + } else { + icon.classList.remove('fa-minus'); + icon.classList.add('fa-plus'); + } + }); + }); + }); diff --git a/app/views/home/_whats_new.html.haml b/app/views/home/_whats_new.html.haml index 130643baf..c045b66e9 100644 --- a/app/views/home/_whats_new.html.haml +++ b/app/views/home/_whats_new.html.haml @@ -1,2 +1,9 @@ -%a{ href: 'https://www.blackduck.com/resources/analyst-reports/open-source-security-risk-analysis.html', target: '_blank' } - %img{src: image_path('home/OSSRA-OH-banner.png'), style: 'margin-left: 25px; margin-top: 15px'} +.whats_new_card + .card_inner + .card_header_section + %h3.whats_new_title= t('.whats_new') + %p.whats_new_subtitle= t('.latest_updates_reports') + + .featured_image_section + %a{ href: 'https://www.blackduck.com/resources/analyst-reports/ossra-report.html', target: '_blank', class: 'featured_image_link' } + %img.featured_image{src: image_path('home/OSSRA-OH-banner.png'), alt: t('.ossra_report_alt')} diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index c47b3002b..473afba49 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,31 +1,81 @@ - content_for(:html_title) { t('.page_title') } -.showcase - .billboard - .row - .col-md-12 - %h2.discover_msg - = t('.discover_message') - .row - .col-md-12.billboard_search - %form.search#search_form{ action: projects_path } - %input{ type: :hidden, name: 'ref', value: 'homepage' } - %input.for_search_all_code{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text' } - %i#icon_text.fa.fa-search +- content_for(:javascript) { javascript_include_tag 'rotating_stats' } + +/ Hero Section +.hero_section + .hero_container + / Hero Header - Centered + .hero_header + %h1.hero_title= t('.discover_message') + %p.hero_subtitle= t('.worlds_largest_directory') + + / Search Bar - Centered & Compact + .search_container + %form.search_form#search_form{ action: projects_path } + %input{ type: :hidden, name: 'ref', value: 'homepage' } + .search_input_wrapper + %i.fa.fa-search.search_icon + %input.search_input{ type: :text, name: 'query', placeholder: t('.search_projects'), id: 'text', autocomplete: 'off' } + + / Rotating Stats + .rotating_stats + #global_statistics + %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) + %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) + %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) + %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) + / Feature Cards - Moved to Top + .features_section + .feature_card + .feature_icon.feature_icon_yellow + %i.fa.fa-database + %h3.feature_title= t('.feature_directory_title') + %p.feature_description= t('.feature_directory_desc') + .feature_card + .feature_icon.feature_icon_blue + %i.fa.fa-heartbeat + %h3.feature_title= t('.feature_health_title') + %p.feature_description= t('.feature_health_desc') + .feature_card + .feature_icon.feature_icon_green + %i.fa.fa-user-circle + %h3.feature_title= t('.feature_portfolio_title') + %p.feature_description= t('.feature_portfolio_desc') + + / Quick Stats Bar - Ultra Compact & Elegant + .quick_stats_bar + .stat_item + .stat_value= "2.5M+" + .stat_label= t('.projects_label') + .stat_divider + .stat_item + .stat_value= number_with_delimiter(@home.person_count / 1000) + "K" + .stat_label= t('.contributors_label') + .stat_divider + .stat_item + .stat_value= "450+" + .stat_label= t('.languages_label') + .stat_divider + .stat_item + .stat_value= "12K" + .stat_label= t('.organizations_label') +/ Top Contributors and What's New Section +.content_section + .container .row - .col-md-12 - .global_stats - #global_statistics - %p.hide#content-0= t('.counting', count: number_with_delimiter(@home.lines_count.to_i)) - %p.hide#content-1= t('.indexing', count: number_with_delimiter(@home.active_project_count)) - %p.hide#content-2= t('.connecting', count: number_with_delimiter(@home.person_count)) - %p.hide#content-3= t('.tracking', count: number_with_delimiter(@home.repository_count)) -.home_page_row - .col-md-4.col-xs-7.col-sm-6 - %h2.join_now= t('.join_now') - = render partial: '/home/join_now_home' - .col-md-8.col-xs-5.col-sm-6 - %h2.wh_new= t('.whats_new') - = render partial: '/home/whats_new' + / Top Contributors - 35% + .col-md-4.col-sm-12.contributors_column + = render partial: '/home/top_contributors' + + / What's New and Join Now - 65% + .col-md-8.col-sm-12.whats_new_column + .whats_new_join_stack + = render partial: '/home/whats_new' + = render partial: '/home/join_now_home' + +/ User Journeys Section += render partial: '/home/user_journeys' + .landing.col-md-12.col-sm-12.col-xs-12.last_section = home_top_lists diff --git a/app/views/layouts/partials/_footer.html.haml b/app/views/layouts/partials/_footer.html.haml index 53b3e40d7..d16f5375d 100644 --- a/app/views/layouts/partials/_footer.html.haml +++ b/app/views/layouts/partials/_footer.html.haml @@ -1,40 +1,51 @@ - raw_email = '%69%6Efo%40O%70en%48u%62.%6E%65t' -.footer-left - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } +.footer-container + .footer-grid + .footer-left + %a.logo_link{ href: 'https://www.blackduck.com' } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck' } -.footer-mid - = "ABOUT BLACK DUCK" - %p - %p - %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' - %p - %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' - %p - %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' - %p - %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' + .footer-mid + %h3 ABOUT BLACK DUCK + %ul + %li + %a{ href: 'https://www.blackduck.com/solutions/application-security-testing.html' }= 'Application Security Testing' + %li + %a{ href: 'https://www.blackduck.com/services.html' }= 'Application Security Services' + %li + %a{ href: 'https://www.blackduck.com/services/security-program/strategy-planning.html' }= 'AppSec Program Development' + %li + %a{ href: 'https://www.blackduck.com/training.html', target: '_blank' }= 'Training' -.footer-right - = "ABOUT OPEN HUB" - %p - %p - %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms - %p - %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog - %p - %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') - %p - %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us + .footer-right + %h3 ABOUT OPEN HUB + %ul + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub' }= t :forum + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Terms-of-Use' }= t :terms + %li + %a{ href: 'https://community.blackduck.com/s/article/Black-Duck-Open-Hub-Open-Hub-Privacy-Policy' }= t :privacy_blog + %li + %a{ href: 'https://github.com/blackducksoftware/ohloh-ui', target: '_blank' }= t('copyright.source_code') + %li + %a{ href: "mailto:#{raw_email}".html_safe }= t :contact_us - -.footer-bottom - %sup © - = Time.current.year - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span{ itemprop: 'publisher' } - %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." - = t('copyright.rights_text') + .footer-bottom + .footer-bottom-content + .copyright + %sup © + = Time.current.year + %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span{ itemprop: 'publisher' } + %a{ href: 'http://www.blackduck.com', target: '_blank' }= "Black Duck Software, Inc." + = t('copyright.rights_text') + .follow-us + %span Follow us: + :ruby + twitter_url = 'https://x.com/intent/follow?original_referer=' + twitter_url += CGI.escape request.url + twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' + %a{ href: twitter_url, target: '_blank', 'aria-label' => 'Follow us on Twitter' } + %i.icon-twitter diff --git a/app/views/layouts/partials/_mast.html.haml b/app/views/layouts/partials/_mast.html.haml index f28f75c58..e67c7f71c 100644 --- a/app/views/layouts/partials/_mast.html.haml +++ b/app/views/layouts/partials/_mast.html.haml @@ -1,52 +1,103 @@ +:ruby + explore_projects_class = params[:action] == 'explore/projects' ? 'select' : '' + explore_orgs_class = params[:action] == 'explore/orgs' ? 'select' : '' + tools_select = params[:action] == 'tools' ? 'select' : '' + .navbar - %span{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } - %span.hidden{ itemprop: 'author' } openhub.net - %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. - %span + .navbar-container + %span.hidden{ itemscope: '', itemtype: 'http://schema.org/CreativeWork' } + %span.hidden{ itemprop: 'author' } openhub.net + %span.hidden{ itemprop: 'publisher' } Black Duck Software, Inc. + .logo-and-brand %div.logo-div - %a.logo_link{ href: 'https://www.blackduck.com' } - %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo.svg'), alt: 'Open Hub' } - %div.spacing-div + %a.logo_link{ href: root_path } + %img.logo_img{ itemprop: 'image', src: image_path('logo/BlackDuckLogo-OnDark.svg'), alt: 'Black Duck Open Hub' } %div.separator-div - %div.spacing-div %div.company-div %a{ href: root_path } %span.navbar_large_text Open Hub - %div.actions-div - %ul#top_nav_actions - %li.twitter_follow - :ruby - twitter_url = 'https://twitter.com/intent/follow?original_referer=' - twitter_url += CGI.escape request.url - twitter_url += '®ion=follow_link&screen_name=bdopenhub&source=followbutton&variant=2.0' - %a.btn.follow_btn{ href: twitter_url, target: '_blank' } - %i.icon-twitter - %p.follow! #{t(:follow)} @ - %p.twitter-text  OH  - - if logged_in? - %li - .dropdown#logged_user_menu - %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } - %i.icon-user - = h(current_user.name) - %i.icon-caret-down - %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer - %li - = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %nav.nav-menu + %ul.new_main_menu{ class: page_context[:select_top_menu_nav] } + %li.menu_item.projects + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li.menu_item.people + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li.menu_item.organizations + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li.menu_item.tools + = link_to t(:tools_menu), tools_path, class: tools_select + %li.menu_item.blog + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li.menu_item.bdsa + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + %div.actions-div + %button#theme-toggle.theme-toggle-btn{ 'aria-label' => 'Toggle theme' } + %i#theme-icon-moon.icon-moon.theme-icon + %i#theme-icon-sun.icon-sun.theme-icon.hidden + %ul#top_nav_actions.desktop-actions + - if logged_in? + %li + .dropdown#logged_user_menu + %a{ 'data-toggle' => 'dropdown', :href => '#', :class => 'dropdown-toggle navbar-text link_no_underline' } + %i.icon-user + = h(current_user.name) + %i.icon-caret-down + %ul.dropdown-menu.user-menu.pull-right.dropdown-menu.dropdown-yellow.dropdown-caret.dropdown-closer + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) - - if current_user_is_admin? - %li - = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) - %li.divider - %li - = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) - - else - %li - %a.btn.btn-mini.btn-primary.btn-header{ href: new_session_path }= t :sign_in + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li.divider + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + %li + %a.btn.btn-primary.btn-header{ href: new_session_path } + %i.icon-user + = t :sign_in + %li + %a.btn.btn-outline.btn-join-now{ href: new_account_path } + Join Now + %button#mobile-menu-toggle.mobile-menu-toggle{ 'aria-label' => 'Menu' } + %i#mobile-menu-icon.icon-reorder + #mobile-menu.mobile-menu + .mobile-menu-content + %ul.mobile-menu-items + %li + = link_to t(:projects_menu), projects_explores_path, class: explore_projects_class + %li + = link_to t(:people_menu), people_path, class: params[:action] == 'people' ? 'select' : '' + %li + = link_to t(:organizations_menu), orgs_explores_path, class: explore_orgs_class + %li + = link_to t(:tools_menu), tools_path, class: tools_select + %li + %a{ href: 'https://community.blackduck.com/s/black-duck-open-hub?tabset-c30ff=d9d77', target: '_blank' }= t :blog + %li + %a{ href: bdsa_vulnerabilities_path, target: '_blank' }= t :bdsa + = render 'shared/search.html.haml' + - if logged_in? + %ul.mobile-menu-user + %li + = link_to(" #{t(:profile)}".html_safe, account_path(current_user).to_s) + %li + = link_to(" #{t(:settings)}".html_safe, settings_account_path(current_user).to_s) + - if current_user_is_admin? %li - %a.btn.btn-mini.btn-success.btn-header{ href: new_account_path }= t :join_now + = link_to(" #{t(:admin_dashboard)}".html_safe, admin_accounts_path) + %li + = link_to(" #{t(:logout)}".html_safe, sessions_path, method: :delete) + - else + .mobile-menu-signin + %a.btn.btn-primary.btn-mobile-signin{ href: new_session_path } + %i.icon-user + = t :sign_in + %a.btn.btn-outline.btn-mobile-join-now{ href: new_account_path } + Join Now - if read_only_mode? %div diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index e3160c2b9..3fe5a83df 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -6,5 +6,5 @@ Rails.application.config.assets.version = '1.0' Rails.application.config.assets.precompile += %w[application.js application.css permissions.js admin/admin.css - api/vulnerability.sass api/vulnerability.js] + api/vulnerability.sass api/vulnerability.js rotating_stats.js] Rails.application.config.assets.precompile += %w[*.svg *.eot *.woff *.ttf] diff --git a/config/locales/home.en.yml b/config/locales/home.en.yml index 9e5497ce0..075a8dbc6 100644 --- a/config/locales/home.en.yml +++ b/config/locales/home.en.yml @@ -3,6 +3,7 @@ en: index: page_title: 'Open Hub, the open source network' discover_message: 'Discover, Track and Compare Open Source' + worlds_largest_directory: "The world's largest directory of open source projects and contributors" join_now: 'Join Now' whats_new: "What's New" counting: 'Counting %{count} lines of code' @@ -10,6 +11,16 @@ en: connecting: 'Connecting %{count} open source contributors' tracking: 'Tracking %{count} source control repositories' search_projects: 'Search Projects...' + feature_directory_title: 'Massive Directory' + feature_directory_desc: 'Indexing over 2.5 million open source projects.' + feature_health_title: 'Assess Health' + feature_health_desc: 'Check activity levels and risk before you adopt.' + feature_portfolio_title: 'Manage Portfolio' + feature_portfolio_desc: 'Claim your commits and track your developer rankings.' + projects_label: 'Projects' + contributors_label: 'Contributors' + languages_label: 'Languages' + organizations_label: 'Organizations' top_lists: popular_projects: 'Most Popular Projects' active_projects: 'Most Active Projects' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb index 04d2523cc..3e27b7716 100644 --- a/test/controllers/home_controller_test.rb +++ b/test/controllers/home_controller_test.rb @@ -9,7 +9,13 @@ class HomeControllerTest < ActionController::TestCase best_account_analysis.account.update(best_vita_id: best_account_analysis.id, created_at: 4.days.ago) account_analysis_fact = best_account_analysis.account_analysis_fact account_analysis_fact.update(last_checkin: Time.current) - Rails.cache.stubs(:fetch).returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-recently_active_accounts-cache').returns(Account.recently_active) + Rails.cache.stubs(:fetch).with('HomeDecorator-person_count-cache').returns(1_000_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-active_project_count-cache').returns(2_500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-repository_count-cache').returns(500_000) + Rails.cache.stubs(:fetch).with('HomeDecorator-vita_count-cache').returns([]) + Rails.cache.stubs(:fetch).with('HomeDecorator-most_active_projects-cache').returns([]) + Rails.cache.stubs(:fetch).with('homepage_top_lists').returns([]) get :index assert_response :success diff --git a/test/controllers/licenses_controller_test.rb b/test/controllers/licenses_controller_test.rb index b2ac3ce27..e04bba48b 100644 --- a/test/controllers/licenses_controller_test.rb +++ b/test/controllers/licenses_controller_test.rb @@ -52,7 +52,7 @@ class LicensesControllerTest < ActionController::TestCase get :show, params: { id: @license.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end end diff --git a/test/controllers/organizations_controller_test.rb b/test/controllers/organizations_controller_test.rb index e59b18bb3..fc1e35bab 100644 --- a/test/controllers/organizations_controller_test.rb +++ b/test/controllers/organizations_controller_test.rb @@ -83,7 +83,7 @@ class OrganizationsControllerTest < ActionController::TestCase get :show, params: { id: @organization.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'should support show page via xml api' do diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb index def7e2c23..0c4a958ea 100644 --- a/test/controllers/projects_controller_test.rb +++ b/test/controllers/projects_controller_test.rb @@ -292,7 +292,7 @@ class ProjectsControllerTest < ActionController::TestCase get :show, params: { id: project.vanity_url } - _(assert_select('p')[3].text).must_equal "foo \n " + _(assert_select('p')[1].text).must_equal "foo \n " end it 'show accepts being called via api' do From 4ae08e832f68bab0130329affe4ade9f06107c31 Mon Sep 17 00:00:00 2001 From: alex-sig <143193681+alex-sig@users.noreply.github.com> Date: Tue, 24 Mar 2026 12:07:33 +0530 Subject: [PATCH 29/53] OTWO-7563 Enable redis password for non test environments (#1865) --- config/application.rb | 2 +- config/initializers/sidekiq.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/application.rb b/config/application.rb index 2096c4eb0..afaf2992e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -46,7 +46,7 @@ class Application < Rails::Application redis_config = { host: ENV.fetch('REDIS_HOST', nil), port: ENV.fetch('REDIS_PORT', 0).to_i, namespace: ENV.fetch('REDIS_NAMESPACE', nil) } - redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) if Rails.env.development? + redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) unless Rails.env.test? config.cache_store = :redis_store, redis_config config.action_dispatch.default_headers = { 'X-Content-Type-Options' => 'nosniff' } config.active_record.dump_schemas = :all diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 383422c7c..502895154 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -2,14 +2,14 @@ Sidekiq.configure_server do |config| redis_config = { url: "redis://#{ENV.fetch('REDIS_HOST', nil)}:#{ENV.fetch('REDIS_PORT', nil).to_i}/0" } - redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) if Rails.env.development? + redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) unless Rails.env.test? redis_config[:id] = nil config.redis = redis_config end Sidekiq.configure_client do |config| redis_config = { url: "redis://#{ENV.fetch('REDIS_HOST', nil)}:#{ENV.fetch('REDIS_PORT', nil).to_i}/0" } - redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) if Rails.env.development? + redis_config[:password] = ENV.fetch('REDIS_PASSWORD', nil) unless Rails.env.test? redis_config[:id] = nil config.redis = redis_config end From 5160ce382b6ee32a706f78439d21d95c417d1c5a Mon Sep 17 00:00:00 2001 From: bd-vaibhav Date: Tue, 31 Mar 2026 11:54:17 +0530 Subject: [PATCH 30/53] OTWO-7603 Implement project contributions page design (#1879) * OTWO-7603 Implement project contributors page redesign * OTWO-7603 Implementation of Project contributors page --- app/assets/javascripts/contributors_cards.js | 20 + app/assets/stylesheets/accounts/pai.sass | 51 +- app/assets/stylesheets/alias.sass | 50 +- app/assets/stylesheets/avatar.sass | 1 - app/assets/stylesheets/buttons.sass | 80 ++- app/assets/stylesheets/charts.sass | 36 ++ app/assets/stylesheets/contributions.sass | 425 ++++++++++++- app/assets/stylesheets/dark_theme.sass | 10 +- .../newest_contributors_table.sass | 77 +++ app/assets/stylesheets/oh-styles.sass | 7 +- app/assets/stylesheets/page.sass | 110 ++++ .../stylesheets/project_show_redesign.sass | 590 +----------------- app/assets/stylesheets/projects.sass | 362 ++++++++--- app/assets/stylesheets/sidebar.sass | 473 ++++++++++---- .../stylesheets/top_contributors_table.sass | 130 ++++ .../contributions/_contributions.html.haml | 157 +++-- .../_newest_contributions.html.haml | 2 - app/views/contributions/index.html.haml | 4 +- app/views/contributions/show.html.haml | 2 - app/views/contributions/summary.html.haml | 46 +- .../layouts/partials/_footer_nav.html.haml | 18 +- .../layouts/partials/_navigator.html.haml | 72 ++- app/views/projects/_project_index.html.haml | 22 +- app/views/projects/show.html.haml | 134 ---- app/views/projects/show/_header.html.haml | 149 +++-- .../projects/show/_header_redesign.html.haml | 135 ---- .../show/_no_analysis_summary.html.haml | 17 +- .../shared/_analysis_timestamp.html.haml | 2 +- .../_commits_or_contributor_search.html.haml | 23 +- 29 files changed, 1892 insertions(+), 1313 deletions(-) create mode 100644 app/assets/javascripts/contributors_cards.js create mode 100644 app/assets/stylesheets/newest_contributors_table.sass create mode 100644 app/assets/stylesheets/top_contributors_table.sass delete mode 100644 app/views/projects/show/_header_redesign.html.haml diff --git a/app/assets/javascripts/contributors_cards.js b/app/assets/javascripts/contributors_cards.js new file mode 100644 index 000000000..c0fcf560e --- /dev/null +++ b/app/assets/javascripts/contributors_cards.js @@ -0,0 +1,20 @@ +// Contributors mobile card expand/collapse functionality +document.addEventListener('DOMContentLoaded', function() { + var contributorCards = document.querySelectorAll('.contributor-card-item'); + + contributorCards.forEach(function(card) { + var header = card.querySelector('.card-item-header'); + + if (header) { + header.addEventListener('click', function(e) { + // Don't toggle if clicking on the avatar/name link + if (e.target.closest('.contributor-link')) { + return; + } + + e.preventDefault(); + card.classList.toggle('expanded'); + }); + } + }); +}); diff --git a/app/assets/stylesheets/accounts/pai.sass b/app/assets/stylesheets/accounts/pai.sass index 43a52fa12..50afdd84a 100644 --- a/app/assets/stylesheets/accounts/pai.sass +++ b/app/assets/stylesheets/accounts/pai.sass @@ -1,10 +1,5 @@ @import oh-styles -#project_header_activity_indicator - width: 88px - margin-top: 1.4rem - margin-left: 2rem - .thirtyfive_project_activity_text font-size: 11px text-align: center @@ -29,26 +24,28 @@ text-align: left margin-left: 19px -#i_use_this_container - margin-bottom: 0.8rem - .btn.iusethis - font-size: 13px - height: 30px - margin-right: 10px - - .use_count - padding: 0 10px - font-size: 24px - height: 32px - line-height: 36px - display: block +// Redesigned header activity indicator wrapper +.activity-indicator-wrapper + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 8px + min-width: 80px + + [class^='thirtyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + transform: scale(1.25) + transform-origin: center center + + .thirtyfive_project_activity_text + margin: 0 + width: auto + line-height: 1.2 + font-size: 12px + font-weight: 600 + color: white text-align: center - margin-top: 2px - a - @include site-link-color - -#project_container - #i_use_this_container - padding: 0 - .btn.iusethis - font-size: 2rem + white-space: nowrap diff --git a/app/assets/stylesheets/alias.sass b/app/assets/stylesheets/alias.sass index c57bd86cb..2bd2c6cfb 100644 --- a/app/assets/stylesheets/alias.sass +++ b/app/assets/stylesheets/alias.sass @@ -1,16 +1,46 @@ +// Design System Variables +$purple-primary: #5A2A82 +$gray-500: #6b7280 +$gray-200: #e5e7eb +$yellow-accent: #FFB91A + +// System Font Stack +$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + .project_content_title - :height 50px + height: 72px + font-family: $font-family + .alias + font-family: $font-family + .undone - :text-decoration line-through - :color #808080 + text-decoration: line-through + color: $gray-500 + .pending - :color #8B0000 - :padding-left 18px - :background asset_url('icons/fact_warning.png') left center no-repeat + color: $purple-primary + padding-left: 18px + background: asset_url('icons/fact_warning.png') left center no-repeat + font-weight: 500 + li - :margin-left 25px - :margin-bottom 8px - :border-bottom 1px solid #ddd - :padding-bottom 5px + margin-left: 25px + margin-bottom: 12px + border-bottom: 1px solid $gray-200 + padding-bottom: 8px + font-weight: 400 + line-height: 1.6 + +// Dark Mode +html.dark, +body.dark + .alias + .undone + color: #94a3b8 + + .pending + color: $yellow-accent + li + border-bottom-color: rgba(255, 255, 255, 0.1) \ No newline at end of file diff --git a/app/assets/stylesheets/avatar.sass b/app/assets/stylesheets/avatar.sass index bca18dbae..2c2df6dcc 100755 --- a/app/assets/stylesheets/avatar.sass +++ b/app/assets/stylesheets/avatar.sass @@ -1,6 +1,5 @@ img.avatar :background #fff - :padding 4px :border 1px solid #E5DFC7 :float left :display block diff --git a/app/assets/stylesheets/buttons.sass b/app/assets/stylesheets/buttons.sass index 6eb3346ff..3b34a6bee 100644 --- a/app/assets/stylesheets/buttons.sass +++ b/app/assets/stylesheets/buttons.sass @@ -284,44 +284,70 @@ button.btn:active, a.btn:active border-color: #ffb91a !important opacity: 0.4 -.i_use_this_btn - background-color: white !important - border-color: #e5e7eb !important - color: #374151 !important - font-weight: 500 !important +// Modern "I Use This" Button Design - All variants +.i_use_this_btn, +.i-use-this-btn, +.i-use-this-btn-mobile, +.btn-use-this + padding: 6px 16px !important + background-color: #5A2A82 !important + color: #ffffff !important + border: none !important + border-radius: 8px !important + font-size: 12px !important + font-weight: 600 !important + cursor: pointer !important box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !important - width: 100% - + transition: background-color 0.2s ease !important + display: inline-flex !important + align-items: center !important + justify-content: center !important + &:hover - border-color: #5A2A82 !important - background-color: white !important - color: #374151 !important - + background-color: rgba(90, 42, 130, 0.9) !important + color: #ffffff !important + &:active, &:focus - background-color: white !important - border-color: #5A2A82 !important - + background-color: rgba(90, 42, 130, 0.9) !important + color: #ffffff !important + &.disabled, &[disabled] - background-color: #f9fafb !important - color: #9ca3af !important + background-color: #9ca3af !important opacity: 0.6 !important cursor: not-allowed !important - + // Dark mode - .dark & - background-color: #2D1548 !important - border-color: #5A2A82 !important - color: #d1d5db !important + html.dark & + background-color: #ffb91a !important + color: #1D0631 !important box-shadow: 0 1px 3px rgba(0,0,0,0.24), 0 1px 2px rgba(0,0,0,0.48) !important - + &:hover - border-color: #ffb91a !important - background-color: #2D1548 !important - + background-color: rgba(255, 185, 26, 0.9) !important + color: #1D0631 !important + + &:active, &:focus + background-color: rgba(255, 185, 26, 0.9) !important + &.disabled, &[disabled] - background-color: #1D0631 !important - color: #6b7280 !important + background-color: #6b7280 !important + opacity: 0.6 !important +// State variants for redesigned button +.i-use-this-btn, +.i-use-this-btn-mobile + &.using + background-color: #5A2A82 !important + + html.dark & + background-color: #ffb91a !important + + &.not-using + background-color: #5A2A82 !important + + html.dark & + background-color: #ffb91a !important + .btn-header width: 100px diff --git a/app/assets/stylesheets/charts.sass b/app/assets/stylesheets/charts.sass index 7f3f511f2..d1927890f 100644 --- a/app/assets/stylesheets/charts.sass +++ b/app/assets/stylesheets/charts.sass @@ -394,3 +394,39 @@ .highcharts-graph @include code-analysis-chart-color-0-stroke +// Dark mode chart axis styling +html.dark + #top_commit_volume_chart, + #committer_history_chart, + .committers_chart + .highcharts-axis-title + fill: #e2e8f0 !important + + .highcharts-axis-labels text + fill: #e2e8f0 !important + + .highcharts-xaxis-labels text, + .highcharts-yaxis-labels text + fill: #e2e8f0 !important + + .highcharts-legend-item text + fill: #e2e8f0 !important + + .highcharts-legend-box + fill: #2D1548 !important + + .highcharts-button text, + g.highcharts-button text + fill: #e2e8f0 !important + + // Target all text in button groups + .highcharts-exporting-group text + fill: #e2e8f0 !important + + .highcharts-button-box + fill: #2D1548 !important + stroke: #5A2A82 !important + + #committer_history_chart + .highcharts-graph + stroke: #60a5fa !important diff --git a/app/assets/stylesheets/contributions.sass b/app/assets/stylesheets/contributions.sass index cd286766f..1f22b6d53 100644 --- a/app/assets/stylesheets/contributions.sass +++ b/app/assets/stylesheets/contributions.sass @@ -10,7 +10,424 @@ left: 4px font-size: 10px !important -#contributions_index_page - #search-dingus - margin-bottom: 0px !important - margin-top: 0px !important +// Search bar styling moved to search-dingus.sass + +// Contributors Summary Page - Figma Design Styling +.project_content_title + h1, h2 + color: #111827 + html.dark & + color: #ffffff + +.committers_chart + display: flex + flex-wrap: wrap + margin-bottom: 24px + + .pull-left + flex: 45 + min-width: 300px + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + padding: 24px + margin-right: 24px !important + margin-bottom: 16px + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .pull-right + flex: 55 + min-width: 300px + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + padding: 24px + margin-bottom: 16px + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + h4 + color: #5A2A82 + font-weight: 700 + margin-bottom: 16px + + html.dark & + color: #ffb91a + +.col-md-12 + h4 + color: #5A2A82 + font-weight: 700 + + html.dark & + color: #ffb91a + + a + color: #5A2A82 !important + text-decoration: underline + + html.dark & + color: #ffb91a !important + +// Contributors Section Layout +.contributors-section + .section-title, + h2.section-title + display: flex + align-items: center + gap: 8px + font-size: 16px + font-weight: 600 + color: #111827 + margin: 0 0 16px 0 !important + + @media (min-width: 768px) + font-size: 20px + + html.dark & + color: #ffffff + + .section-icon + width: 20px + height: 20px + color: #5A2A82 + flex-shrink: 0 + + html.dark & + color: #ffb91a + + .section-title-link + color: #5A2A82 + text-decoration: none + + html.dark & + color: #ffb91a !important + + &:hover + color: #5A2A82 + text-decoration: underline + + html.dark & + color: #ffb91a !important + +// Two-column layout for newest contributors +.two-column-layout + display: grid + grid-template-columns: 1fr + gap: 0 + + @media (min-width: 768px) + grid-template-columns: repeat(2, 1fr) + gap: 32px + + .column-left, + .column-right + .col-md-6 + padding: 0 + width: 100% + +// Desktop only - hide on mobile, show on desktop +.desktop-only + display: none + + @media (min-width: 768px) + display: block + +// Two-column layout specific grid display +.two-column-layout.desktop-only + @media (min-width: 768px) + display: grid + +// Mobile only - show on mobile, hide on desktop +.mobile-only + display: block + + @media (min-width: 768px) + display: none + +// Mobile contributors cards styling +.contributors-cards-container + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + padding: 0 + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .contributor-card-item + border-bottom: 1px solid #f3f4f6 + transition: background-color 0.2s + + html.dark & + border-bottom-color: rgba(90, 42, 130, 0.2) + + &:last-child + border-bottom: none + + .card-item-header + display: flex + align-items: center + justify-content: space-between + padding: 16px 24px + cursor: pointer + + &:hover + background-color: #f9fafb + + html.dark & + background-color: rgba(255, 255, 255, 0.05) + + .contributor-link + display: flex + align-items: center + gap: 12px + text-decoration: none + flex: 1 + min-width: 0 + + img + width: 48px + height: 48px + border-radius: 50% + flex-shrink: 0 + + .contributor-name + font-size: 14px + font-weight: 600 + color: #111827 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + html.dark & + color: #ffffff + + .expand-toggle + background: none + border: none + padding: 8px + cursor: pointer + color: #6b7280 + transition: transform 0.2s + + html.dark & + color: #9ca3af + + i + font-size: 16px + + .card-item-content + display: none + padding: 0 24px 16px + flex-direction: column + gap: 12px + + .content-row + display: grid + grid-template-columns: 1fr 1fr + gap: 12px + + &.full-width + grid-template-columns: 1fr + + .content-item + display: flex + flex-direction: column + gap: 4px + text-align: center + + .label + font-size: 11px + font-weight: 600 + color: #6b7280 + text-transform: uppercase + letter-spacing: 0.05em + + html.dark & + color: #9ca3af + + .value + font-size: 14px + color: #111827 + font-weight: 500 + + html.dark & + color: #e2e8f0 + + img + height: 20px + width: auto + margin-right: 4px + vertical-align: middle + + .trend-image + margin-top: 8px + text-align: center + + &.expanded + .card-item-header + .expand-toggle + transform: rotate(180deg) + + .card-item-content + display: flex + +// See All Contributors Button +.see-all-container + text-align: center + margin-top: 10px + margin-bottom: 0 + + .btn + display: inline-flex + align-items: center + justify-content: center + gap: 8px + font-size: 14px !important + font-weight: 600 + color: #ffffff !important + background-color: #5A2A82 !important + border: 2px solid #5A2A82 + border-radius: 14px !important + padding: 12px 24px + transition: all 0.2s ease + text-decoration: none + + &:hover + color: #ffffff !important + background-color: #1D0631 !important + border-color: #1D0631 + text-decoration: none + + &:focus + outline: none + box-shadow: 0 0 0 3px rgba(90, 42, 130, 0.2) + + html.dark & + color: #1D0631 !important + border-color: #ffb91a + + &:hover + border-color: #ffd966 + + &:focus + box-shadow: 0 0 0 3px rgba(255, 185, 26, 0.2) + +// Responsive improvements +// Tablet (768px - 991px) +@media (max-width: 991px) + .committers_chart + .pull-left, .pull-right + flex: 1 1 100% !important + margin-right: 0 !important + margin-bottom: 16px + +// Mobile (below 768px) +@media (max-width: 767px) + .project_content_title + h1 + font-size: 24px + + .committers_chart + margin-bottom: 16px + + .pull-left, .pull-right + flex: 1 1 100% !important + margin-right: 0 !important + padding: 16px + margin-bottom: 16px + + .chart + height: 280px !important + width: 100% !important + + .top-committers + margin-top: 16px + overflow-x: auto + + h4 + font-size: 18px + margin-bottom: 12px + + table + font-size: 11px + + thead th + padding: 8px 4px + white-space: nowrap + font-size: 11px + + tbody td + padding: 8px 4px + + &:first-child + min-width: 150px + + img[src*="commits_spark"] + width: 120px + height: 24px + + .avatar_name + font-size: 12px + + .see-all-container .btn + font-size: 13px + padding: 10px 20px + +// Extra small mobile (below 480px) +@media (max-width: 480px) + .project_content_title + h1 + font-size: 20px + line-height: 1.2 + + .committers_chart + .pull-left, .pull-right + padding: 12px + border-radius: 12px + + h4 + font-size: 16px + margin-bottom: 12px + + .chart + height: 240px !important + + .top-committers + + h4 + font-size: 16px + + table + font-size: 10px + + thead th + padding: 6px 2px + font-size: 10px + + tbody td + padding: 6px 2px + + &:first-child + min-width: 120px + + img[src*="commits_spark"] + width: 100px + height: 20px + display: block + + .contribution_title + font-size: 9px + + .see-all-container .btn + width: 100% + padding: 12px 20px + font-size: 14px diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index ca6ff9992..8bfdced42 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -35,17 +35,17 @@ html.dark // Links a - color: #60a5fa !important + color: #ffb91a !important &:hover - color: #93c5fd !important + color: #ffd966 !important // Buttons (keep primary yellow button, update others) .btn &:not(.btn-primary):not(.btn-success) - background-color: #334155 !important - color: #e2e8f0 !important + background-color: #ffb91a !important + color: #1D0631 !important &:hover - background-color: #475569 !important + background-color: #ffd966 !important // Forms input, textarea, select diff --git a/app/assets/stylesheets/newest_contributors_table.sass b/app/assets/stylesheets/newest_contributors_table.sass new file mode 100644 index 000000000..58c511d35 --- /dev/null +++ b/app/assets/stylesheets/newest_contributors_table.sass @@ -0,0 +1,77 @@ +// Newest Contributors Table Styling (Figma Design) +// This applies to all pages using _newest_contributions.html.haml partial + +table.table.table-striped.table-condensed + width: 100% + background: #ffffff !important + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + transition: box-shadow 0.2s + padding: 0 24px + + @media (min-width: 768px) + padding: 0 32px + + html.dark & + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + thead + tr + border-bottom: 1px solid #e5e7eb + + html.dark & + border-bottom: 1px solid rgba(90, 42, 130, 0.4) + + th + text-align: center + vertical-align: middle + font-size: 11px + font-weight: 600 + color: #6b7280 + padding: 8px + text-transform: uppercase + letter-spacing: 0.05em + border-bottom: none !important + + html.dark & + color: #9ca3af + + tbody + tr + border-bottom: 1px solid #f3f4f6 + transition: background-color 0.2s + background-color: transparent !important + + html.dark & + border-bottom: 1px solid rgba(90, 42, 130, 0.2) + background-color: transparent !important + + &:hover + background-color: #f9fafb !important + + html.dark & + background-color: rgba(255, 255, 255, 0.05) !important + + // Remove alternating row colors from Bootstrap striping + &:nth-child(odd) + background-color: transparent !important + + html.dark & + background-color: transparent !important + + &:hover + background-color: #f9fafb !important + + html.dark & + background-color: rgba(255, 255, 255, 0.05) !important + + td + text-align: center + padding: 10px + vertical-align: middle + border-bottom: none !important + + html.dark & + color: #e2e8f0 diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index 1a2fec7de..8455fce86 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -32,7 +32,10 @@ background-color: color-gradient($SECTION_BACKGROUND, 0) !important @mixin site-link-color - color: color-gradient($SECONDARY_SLATE_BLUE, 120) + color: #5A2A82 + + html.dark & + color: #ffb91a @mixin site-placeholder-color color: color-gradient($PRIMARY_DARK_GRAY, 120) @@ -444,7 +447,7 @@ background-color: color-gradient($LIGHT_GRAY, 100) !important @mixin analysis-timestamp-color - color: color-gradient($LIGHT_GRAY, 120) + color: #ffffff @mixin licenses-permitted-color color: color-gradient($SECONDARY_FOREST_GREEN, 120) diff --git a/app/assets/stylesheets/page.sass b/app/assets/stylesheets/page.sass index dfc37ab81..ed509fa22 100644 --- a/app/assets/stylesheets/page.sass +++ b/app/assets/stylesheets/page.sass @@ -129,9 +129,15 @@ header .new_main_menu gap: 24px #page_contents + padding: 0 32px 24px max-width: 100% .separator-div @include site-separator-color + + // Mobile responsive padding + @media (max-width: 767px) + padding: 0 16px 16px + #project_container background-color: white @@ -1165,6 +1171,110 @@ html.dark span color: #60a5fa !important +// Modern Info Card Styles for No Analysis Summary +.no_analysis_message + margin-bottom: 24px + + .modern-info-card + background: #ffffff + border-radius: 16px + overflow: hidden + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.08) + transition: box-shadow 0.2s ease + + .info-card-header + display: flex + align-items: center + gap: 16px + padding: 24px + + .info-icon-wrapper + width: 40px + height: 40px + flex-shrink: 0 + background: linear-gradient(135deg, #5A2A82 0%, #9F7ABA 100%) + border-radius: 10px + display: flex + align-items: center + justify-content: center + + i + color: white + font-size: 20px + + .info-content + flex: 1 + + .info-title + margin: 0 + font-size: 20px + font-weight: 600 + color: #111827 + line-height: 1.4 + + .info-card-body + padding: 0 24px 24px 24px + text-align: center + font-size: 17px + + @media (max-width: 768px) + padding: 0 24px 24px 24px + + .info-text + margin: 0 0 12px 0 + line-height: 1.6 + color: #374151 + text-align: center + + &:last-child + margin-bottom: 0 + + .info-action + margin-top: 24px + text-align: center + + .info-link + color: #0E4B7A + font-weight: 500 + text-decoration: none + transition: color 0.2s ease + + &:hover + color: #2E8B9E + text-decoration: underline + +// Dark Mode Styles for Modern Info Card +html.dark + .no_analysis_message + .modern-info-card + background-color: #2D1548 + + .info-icon-wrapper + background: linear-gradient(135deg, #5A2A82 0%, #9F7ABA 100%) + + .info-title + color: #f9fafb + + .info-text + color: #cbd5e1 + + .info-link + color: #60a5fa + + &:hover + color: #93c5fd + +body.dark + .no_analysis_message + .modern-info-card + background-color: #2D1548 + + .info-title + color: #f9fafb + + .info-text + color: #cbd5e1important + body.dark #project_container .project_row diff --git a/app/assets/stylesheets/project_show_redesign.sass b/app/assets/stylesheets/project_show_redesign.sass index 74e4abdcf..366799a62 100644 --- a/app/assets/stylesheets/project_show_redesign.sass +++ b/app/assets/stylesheets/project_show_redesign.sass @@ -95,7 +95,6 @@ body:has(.project-show-redesign) .project-show-redesign width: 100% min-height: 100vh - background-color: $gray-50 font-family: $font-family -webkit-font-smoothing: antialiased -moz-osx-font-smoothing: grayscale @@ -122,349 +121,15 @@ body:has(.project-show-redesign) &:hover color: $purple-primary - // ============================================================================ - // Project Header Section - // ============================================================================ - .project-header-gradient - background: linear-gradient(135deg, $purple-dark 0%, $purple-primary 100%) - padding: 50px 16px 32px - position: relative - overflow: hidden - - // Decorative gradient circle - &::before - content: '' - position: absolute - top: -192px - right: -192px - width: 384px - height: 384px - background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%) - border-radius: 50% - - .header-content - margin: 0 auto - position: relative - z-index: 10 - - .header-main - display: flex - flex-direction: column - gap: 16px - - @media (min-width: 768px) - flex-direction: row - align-items: flex-start - justify-content: space-between - gap: 24px - - a, - button - font-family: $font-family - - // Desktop: Top-right user stats and button - .header-top-right - position: absolute - top: 49px - right: 16px - display: none - align-items: center - gap: 12px - z-index: 20 - - @media (min-width: 640px) - display: flex - right: 24px - - @media (min-width: 1024px) - right: 32px - - .stats-card - background: white - border-radius: 8px - padding: 12px 16px - box-shadow: $shadow-md - min-width: 140px - - .activity-indicator-wrapper - display: flex - flex-direction: column - align-items: center - justify-content: center - gap: 8px - min-width: 80px - - [class^='thirtyfive_project_activity_level_'] - position: static - top: auto - margin-left: 0 - transform: scale(1.25) - transform-origin: center center - - .thirtyfive_project_activity_text - margin: 0 - width: auto - line-height: 1.2 - font-size: 12px - font-weight: 600 - color: white - text-align: center - white-space: nowrap - - .stats-card - .stats-inner - display: flex - align-items: center - gap: 12px - margin-bottom: 12px - - svg - width: 20px - height: 20px - color: $purple-primary - flex-shrink: 0 - - .user-count - font-size: 28px - font-weight: 700 - color: $purple-primary - line-height: 1 - - .i-use-this-btn - width: 100% - padding: 10px 16px - border-radius: 6px - font-weight: 600 - font-size: 14px - display: flex - align-items: center - justify-content: center - gap: 8px - border: none - cursor: pointer - transition: all 0.2s - - &.using - background: $purple-primary - color: white - - &:hover - background: darken($purple-primary, 8%) - - &.not-using - background: $yellow-accent - color: $purple-dark - - &:hover - background: $yellow-hover - - svg - width: 16px - height: 16px - - // Project title and info section - .project-title-section - flex: 1 - min-width: 0 - max-width: 900px - - @media (min-width: 768px) - max-width: calc(100% - 280px) - - h1 - font-size: 32px - font-weight: 700 - color: white - line-height: 1.2 - letter-spacing: -0.02em - margin: 0 0 12px - - @media (min-width: 768px) - font-size: 48px - - @media (min-width: 1024px) - font-size: 48px - - .action-buttons - display: flex - flex-wrap: wrap - gap: 8px - margin-bottom: 20px - - .action-btn - display: inline-flex - align-items: center - gap: 6px - padding: 6px 12px - border-radius: 8px - font-size: 12px - font-weight: 500 - line-height: 1.25 - text-decoration: none !important - appearance: none - -webkit-appearance: none - background-image: none !important - box-shadow: none !important - border: 0 - cursor: pointer - transition: all 0.2s ease - - &:link, - &:visited, - &:hover, - &:active - color: white !important - text-decoration: none !important - - @media (min-width: 768px) - font-size: 14px - - svg - width: 14px - height: 14px - flex-shrink: 0 - stroke: currentColor - - // Outlined button style (for Settings and Report Duplicate) - &.action-btn-outline - background: rgba(255, 255, 255, 0.1) - border: 1px solid rgba(255, 255, 255, 0.2) - color: white !important - box-shadow: none - - &:hover - background: rgba(255, 255, 255, 0.2) - border-color: rgba(255, 255, 255, 0.2) - color: white !important - transform: none - - &:active - transform: none - background: rgba(255, 255, 255, 0.2) - - &:focus-visible - outline: 2px solid rgba(255, 255, 255, 0.55) - outline-offset: 2px - - .project-description - font-size: 16px - color: rgba(255, 255, 255, 0.9) - line-height: 1.6 - margin-bottom: 12px - - @media (min-width: 768px) - font-size: 17px - - .timestamp - font-size: 14px - color: rgba(255, 255, 255, 0.7) - line-height: 1.5 - - // Mobile: Bottom stats card - .header-mobile-stats - display: flex - gap: 12px - margin-top: 24px - - @media (min-width: 640px) - display: none - - .activity-card-mobile - display: flex - flex-direction: column - align-items: center - justify-content: center - gap: 4px - padding: 10px 12px - background: rgba(255, 255, 255, 0.1) - backdrop-filter: blur(10px) - border: 1px solid rgba(255, 255, 255, 0.2) - border-radius: 8px - - [class^='twentyfive_project_activity_level_'] - position: static - top: auto - margin-left: 0 - - .twentyfive_project_activity_text - margin: 0 - width: auto - min-height: 0 - font-size: 10px - line-height: 1.2 - font-weight: 600 - color: white - text-align: center - white-space: nowrap - - .stats-card-mobile - flex: 1 - display: flex - flex-direction: column - background: rgba(255, 255, 255, 0.1) - backdrop-filter: blur(10px) - border: 1px solid rgba(255, 255, 255, 0.2) - border-radius: 8px - padding: 0 - overflow: hidden - - .stats-top - display: flex - align-items: center - gap: 8px - margin-bottom: 0 - padding: 10px 12px 6px - - svg - width: 16px - height: 16px - color: $yellow-accent - - .user-count - font-size: 18px - font-weight: 700 - color: $yellow-accent - - .users-label - font-size: 12px - color: rgba(255, 255, 255, 0.8) - - .i-use-this-btn-mobile - width: calc(100% - 20px) - margin: 0 10px 10px - padding: 8px 12px - border-radius: 8px - font-weight: 600 - font-size: 14px - display: flex - align-items: center - justify-content: center - gap: 8px - border: none - cursor: pointer - line-height: 1.25 - - &.using - background: $purple-primary - color: white - - &.not-using - background: $yellow-accent - color: $purple-dark - - svg - width: 16px - height: 16px - // ============================================================================ // Main Content Area // ============================================================================ .project-content margin: 0 auto - padding: 24px + padding: 24px 0 @media (min-width: 768px) - padding: 32px + padding: 32px 0 // ============================================================================ // Stats Grid (3 columns) @@ -1344,161 +1009,27 @@ body:has(.project-show-redesign) font-size: 14px // ============================================================================ - // Footer Navigation + // No Analysis Message (Light Mode) // ============================================================================ - .footer-nav-section - background: white - border-radius: 16px - padding: 16px - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) - margin-bottom: 24px - - @media (min-width: 640px) - padding: 32px - margin-bottom: 32px + .no_analysis_message + .btn.btn-primary, + .btn.btn-large.btn-primary + background-color: $purple-primary !important + border-color: $purple-primary !important + color: white !important - a - &:link, - &:visited, &:hover, + &:focus, &:active - text-decoration: none - - // Desktop: 4-column grid - .footer-nav-grid - display: none - - @media (min-width: 768px) - display: grid - grid-template-columns: repeat(4, 1fr) - gap: 32px - - .nav-column - .column-header - display: flex - align-items: center - gap: 8px - margin-bottom: 12px - padding-bottom: 8px - border-bottom: 1px solid $gray-200 - - svg - width: 16px - height: 16px - color: $purple-primary - - h4 - font-size: 14px - font-weight: 600 - color: $gray-900 - margin: 0 - - ul - list-style: none - padding: 0 - margin: 0 - - li - margin-bottom: 8px - - &:last-child - margin-bottom: 0 - - a - font-size: 14px - color: #5A2A82 !important - text-decoration: none - transition: color 0.15s - - &:hover - text-decoration: underline - - &:visited, - &:active - color: $purple-primary - - // Mobile: Accordion - .footer-nav-accordion - display: flex - flex-direction: column - - @media (min-width: 768px) - display: none - - .accordion-item - border-bottom: 1px solid $gray-200 - - &:last-child - border-bottom: none - - .accordion-header-btn - width: 100% - display: flex - align-items: center - justify-content: space-between - padding: 12px 0 - background: none - border: none - cursor: pointer - - .header-left - display: flex - align-items: center - gap: 10px - - svg - width: 16px - height: 16px - color: $purple-primary - - span - font-size: 14px - font-weight: 600 - color: $gray-900 - - .chevron - width: 16px - height: 16px - color: $gray-600 - transition: transform 0.2s - - &.expanded - transform: rotate(180deg) - - .accordion-content-footer - padding-bottom: 12px - padding-left: 24px - display: none - - &.expanded - display: block - - ul - list-style: none - padding: 0 - margin: 0 - - li - margin-bottom: 6px - - a - font-size: 14px - color: $purple-primary - text-decoration: none - - &:hover - text-decoration: underline - - &:visited, - &:active - color: $purple-primary + background-color: $purple-dark !important + border-color: $purple-dark !important + color: white !important // ============================================================================ // Dark Theme Overrides (Project Show Redesign) // ============================================================================ html.dark .project-show-redesign - background-color: $purple-dark color: #e2e8f0 !important // Override any legacy color styles @@ -1555,7 +1086,7 @@ html.dark color: $yellow-accent h3 - color: #f8fafc + color: #f8fafc !important .chevron color: #94a3b8 @@ -1885,55 +1416,6 @@ html.dark .no-data color: #94a3b8 - .footer-nav-section - background: #2D1548 - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) - - .footer-nav-grid - .nav-column - .column-header - border-bottom-color: rgba(159, 122, 186, 0.3) - - svg - color: $yellow-accent - - h4 - color: #f8fafc - - ul - li - a - color: $yellow-accent !important - - &:visited, - &:active - color: $yellow-accent !important - - .footer-nav-accordion - .accordion-item - border-bottom-color: rgba(159, 122, 186, 0.3) - - .accordion-header-btn - .header-left - svg - color: $yellow-accent - - span - color: #f8fafc - - .chevron - color: #94a3b8 - - .accordion-content-footer - ul - li - a - color: $yellow-accent - - &:visited, - &:active - color: $yellow-accent - // Legacy sections rendered inside redesign page (security + analysis summary) .project_row color: #e2e8f0 @@ -3012,47 +2494,3 @@ body.dark #project_container .project-show-redesign, .view-all-link a color: $yellow-accent !important - - .footer-nav-section - background: #2D1548 !important - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important - - .footer-nav-grid - .nav-column - .column-header - border-bottom-color: rgba(159, 122, 186, 0.3) !important - - svg - color: $yellow-accent !important - - h4 - color: #f8fafc !important - - ul li a - color: $yellow-accent !important - - &:visited, - &:active - color: $yellow-accent !important - - .footer-nav-accordion - .accordion-item - border-bottom-color: rgba(159, 122, 186, 0.3) !important - - .accordion-header-btn - .header-left - svg - color: $yellow-accent !important - - span - color: #f8fafc !important - - .chevron - color: #94a3b8 !important - - .accordion-content-footer ul li a - color: $yellow-accent !important - - &:visited, - &:active - color: $yellow-accent !important diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index e2c715a1e..4f470adcc 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -1,3 +1,30 @@ +// Design System Variables for Redesigned Header +// ============================================================================ +$purple-dark: #1D0631 +$purple-primary: #5A2A82 +$purple-light: #9F7ABA +$yellow-accent: #FFB91A +$yellow-hover: #FFCC4D + +// Neutrals +$gray-50: #f9fafb +$gray-100: #f3f4f6 +$gray-200: #e5e7eb +$gray-300: #d1d5db +$gray-400: #9ca3af +$gray-500: #6b7280 +$gray-600: #4b5563 +$gray-700: #374151 +$gray-900: #111827 + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05) +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05) + +// System Font Stack +$font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif + $sizes: (('fifteen', 15, 1), ('twenty', 20, 0), ('twentyfive', 25, 19), ('thirtyfive', 35, 23)) $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_low', 4), ('inactive', 5), ('na', 6), ('new', 7)) @@ -33,9 +60,6 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ .margin_top_two margin-top: 32px -.thirtyfive_project_activity_text - width: 200px - .row-eq-height display: -webkit-box display: -webkit-flex @@ -57,40 +81,213 @@ $pai_levels: (('very_high',0), ('high', 1), ('moderate', 2), ('low', 3), ('very_ li margin: 0 width: 130px - overflow: auto + overflow: edit_authorized + +.project-header-gradient + width: 100vw + margin-left: calc(-50vw + 50%) + margin-right: calc(-50vw + 50%) + background: linear-gradient(135deg, $purple-dark 0%, $purple-primary 100%) + padding: 32px 16px 32px + position: relative + overflow: hiddenßß -#project_masthead - height: 85px + .header-content + padding: 0px 10px + margin: 0 auto + position: relative + z-index: 10 + + .header-main + display: flex + flex-direction: column + gap: 16px - #project_icon - margin-top: 8px + @media (min-width: 768px) + flex-direction: row + align-items: flex-start + justify-content: space-between + gap: 24px - #widgets - width: 496px + a, + button + font-family: $font-family - #project_header - padding-right: 0px - .project_title - margin: 0px 0px 0px 0px - h1 - margin: 15px 0px 6px 0px - i - @include project-icon-colors + // Top-right user stats and button (mobile and desktop) + .header-top-right + display: flex + align-items: center + gap: 12px + margin-top: 24px + z-index: 20 + + @media (min-width: 640px) + position: absolute + top: 49px + right: 24px + margin-top: 0 + + @media (min-width: 1024px) + right: 32px + + .activity-indicator-wrapper + display: flex + flex-direction: column + align-items: center + justify-content: center + gap: 8px + min-width: 80px + + [class^='thirtyfive_project_activity_level_'] + position: static + top: auto + margin-left: 0 + transform: scale(1.25) + transform-origin: center center + + .thirtyfive_project_activity_text + margin: 0 + width: auto + line-height: 1.2 + font-size: 12px + font-weight: 600 + color: white + text-align: center + + .stats-card + background: white + border-radius: 8px + padding: 12px 16px + box-shadow: $shadow-md + min-width: 140px + + .stats-card + .stats-inner + display: flex + align-items: center + gap: 12px + margin-bottom: 12px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .user-count + font-size: 28px + font-weight: 700 + color: $purple-primary + line-height: 1 + + .i-use-this-btn + width: 100% + html.dark & + background-color: #5A2A82 !important + color: #ffffff !important + + // Project title and info section + .project-title-section + flex: 1 + min-width: 0 + max-width: 900px + + @media (min-width: 768px) + max-width: calc(100% - 280px) + + h1 + font-size: 32px + font-weight: 700 + color: white + line-height: 1.2 + letter-spacing: -0.02em + margin: 0 0 12px + + @media (min-width: 768px) + font-size: 48px + + @media (min-width: 1024px) + font-size: 48px + + .action-buttons + display: flex + flex-wrap: wrap + gap: 8px + margin-bottom: 20px + + .action-btn + display: inline-flex + align-items: center + gap: 6px + padding: 6px 12px + border-radius: 8px + font-size: 12px + font-weight: 500 + line-height: 1.25 + text-decoration: none !important + appearance: none + -webkit-appearance: none + background-image: none !important + box-shadow: none !important + border: 0 + cursor: pointer + transition: all 0.2s ease + + &:link, + &:visited, + &:hover, + &:active + color: white !important + text-decoration: none !important + + @media (min-width: 768px) + font-size: 14px + + svg + width: 14px + height: 14px + flex-shrink: 0 + stroke: currentColor + + // Outlined button style (for Settings and Report Duplicate) + &.action-btn-outline + background: rgba(255, 255, 255, 0.1) + border: 1px solid rgba(255, 255, 255, 0.2) + color: white !important + box-shadow: none + + &:hover + background: rgba(255, 255, 255, 0.2) + border-color: rgba(255, 255, 255, 0.2) + color: white !important + transform: none + + &:active + transform: none + background: rgba(255, 255, 255, 0.2) + + &:focus-visible + outline: 2px solid rgba(255, 255, 255, 0.55) + outline-offset: 2px + + .project-description + font-size: 16px + color: rgba(255, 255, 255, 0.9) + line-height: 1.6 + margin-bottom: 12px + + @media (min-width: 768px) + font-size: 17px + + // Mobile Stats Section + .header-mobile-stats + display: flex + gap: 12px + margin-top: 4px + + @media (min-width: 640px) + display: none - .about - margin-top: 10px - li - margin: 15px 0 15px 30px -#add_this - margin-left: 85px - margin-top: 85px - a - @include add-this-link-colors - -#project_container - #project_masthead - #widgets - width: 480px // settings page styles .settings_module :cursor pointer @@ -842,49 +1039,6 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) html.dark & color: $dark-text-primary - .project-tags - display: flex - flex-wrap: wrap - gap: 6px - align-items: center - - .tags-label - font-size: 12px - color: $text-secondary - margin-right: 4px - html.dark & - color: $dark-text-secondary - - .tag - padding: 2px 10px - border-radius: 9999px - font-size: 11px - font-weight: 500 - background-color: rgba(255, 185, 26, 0.2) - color: $accent-color - border: 1px solid rgba(255, 185, 26, 0.3) - transition: background-color 0.2s ease - cursor: pointer - text-decoration: none - &:hover - background-color: rgba(255, 185, 26, 0.3) - html.dark & - background-color: rgba(255, 185, 26, 0.15) - color: $dark-primary-color !important - border-color: rgba(255, 185, 26, 0.2) - &:hover - background-color: rgba(255, 185, 26, 0.25) - - .more-tags - color: $primary-color - font-size: 12px - font-weight: 500 - cursor: pointer - &:hover - text-decoration: underline - html.dark & - color: $dark-primary-color - .project-stats grid-column: 2 grid-row: 1 / 3 @@ -917,8 +1071,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) font-size: 16px font-weight: bold color: $primary-color - min-width: 70px - text-align: right + text-align: left html.dark & color: $dark-primary-color @@ -1006,24 +1159,45 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) display: flex justify-content: center align-items: center + + .project-tags + display: flex + flex-wrap: wrap + gap: 6px + align-items: center - .btn-use-this - padding: 6px 16px - background-color: $primary-color - color: #ffffff - border: none - border-radius: 8px - font-size: 12px - font-weight: 600 - cursor: pointer - transition: background-color 0.2s ease - &:hover - background-color: $primary-hover - html.dark & - background-color: $dark-primary-color - color: $dark-bg-color - &:hover - background-color: $dark-primary-hover + .tags-label + font-size: 12px + color: $text-secondary + margin-right: 4px + html.dark & + color: $dark-text-secondary + + .tag + padding: 2px 10px + border-radius: 9999px + font-size: 11px + font-weight: 500 + background-color: rgba(255, 185, 26, 0.2) + color: #1D0631 + transition: background-color 0.2s ease + cursor: pointer + text-decoration: none + &:hover + background-color: rgba(255, 185, 26, 0.3) + html.dark & + background-color: #ffb91a + color: #1D0631 + + .more-tags + color: $primary-color + font-size: 12px + font-weight: 500 + cursor: pointer + &:hover + text-decoration: underline + html.dark & + color: $dark-primary-color // Responsive adjustments @media (max-width: 1024px) diff --git a/app/assets/stylesheets/sidebar.sass b/app/assets/stylesheets/sidebar.sass index d8a2f583c..f6092085d 100644 --- a/app/assets/stylesheets/sidebar.sass +++ b/app/assets/stylesheets/sidebar.sass @@ -1,114 +1,367 @@ +// Design System Variables +$purple-dark: #1D0631 +$purple-primary: #5A2A82 +$purple-light: #9F7ABA +$yellow-accent: #FFB91A +$yellow-hover: #FFCC4D + +// Neutrals +$gray-50: #f9fafb +$gray-100: #f3f4f6 +$gray-200: #e5e7eb +$gray-300: #d1d5db +$gray-400: #9ca3af +$gray-500: #6b7280 +$gray-600: #4b5563 +$gray-700: #374151 +$gray-900: #111827 + +// Shadows +$shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05) +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06) + .footer-navigation + background: white + border-radius: 24px + margin: 0 16px + padding: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + margin-bottom: 24px + + @media (min-width: 640px) + padding: 32px + margin: 0 32px + margin-bottom: 32px + a - @include site-link-color - .sidebar-nav - line-height: 12px - &:hover - line-height: 12px - a - border-radius: 0px - padding-left: 5px - padding-right: 10px - - .nav-active - @include bottom-nav-active-colors - margin-left: -3px - - .nav-hover - @include bottom-nav-hover-colors - border-radius: 0px - margin-left: -3px - - .navbar-justify - border-radius: 0px - - .actions - line-height: 0.5em - h4 - font-size: 15px - h4.linked - padding: 6px - margin-top: 2px - .footer-nav - &.nav-active - @extend .nav-active - margin-left: 15px - padding-left: 0px - .footer-nav - &:hover - @extend .nav-hover - margin-left: 15px - padding-left: 18px - border-top: -10px - .footer-navbar, h4 - padding-top: 2px - padding-bottom: 6px - margin-top: 8px - margin-bottom: 0px - a - @include site-link-color - &:hover + &:link, + &:visited, + &:hover, + &:active + text-decoration: none + + // Desktop: 4-column grid + .footer-nav-grid + display: none + + @media (min-width: 768px) + display: grid + grid-template-columns: repeat(4, 1fr) + gap: 32px + + .nav-column + .column-header + display: flex + align-items: center + gap: 8px + margin-bottom: 12px + padding-bottom: 8px + border-bottom: 1px solid $gray-200 + + a + display: flex + align-items: center + gap: 8px text-decoration: none - .icon - background-image: asset_url('icons/ohloh-icon-sprite.png') - height: 24px - width: 24px - float: left - margin-right: 6px - margin-top: -3px - &.project_summary,&.account_summary,&.org_summary - background-position: -448px -8px - &.contributions - background-position: -768px -8px - &.recognition - background-position: -807px -8px - &.usage - background-position: -848px -8px - &.code_data - background-position: -489px -6px - margin-top: -5px - margin-left: 1px - &.scm_data - background-position: -528px -6px - margin-top: -5px - &.user_data - background-position: -568px -6px - margin-top: -4px - &.selected - @extend .nav-active, .navbar-justify - margin-right: 20px - padding: 6px - &:hover - @extend .nav-hover, .navbar-justify - margin-right: 20px - &.linked - &:hover - @extend .nav-hover, .navbar-justify - margin-right: 20px -@media only all and (min-width: 320px) and (max-width: 480px) - .col-xs-offset-1 - margin-left: 0px !important - .footer-navigation - .actions - margin-bottom: 0px !important - margin-top: 0px !important - line-height: 10px - h4 - font-size: 10px - .footer-nav - padding-left: 0px -@media only all and (min-width: 540px) and (max-width: 1024px) + flex: 1 + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .icon + display: inline-block + background-image: asset_url('icons/ohloh-icon-sprite.png') + width: 24px + height: 24px + flex-shrink: 0 + background-repeat: no-repeat + filter: brightness(0) saturate(100%) invert(24%) sepia(35%) saturate(1697%) hue-rotate(245deg) brightness(90%) contrast(90%) + + &.project_summary, + &.account_summary, + &.org_summary + background-position: -448px -8px + + &.contributions + background-position: -768px -8px + + &.recognition + background-position: -807px -8px + + &.usage + background-position: -848px -8px + + &.code_data + background-position: -489px -6px + + &.scm_data + background-position: -528px -6px + + &.user_data + background-position: -568px -6px + + h4 + font-size: 14px + font-weight: 600 + color: $gray-900 + margin: 0 + white-space: nowrap + + &.selected + color: $purple-primary + font-weight: 700 + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 8px + + &:last-child + margin-bottom: 0 + + &.nav-active + background-color: rgba(90, 42, 130, 0.08) + border-left: 3px solid $purple-primary + margin-left: -8px + padding-left: 5px + padding-top: 4px + padding-bottom: 4px + border-radius: 4px + + a + color: $purple-primary !important + font-weight: 600 + + a + font-size: 14px + color: #5A2A82 !important + text-decoration: none + transition: color 0.15s + + &:hover + text-decoration: underline + color: $purple-dark !important + + &:visited, + &:active + color: $purple-primary + + // Mobile: Accordion + .footer-nav-accordion + display: flex + flex-direction: column + + @media (min-width: 768px) + display: none + + .accordion-item + border-bottom: 1px solid $gray-200 + + &:last-child + border-bottom: none + + .accordion-header-btn + width: 100% + display: flex + align-items: center + justify-content: space-between + padding: 12px 0 + background: none + border: none + cursor: pointer + + .header-left + display: flex + align-items: center + gap: 10px + + svg + width: 20px + height: 20px + color: $purple-primary + flex-shrink: 0 + + .icon + display: inline-block + background-image: asset_url('icons/ohloh-icon-sprite.png') + width: 24px + height: 24px + flex-shrink: 0 + background-repeat: no-repeat + filter: brightness(0) saturate(100%) invert(24%) sepia(35%) saturate(1697%) hue-rotate(245deg) brightness(90%) contrast(90%) + + &.project_summary, + &.account_summary, + &.org_summary + background-position: -448px -8px + + &.contributions + background-position: -768px -8px + + &.recognition + background-position: -807px -8px + + &.usage + background-position: -848px -8px + + &.code_data + background-position: -489px -6px + + &.scm_data + background-position: -528px -6px + + &.user_data + background-position: -568px -6px + + span + font-size: 14px + font-weight: 600 + color: $gray-900 + + .chevron + width: 16px + height: 16px + color: $gray-600 + transition: transform 0.2s + + &.expanded + transform: rotate(180deg) + + .accordion-content-footer + padding-bottom: 12px + padding-left: 24px + display: none + + &.expanded + display: block + + ul + list-style: none + padding: 0 + margin: 0 + + li + margin-bottom: 6px + + &.nav-active + background-color: rgba(90, 42, 130, 0.08) + border-left: 3px solid $purple-primary + margin-left: -8px + padding-left: 5px + padding-top: 4px + padding-bottom: 4px + border-radius: 4px + + a + font-weight: 600 + + a + font-size: 14px + color: $purple-primary + text-decoration: none + + &:hover + text-decoration: underline + + &:visited, + &:active + color: $purple-primary + +// ============================================================================ +// Dark Mode +// ============================================================================ +html.dark, +body.dark, +.dark .footer-navigation - .actions - line-height: 12px -#page - .footer-nav - &:hover - @extend .nav-hover - margin-left: 10px - padding-left: 10px !important - font-size: 12px !important - &.nav-active - @extend .nav-active - margin-left: 0px !important - padding-left: 0px !important + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .footer-nav-grid + .nav-column + .column-header + border-bottom-color: rgba(159, 122, 186, 0.3) + + svg + color: $yellow-accent + + .icon + filter: brightness(0) saturate(100%) invert(84%) sepia(39%) saturate(1638%) hue-rotate(347deg) brightness(103%) contrast(98%) + + h4 + color: #f8fafc + + ul + li + &.nav-active + background-color: rgba(255, 185, 26, 0.12) + border-left: 3px solid $yellow-accent + margin-left: -8px + padding-left: 5px + padding-top: 4px + padding-bottom: 4px + border-radius: 4px + + a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + + .footer-nav-accordion + .accordion-item + border-bottom-color: rgba(159, 122, 186, 0.3) + + .accordion-header-btn + .header-left + svg + color: $yellow-accent + + .icon + filter: brightness(0) saturate(100%) invert(84%) sepia(39%) saturate(1638%) hue-rotate(347deg) brightness(103%) contrast(98%) + + span + color: #f8fafc + + .chevron + color: #94a3b8 + + .accordion-content-footer + ul + li + &.nav-active + background-color: rgba(255, 185, 26, 0.12) + border-left: 3px solid $yellow-accent + margin-left: -8px + padding-left: 5px + padding-top: 4px + padding-bottom: 4px + border-radius: 4px + + a + font-weight: 600 + + a + color: $yellow-accent !important + + &:visited, + &:active + color: $yellow-accent !important + +// ============================================================================ +// Backward Compatibility Stub +// ============================================================================ +// Keeping .actions for clearance.sass @extend compatibility +// The footer navigation no longer uses .actions, but clearance.sass +// has .submit-field that extends it +.actions + line-height: 0.5em + h4 + font-size: 15px diff --git a/app/assets/stylesheets/top_contributors_table.sass b/app/assets/stylesheets/top_contributors_table.sass new file mode 100644 index 000000000..cf4999e3f --- /dev/null +++ b/app/assets/stylesheets/top_contributors_table.sass @@ -0,0 +1,130 @@ +// Top Contributors Table Styling (Figma Design) +// This applies to all pages using _contributions.html.haml partial +// Targets tables with specific column structure (col-md-3 for Name column) + +// Card border styling for tables +table.table + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + transition: box-shadow 0.2s + padding: 0 24px + + @media (min-width: 768px) + padding: 0 32px + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + +// Only apply to tables that have the contributions structure +table.table + // Target only if table has thead with col-md-3 (Name column from contributions partial) + thead + + th.col-md-3 + text-align: center + vertical-align: middle + font-size: 11px + font-weight: 600 + color: #6b7280 + text-transform: uppercase + letter-spacing: 0.05em + border-bottom: none !important + + html.dark & + color: #9ca3af + +// Apply full styling to tables with contributions columns +table.table thead:has(th.col-md-3) + ~ tbody + tr + border-bottom: 1px solid #f3f4f6 + transition: background-color 0.2s + + html.dark & + border-bottom: 1px solid rgba(90, 42, 130, 0.2) + + &:hover + background-color: #f9fafb + + html.dark & + background-color: rgba(255, 255, 255, 0.05) + + td + text-align: center + padding: 12px + vertical-align: middle + border-bottom: none !important + + html.dark & + color: #e2e8f0 + + tr + border-bottom: 2px solid #e5e7eb + + html.dark & + border-bottom: 2px solid rgba(90, 42, 130, 0.4) + +// Fallback for browsers that don't support :has() +// Target tables in known contributors containers +.top-committers, +.col-md-12 + table.table + width: 100% + background: #ffffff !important + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + padding: 0 24px + + @media (min-width: 768px) + padding: 0 32px + + html.dark & + background: #2D1548 !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + thead + tr + border-bottom: 2px solid #e5e7eb + + html.dark & + border-bottom: 2px solid rgba(90, 42, 130, 0.4) + + th + text-align: center + vertical-align: middle + font-size: 11px + font-weight: 600 + color: #6b7280 + text-transform: uppercase + letter-spacing: 0.05em + border-bottom: none !important + + html.dark & + color: #9ca3af + + tbody + tr + border-bottom: 1px solid #f3f4f6 + transition: background-color 0.2s + + html.dark & + border-bottom: 1px solid rgba(90, 42, 130, 0.2) + + &:hover + background-color: #f9fafb + + html.dark & + background-color: rgba(255, 255, 255, 0.05) + + td + text-align: center + padding: 12px + vertical-align: middle + border-bottom: none !important + + html.dark & + color: #e2e8f0 diff --git a/app/views/contributions/_contributions.html.haml b/app/views/contributions/_contributions.html.haml index bb03597d7..d56907f74 100644 --- a/app/views/contributions/_contributions.html.haml +++ b/app/views/contributions/_contributions.html.haml @@ -1,57 +1,108 @@ -%table.table - %thead - %tr{ id: 'dingus-row' } - %td{ colspan: 10 } - - if search_bar - = render 'shared/search_dingus/commits_or_contributor_search', items: contributions, type: :contributions - - else -   - %tr - %th.col-md-3.center= t('.name') - %th{ width: '45px' }= t('.kudos') - %th.center{ width: '80px' }= t('.12_month_commits') - %th{ width: '80px' }= t('.all_commits') - %th= t('.trend') - %th= t('.language') - %th= t('.first_commit') - %th= t('.last_commit') - %tbody - - contributions.each do |contribution| - :ruby - person_name = contribution.person.person_name - fact = contribution.contributor_fact - contributor_path = project_contributor_path(@project, contribution) +// Desktop table view +.desktop-only + %table.table + %thead %tr - %td - = link_to contributor_path, class: 'pull-left' do - = avatar_img_for(contribution.person) + %th.col-md-3.center= t('.name') + %th{ width: '45px' }= t('.kudos') + %th.center{ width: '80px' }= t('.12_month_commits') + %th{ width: '80px' }= t('.all_commits') + %th= t('.trend') + %th= t('.language') + %th= t('.first_commit') + %th= t('.last_commit') + %tbody + - contributions.each do |contribution| + :ruby + person_name = contribution.person.person_name + fact = contribution.contributor_fact + contributor_path = project_contributor_path(@project, contribution) + %tr + %td + = link_to contributor_path, class: 'pull-left' do + = avatar_img_for(contribution.person) - = link_to contributor_path, class: 'avatar_name pull-left col-md-10 padding_left_0', title: person_name do - = h truncate(person_name, length: 23) - %span.small.contribution_title.pull-left - - if contribution.position.title.present? - = t('.position_title', title: contribution.position.title) - - elsif contribution.position.present? && !contribution.position.active? - = t('.inactive') - - elsif @project.active_managers.include?(contribution.person.account) - = t('.manager') - %td - - rank = contribution.person.kudo_rank || 1 - = image_tag "icons/sm_laurel_#{rank}.png", alt: "KudoRank #{rank}" - %td.center - = fact.twelve_month_commits - %td - = fact.commits - %td - = image_tag commits_spark_project_contributor_url(project_id: @project.to_param, id: fact.name_id), - width: 179, height: 32, align: 'left' if fact.name_id - %td - = fact.primary_language.nice_name - %td - - if fact.first_checkin - %abbr.date{ title: fact.first_checkin }= t('.ago', duration: time_ago_in_words(fact.first_checkin)) - %td - - if fact.last_checkin - %abbr.date{ title: fact.last_checkin }= t('.ago', duration: time_ago_in_words(fact.last_checkin)) + = link_to contributor_path, class: 'avatar_name pull-left col-md-10 padding_left_0', title: person_name do + = h truncate(person_name, length: 23) + %span.small.contribution_title.pull-left + - if contribution.position.title.present? + = t('.position_title', title: contribution.position.title) + - elsif contribution.position.present? && !contribution.position.active? + = t('.inactive') + - elsif @project.active_managers.include?(contribution.person.account) + = t('.manager') + %td + - rank = contribution.person.kudo_rank || 1 + = image_tag "icons/sm_laurel_#{rank}.png", alt: "KudoRank #{rank}" + %td.center + = fact.twelve_month_commits + %td + = fact.commits + %td + = image_tag commits_spark_project_contributor_url(project_id: @project.to_param, id: fact.name_id), + width: 179, height: 32 if fact.name_id + %td + = fact.primary_language.nice_name + %td + - if fact.first_checkin + %abbr.date{ title: fact.first_checkin }= t('.ago', duration: time_ago_in_words(fact.first_checkin)) + %td + - if fact.last_checkin + %abbr.date{ title: fact.last_checkin }= t('.ago', duration: time_ago_in_words(fact.last_checkin)) + +// Mobile card view +.contributors-cards-container.mobile-only + - contributions.each do |contribution| + :ruby + person_name = contribution.person.person_name + fact = contribution.contributor_fact + contributor_path = project_contributor_path(@project, contribution) + .contributor-card-item + .card-item-header + = link_to contributor_path, class: 'contributor-link' do + = avatar_img_for(contribution.person) + .contributor-name= h truncate(person_name, length: 30) + %button.expand-toggle{ type: 'button', 'aria-label' => 'Expand details' } + %i.fa.fa-chevron-down + + .card-item-content + .content-row + .content-item + .label= t('.12_month_commits') + .value= fact.twelve_month_commits + .content-item + .label= t('.primary_language') + .value= fact.primary_language.nice_name + + .content-row + .content-item + .label= t('.all_commits') + .value= fact.commits + .content-item + .label= t('.kudos') + .value + - rank = contribution.person.kudo_rank || 1 + = image_tag "icons/sm_laurel_#{rank}.png", alt: "KudoRank #{rank}" + = rank + + .content-row + .content-item + .label= t('.first_commit') + .value + - if fact.first_checkin + %abbr.date{ title: fact.first_checkin }= t('.ago', duration: time_ago_in_words(fact.first_checkin)) + .content-item + .label= t('.last_commit') + .value + - if fact.last_checkin + %abbr.date{ title: fact.last_checkin }= t('.ago', duration: time_ago_in_words(fact.last_checkin)) + + - if fact.name_id + .content-row.full-width + .content-item + .label= t('.trend') + .trend-image + = image_tag commits_spark_project_contributor_url(project_id: @project.to_param, id: fact.name_id), + width: 179, height: 32 = render partial: 'shared/alert', locals: { message: t('.no_data') } if contributions.empty? diff --git a/app/views/contributions/_newest_contributions.html.haml b/app/views/contributions/_newest_contributions.html.haml index f973e1e97..9fb4f36d4 100644 --- a/app/views/contributions/_newest_contributions.html.haml +++ b/app/views/contributions/_newest_contributions.html.haml @@ -1,7 +1,5 @@ %table.table.table-striped.table-condensed %thead - %tr{ id: 'dingus-row' } - %td{ colspan: '3' }   %tr %th.col-md-3.center= t('.name') %th.col-md-1.center= t('.commits') diff --git a/app/views/contributions/index.html.haml b/app/views/contributions/index.html.haml index 8c910e0b4..34981c36a 100644 --- a/app/views/contributions/index.html.haml +++ b/app/views/contributions/index.html.haml @@ -8,11 +8,11 @@ %h2.pull-left = link_to t('.contributors').html_safe, summary_project_contributors_path(@project) = t('.listing').html_safe - = project_analysis_timestamp(@project) .clearfix - if @project.best_analysis.present? - = render partial: 'contributions', locals: { contributions: @contributions, search_bar: true } + = render 'shared/search_dingus/commits_or_contributor_search', items: @contributions, type: :contributions, total_count: @contributions.total_entries + = render partial: 'contributions', locals: { contributions: @contributions } = will_paginate @contributions - else = render partial: 'projects/show/no_analysis_summary', locals: { analysis: @project.best_analysis } diff --git a/app/views/contributions/show.html.haml b/app/views/contributions/show.html.haml index b91926851..83ed51ed3 100644 --- a/app/views/contributions/show.html.haml +++ b/app/views/contributions/show.html.haml @@ -17,8 +17,6 @@ = link_to t('.contributors'), summary_project_contributors_path(@project) = committer_name -= project_analysis_timestamp(@project) - .clearfix = render partial: 'contributions/show/activity', locals: { committer: committer_name, account: account, fact: fact } diff --git a/app/views/contributions/summary.html.haml b/app/views/contributions/summary.html.haml index 2e0c5cca7..9eec3d816 100644 --- a/app/views/contributions/summary.html.haml +++ b/app/views/contributions/summary.html.haml @@ -8,8 +8,7 @@ = @project.name .project_content_title - %h2.pull-left= t('.contributors') - = project_analysis_timestamp(@project) + %h1.pull-left= t('.contributors') .clearfix - if @analysis.present? @@ -22,21 +21,36 @@ .chart.watermark440#committer_history_chart{ datasrc: committer_history_chart, style: 'height:320px' } .clearfix - .col-md-12 - %h4= link_to t('.newest_contributors'), project_contributors_path(@project, sort: 'newest') - .col-md-6 - = render partial: 'newest_contributions', locals: { contributions: @newest_contributions[0..4] } - - if @newest_contributions.length > 5 - .col-md-6 - = render partial: 'newest_contributions', locals: { contributions: @newest_contributions[5..-1] } - .clearfix - .clearfix + .contributors-section.newest-contributors-section + %h2.section-title + %svg.section-icon{width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width" => "2"} + %rect{width: "18", height: "18", x: "3", y: "4", rx: "2", ry: "2"} + %line{x1: "16", y1: "2", x2: "16", y2: "6"} + %line{x1: "8", y1: "2", x2: "8", y2: "6"} + %line{x1: "3", y1: "10", x2: "21", y2: "10"} + = link_to t('.newest_contributors'), project_contributors_path(@project, sort: 'newest'), class: 'section-title-link' + .two-column-layout.desktop-only + .column-left + .col-md-6 + = render partial: 'newest_contributions', locals: { contributions: @newest_contributions[0..4] } + - if @newest_contributions.length > 5 + .column-right + .col-md-6 + = render partial: 'newest_contributions', locals: { contributions: @newest_contributions[5..-1] } + .mobile-only + = render partial: 'newest_contributions', locals: { contributions: @newest_contributions } - .col-md-12.top-committers - %h4= link_to t('.top_contributions'), project_contributors_path(@project) - = render partial: 'contributions', locals: { contributions: @top_contributions, search_bar: false } - %p - = link_to t('.see_all'), project_contributors_path(@project), class: 'btn btn-info btn-small' + .contributors-section.top-contributors-section + %h2.section-title + %svg.section-icon{width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width" => "2"} + %path{d: "M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z"} + = link_to t('.top_contributions'), project_contributors_path(@project), class: 'section-title-link' + .col-md-12.top-committers + = render partial: 'contributions', locals: { contributions: @top_contributions } + %p.see-all-container + = link_to project_contributors_path(@project), class: 'btn' do + %i.fa.fa-users + %span= t('.see_all') .clearfix - else = render partial: 'projects/show/no_analysis_summary' diff --git a/app/views/layouts/partials/_footer_nav.html.haml b/app/views/layouts/partials/_footer_nav.html.haml index b5b555825..c011f5576 100644 --- a/app/views/layouts/partials/_footer_nav.html.haml +++ b/app/views/layouts/partials/_footer_nav.html.haml @@ -5,19 +5,9 @@ .clearfix - if responsive_layout && page_context[:footer_menu_list] .clearfix - .row.fluid.mezzo - .footer-navigation.fluid.col-xs-12 - .row - .col-md-12 - .mezzo.margin_bottom_5 - .clearfix - .col-xs-10.col-xs-offset-1 - = render 'layouts/partials/navigator' + .footer-navigation + = render 'layouts/partials/navigator' - elsif page_context[:footer_menu_list] .clearfix.margin_top_15 - .margin_left_20.margin_right_20 - .footer-navigation - .row - .col-md-12 - .mezzo.margin_bottom_5 - = render 'layouts/partials/navigator' + .footer-navigation + = render 'layouts/partials/navigator' diff --git a/app/views/layouts/partials/_navigator.html.haml b/app/views/layouts/partials/_navigator.html.haml index ecb3ef3be..616c4e88e 100644 --- a/app/views/layouts/partials/_navigator.html.haml +++ b/app/views/layouts/partials/_navigator.html.haml @@ -1,27 +1,69 @@ -- page_context[:footer_menu_list].each do |section| - .actions.margin_top_20.margin_bottom_40{ class: ('col-md-3 col-xs-3 no_padding' if page_context[:nav_type].eql?('footer_nav')) } - %ul.nav.nav-stacked.nav-pills +.footer-nav-grid + - page_context[:footer_menu_list].each do |section| + .nav-column - header, *links = section - header_class, header_name, header_url, header_type = header - %h4{ class: [('selected' if header_class == page_context[:select_footer_nav]), ('linked' if header_url)] } + + .column-header - if header_url - %a{ href: header_url } + = link_to header_url do %i{ class: "icon #{header_class}" } - = header_name + %h4{ class: [('selected' if header_class == page_context[:select_footer_nav]), 'linked'] }= header_name - else %i{ class: "icon #{header_class}" } - = header_name + %h4{ class: ('selected' if header_class == page_context[:select_footer_nav]) }= header_name + - if header_type.eql?('select') = select(:language, :url, options_for_select(links), {}, id: 'nav-select', class: 'chzn-select') - else - - links.each do |link| - - link_class, link_name, link_url = link - %li.footer-nav{ class: ('nav-active' if link_class == page_context[:select_footer_nav]) } - - if link_name == 'SBOM' - = link_to h(link_name), link_url, remote: true, - data: { toggle: 'modal', target: '#sbom-details.modal', keyboard: true } - - else - %a{ href: link_url }= link_name + %ul + - links.each do |link| + - link_class, link_name, link_url = link + %li{ class: ('nav-active' if link_class == page_context[:select_footer_nav]) } + - if link_name == 'SBOM' + = link_to h(link_name), link_url, remote: true, + data: { toggle: 'modal', target: '#sbom-details.modal', keyboard: true } + - else + %a{ href: link_url }= link_name + +.footer-nav-accordion + - page_context[:footer_menu_list].each do |section| + .accordion-item + - header, *links = section + - header_class, header_name, header_url, header_type = header + + %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } + .header-left + %i{ class: "icon #{header_class}" } + %span{ class: ('selected' if header_class == page_context[:select_footer_nav]) }= header_name + %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %polyline{ points: "6 9 12 15 18 9" } + + - if header_type.eql?('select') + .accordion-content-footer{ style: "display: none;" } + = select(:language, :url, options_for_select(links), {}, id: 'nav-select-mobile', class: 'chzn-select') + - else + .accordion-content-footer{ style: "display: none;" } + %ul + - links.each do |link| + - link_class, link_name, link_url = link + %li{ class: ('nav-active' if link_class == page_context[:select_footer_nav]) } + - if link_name == 'SBOM' + = link_to h(link_name), link_url, remote: true, + data: { toggle: 'modal', target: '#sbom-details.modal', keyboard: true } + - else + %a{ href: link_url }= link_name + +:javascript + function toggleFooterAccordion(button) { + const content = button.nextElementSibling; + const chevron = button.querySelector('.chevron'); + const isExpanded = content.style.display === 'block'; + + content.style.display = isExpanded ? 'none' : 'block'; + chevron.classList.toggle('expanded', !isExpanded); + } + #avatar-dialog.modal.pmd-modal.fade#sbom-details{"aria-hidden" => "true", :style => "display: none;", :tabindex => "-1"} .modal-dialog.modal-md .modal-content diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index 72a1e1700..0119696c1 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -67,17 +67,6 @@ - else = t('.no_declared_licenses') - / Tags - .project-tags - - unless project.tag_list.blank? - %span.tags-label= t('.tags') - - tags = project.tag_list.split(' ') - - tags.first(8).each do |tag| - - tag = tag.fix_encoding_if_invalid - = link_to html_escape(tag), tags_path(names: tag), class: 'tag' - - if tags.length > 8 - %span.more-tags= t('.n_more', n: tags.length - 8) - / Stats - Right Side .project-stats @@ -137,3 +126,14 @@ - else %button.btn-use-this{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' } = t('.i_use_this') + + / Tags + .project-tags + - unless project.tag_list.blank? + %span.tags-label= t('.tags') + - tags = project.tag_list.split(' ') + - tags.first(8).each do |tag| + - tag = tag.fix_encoding_if_invalid + = link_to html_escape(tag), tags_path(names: tag), class: 'tag' + - if tags.length > 8 + %span.more-tags= t('.n_more', n: tags.length - 8) \ No newline at end of file diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 4c94e2bdc..f6417ba7a 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,9 +1,6 @@ :ruby content_for(:html_title) { t('.page_title', name: @project.name) } page_context[:select_top_menu_nav] = 'select_projects' - page_context[:select_footer_nav] = nil # Disable old footer navigation - page_context[:footer_menu_list] = nil # Disable old footer navigation - page_context[:page_header] = nil # Disable default header, using redesigned header instead - content_for :twitter_card do %meta{ content: 'summary', name: 'twitter:card' } @@ -15,8 +12,6 @@ %meta{ content: 'https://www.openhub.net', name: 'twitter:domain' } .project-show-redesign{ itemscope: '', itemtype: 'http://schema.org/SoftwareSourceCode' } - / Project Header with Gradient - = render partial: 'projects/show/header_redesign' .project-content / Stats Grid - 3 columns (Lines of Code, Contributors, Total Commits) @@ -312,126 +307,6 @@ - if @analysis.present? = render partial: 'projects/show/analysis_summary_redesign' - / Project Navigation Footer - .footer-nav-section - / Desktop: 4-column grid - .footer-nav-grid - .nav-column - .column-header - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %circle{ cx: "12", cy: "12", r: "10" } - %line{ x1: "12", y1: "16", x2: "12", y2: "12" } - %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } - %h4= t('.project_summary') - %ul - %li= link_to t('.news'), project_rss_subscriptions_path(@project) - %li= link_to t('.settings'), settings_project_path(@project) - %li= link_to t('.sharing_widgets'), project_widgets_path(@project) - %li= link_to t('.related_projects'), similar_project_path(@project) - - .nav-column - .column-header - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "16 18 22 12 16 6" } - %polyline{ points: "8 6 2 12 8 18" } - %h4= t('.code_data') - %ul - %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') - %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) - %li= link_to t('.security.title'), security_project_path(@project) - - .nav-column - .column-header - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %rect{ x: "3", y: "3", width: "7", height: "7" } - %rect{ x: "14", y: "3", width: "7", height: "7" } - %rect{ x: "14", y: "14", width: "7", height: "7" } - %rect{ x: "3", y: "14", width: "7", height: "7" } - %h4= t('.scm_data') - %ul - %li= link_to t('.commits'), summary_project_commits_path(@project) - %li= link_to t('.contributors'), summary_project_contributors_path(@project) - - .nav-column - .column-header - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } - %circle{ cx: "9", cy: "7", r: "4" } - %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } - %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } - %h4= t('.community_data') - %ul - %li= link_to t('.users'), users_project_path(@project) - %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) - %li= link_to t('.user_contributor_locations'), map_project_path(@project) - - / Mobile: Accordion - .footer-nav-accordion - .accordion-item - %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } - .header-left - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %circle{ cx: "12", cy: "12", r: "10" } - %line{ x1: "12", y1: "16", x2: "12", y2: "12" } - %line{ x1: "12", y1: "8", x2: "12.01", y2: "8" } - %span= t('.project_summary') - %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "6 9 12 15 18 9" } - .accordion-content-footer{ style: "display: none;" } - %ul - %li= link_to t('.news'), project_rss_subscriptions_path(@project) - %li= link_to t('.settings'), settings_project_path(@project) - %li= link_to t('.sharing_widgets'), project_widgets_path(@project) - %li= link_to t('.related_projects'), similar_project_path(@project) - - .accordion-item - %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } - .header-left - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "16 18 22 12 16 6" } - %polyline{ points: "8 6 2 12 8 18" } - %span= t('.code_data') - %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "6 9 12 15 18 9" } - .accordion-content-footer{ style: "display: none;" } - %ul - %li= link_to t('.languages.languages'), languages_summary_project_analysis_path(@project, id: 'latest') - %li= link_to t('.cost_estimates'), estimated_cost_project_path(@project) - %li= link_to t('.security.title'), security_project_path(@project) - - .accordion-item - %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } - .header-left - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %rect{ x: "3", y: "3", width: "7", height: "7" } - %rect{ x: "14", y: "3", width: "7", height: "7" } - %rect{ x: "14", y: "14", width: "7", height: "7" } - %rect{ x: "3", y: "14", width: "7", height: "7" } - %span= t('.scm_data') - %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "6 9 12 15 18 9" } - .accordion-content-footer{ style: "display: none;" } - %ul - %li= link_to t('.commits'), summary_project_commits_path(@project) - %li= link_to t('.contributors'), summary_project_contributors_path(@project) - - .accordion-item - %button.accordion-header-btn{ onclick: "toggleFooterAccordion(this)" } - .header-left - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } - %circle{ cx: "9", cy: "7", r: "4" } - %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } - %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } - %span= t('.community_data') - %svg.chevron{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %polyline{ points: "6 9 12 15 18 9" } - .accordion-content-footer{ style: "display: none;" } - %ul - %li= link_to t('.users'), users_project_path(@project) - %li= link_to t('.ratings_reviews'), summary_project_reviews_path(@project) - %li= link_to t('.user_contributor_locations'), map_project_path(@project) - :javascript function toggleAccordion(button) { const content = button.nextElementSibling; @@ -441,12 +316,3 @@ content.style.display = isExpanded ? 'none' : 'block'; chevron.classList.toggle('expanded', !isExpanded); } - - function toggleFooterAccordion(button) { - const content = button.nextElementSibling; - const chevron = button.querySelector('.chevron'); - const isExpanded = content.style.display === 'block'; - - content.style.display = isExpanded ? 'none' : 'block'; - chevron.classList.toggle('expanded', !isExpanded); - } diff --git a/app/views/projects/show/_header.html.haml b/app/views/projects/show/_header.html.haml index c1958de21..bdb72c4fa 100644 --- a/app/views/projects/show/_header.html.haml +++ b/app/views/projects/show/_header.html.haml @@ -1,57 +1,94 @@ -#project_masthead - .col-md-1.no_padding#project_icon - %a{ href: project_path(@project), itemprop: 'url' } - = @project.decorate.icon(:med) - - .col-md-11#project_header - .pull-left.project_title - %h1.float_left{ itemprop: 'name' } - = link_to h(@project.name), project_path(@project), style: 'color: black', itemprop: 'url' - %small.pull-left.clear_both - != link_to bootstrap_icon('icon-cogs', t('projects.header.settings')), settings_project_path(@project) - - if @project.is_a_duplicate - - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) - | - != link_to bootstrap_icon('icon-flag', t('projects.header.edit_duplicate')), - edit_project_duplicate_path(@project, @project.is_a_duplicate) - - else - | - = link_to bootstrap_icon('icon-flag', t('projects.header.duplicate')), new_project_duplicate_path(@project) - - if current_user_is_admin? - | - = link_to bootstrap_icon('icon-legal', t('projects.header.job')), oh_admin_project_jobs_path(@project) - - if current_user.present? && @project.analysis_updated_or_project_created_time < 1.week.ago - | - = link_to bootstrap_icon('icon-lock', t('projects.header.outdated')), report_outdated_project_path(@project), method: :put - .pull-right.no_padding#widgets - .pull-left - #add_this{ 'data-analytics-id' => ENV['ADDTHIS_ANALYTICS_ID'] } - %p - .addthis_custom_sharing.addthis - %script{ src: '//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-500da8c658f6dda7' } - .pull-right{ itemtype: 'CreativeWork', itemprop: 'interactionCount' } - #i_use_this_container - .use_count - = link_to number_with_delimiter(@project.user_count), users_project_path(@project) - - unless @project.deleted? - - if logged_in? - = render partial: 'projects/show/i_use_this', locals: { project: @project } +.project-header-gradient + .header-content + .header-main + .project-title-section + %h1{ itemprop: 'name' } + = @project.name + + .action-buttons + = link_to settings_project_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "3" } + %path{ d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" } + = t('.settings') + + - if @project.is_a_duplicate + - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) + = link_to edit_project_duplicate_path(@project, @project.is_a_duplicate), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } + %polyline{ points: "14 2 14 8 20 8" } + %line{ x1: "16", y1: "13", x2: "8", y2: "13" } + %line{ x1: "16", y1: "17", x2: "8", y2: "17" } + %polyline{ points: "10 9 9 9 8 9" } + = t('.edit_duplicate') + - else + = link_to new_project_duplicate_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %path{ d: "M4 15s1-1 4-1 5 2 9 2V3s-4 0-6-2-9-1-3 1-4 1-4 1z" } + %line{ x1: "4", y1: "22", x2: "4", y2: "15" } + = t('.report_duplicate') + + - if current_user_is_admin? + = link_to oh_admin_project_jobs_path(@project), class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %rect{ x: "3", y: "7", width: "18", height: "13", rx: "2", ry: "2" } + %path{ d: "M16 3v4M8 3v4M3 11h18" } + = t('projects.header.job') + + - if current_user.present? && @project.analysis_updated_or_project_created_time < 1.week.ago + = link_to report_outdated_project_path(@project), method: :put, class: 'action-btn action-btn-outline' do + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } + %circle{ cx: "12", cy: "12", r: "10" } + %polyline{ points: "12 6 12 12 16 14" } + = t('projects.header.outdated') + + .project-description{ itemprop: 'description' } + - if @project.description.present? + = truncate(@project.description.strip_tags, length: 200) - else - .btn.btn-mini.i_use_this_btn{ 'data-target' => '#LoginModal', 'data-toggle' => 'modal' }= t('.i_use_this') - .modal.fade#LoginModal{ 'aria-hidden' => 'true', 'aria-labelledby' => 'LoginModalLabel', :role => 'dialog' } - .modal-dialog.modal-sm - .modal-content{ style: 'width: 398px' } - .modal-header - %button.close{ 'aria-label' => 'Close', 'data-dismiss' => 'modal', :type => 'button' } - %span{ 'aria-hidden' => 'true' } × - %h4.modal-title#LoginModalLabel= t('.login_required') - .model-body - = render 'sessions/sign_in' - #project_header_activity_indicator - - project_activity_level_class(@project, :thirtyfive) - - project_activity_level_text(@project, :thirtyfive) - -.clearfix -.row.mezzo -= yield :header_title if content_for?(:header_title) -= render partial: 'layouts/partials/alert' + = t('.no_description') + + = project_analysis_timestamp(@project) + + / Stats and button (shown below on mobile, top-right on desktop) + .header-top-right + .activity-indicator-wrapper + / Activity indicator will be rendered here + - project_activity_level_class(@project, :thirtyfive) + - project_activity_level_text(@project, :thirtyfive) + + .stats-card + .stats-inner + / Users icon + %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } + %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } + %circle{ cx: "9", cy: "7", r: "4" } + %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } + %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } + .user-count + = number_with_delimiter(@project.user_count) + + - unless @project.deleted? + - if logged_in? + - if @project.stacks.where(account_id: current_user.id).any? + %button.i-use-this-btn.using{ 'data-project-id': @project.id } + = t('.you_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } + = t('.i_use_this') + - else + %button.i-use-this-btn.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } + = t('.i_use_this') + +/ Login Modal for non-logged in users +- unless logged_in? + .modal.fade#LoginModal{ 'aria-hidden': 'true', 'aria-labelledby': 'LoginModalLabel', role: 'dialog' } + .modal-dialog.modal-sm + .modal-content + .modal-header + %button.close{ 'aria-label': 'Close', 'data-dismiss': 'modal', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %h4.modal-title#LoginModalLabel= t('.login_required') + .model-body + = render 'sessions/sign_in' diff --git a/app/views/projects/show/_header_redesign.html.haml b/app/views/projects/show/_header_redesign.html.haml deleted file mode 100644 index 3c7cd197a..000000000 --- a/app/views/projects/show/_header_redesign.html.haml +++ /dev/null @@ -1,135 +0,0 @@ -.project-header-gradient - / Decorative gradient circle is handled by CSS - - / Top Right Stats & Button (Desktop only, hidden on mobile) - .header-top-right - .activity-indicator-wrapper - / Activity indicator will be rendered here - - project_activity_level_class(@project, :thirtyfive) - - project_activity_level_text(@project, :thirtyfive) - - .stats-card - .stats-inner - / Users icon - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } - %circle{ cx: "9", cy: "7", r: "4" } - %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } - %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } - .user-count - = number_with_delimiter(@project.user_count) - - - if logged_in? - - if @project.stacks.where(account_id: current_user.id).any? - %button.i-use-this-btn.using{ 'data-project-id': @project.id } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.you_use_this') - - else - %button.i-use-this-btn.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.i_use_this') - - else - %button.i-use-this-btn.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.i_use_this') - - .header-content - .header-main - .project-title-section - %h1{ itemprop: 'name' } - = @project.name - - .action-buttons - = link_to settings_project_path(@project), class: 'action-btn action-btn-outline' do - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } - %circle{ cx: "12", cy: "12", r: "3" } - %path{ d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" } - = t('.settings') - - - if @project.is_a_duplicate - - if current_user_is_admin? || (@current_account && @project.is_a_duplicate.account == @current_account) - = link_to edit_project_duplicate_path(@project, @project.is_a_duplicate), class: 'action-btn action-btn-outline' do - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } - %path{ d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" } - %polyline{ points: "14 2 14 8 20 8" } - %line{ x1: "16", y1: "13", x2: "8", y2: "13" } - %line{ x1: "16", y1: "17", x2: "8", y2: "17" } - %polyline{ points: "10 9 9 9 8 9" } - = t('.edit_duplicate') - - else - = link_to new_project_duplicate_path(@project), class: 'action-btn action-btn-outline' do - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } - %path{ d: "M4 15s1-1 4-1 5 2 9 2V3s-4 0-6-2-9-1-3 1-4 1-4 1z" } - %line{ x1: "4", y1: "22", x2: "4", y2: "15" } - = t('.report_duplicate') - - - if current_user_is_admin? - = link_to oh_admin_project_jobs_path(@project), class: 'action-btn action-btn-outline' do - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2", 'stroke-linecap': "round", 'stroke-linejoin': "round" } - %rect{ x: "3", y: "7", width: "18", height: "13", rx: "2", ry: "2" } - %path{ d: "M16 3v4M8 3v4M3 11h18" } - = t('projects.header.job') - - .project-description{ itemprop: 'description' } - - if @project.description.present? - = truncate(@project.description.strip_tags, length: 200) - - else - = t('.no_description') - - %p.timestamp - = t('.analyzed', time: time_ago_in_words(@project.analysis_updated_or_project_created_time)) - = t('.based_on_code', time: time_ago_in_words(@project.best_analysis.try(:logged_at))) if @project.best_analysis.try(:logged_at) - - / Mobile Stats & Button (shown only on mobile) - .header-mobile-stats - .activity-card-mobile - - project_activity_level_class(@project, :twentyfive) - - project_activity_level_text(@project, :twentyfive) - - .stats-card-mobile - .stats-top - %svg{ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", 'stroke-width': "2" } - %path{ d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" } - %circle{ cx: "9", cy: "7", r: "4" } - %path{ d: "M23 21v-2a4 4 0 0 0-3-3.87" } - %path{ d: "M16 3.13a4 4 0 0 1 0 7.75" } - %span.user-count= number_with_delimiter(@project.user_count) - %span.users-label= t('.users') - - - if logged_in? - - if @project.stacks.where(account_id: current_user.id).any? - %button.i-use-this-btn-mobile.using{ 'data-project-id': @project.id } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.you_use_this') - - else - %button.i-use-this-btn-mobile.not-using{ 'data-project-id': @project.id, 'data-toggle': "modal", 'data-target': "#IUseThisModal" } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.i_use_this') - - else - %button.i-use-this-btn-mobile.not-using{ 'data-toggle': "modal", 'data-target': "#LoginModal" } - %svg{ viewBox: "0 0 24 24", fill: "currentColor" } - %path{ d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" } - %polyline{ points: "22 4 12 14.01 9 11.01" } - = t('.i_use_this') - -/ Login Modal for non-logged in users -- unless logged_in? - .modal.fade#LoginModal{ 'aria-hidden': 'true', 'aria-labelledby': 'LoginModalLabel', role: 'dialog' } - .modal-dialog.modal-sm - .modal-content - .modal-header - %button.close{ 'aria-label': 'Close', 'data-dismiss': 'modal', type: 'button' } - %span{ 'aria-hidden': 'true' } × - %h4.modal-title#LoginModalLabel= t('.login_required') - .model-body - = render 'sessions/sign_in' diff --git a/app/views/projects/show/_no_analysis_summary.html.haml b/app/views/projects/show/_no_analysis_summary.html.haml index b3f8a5e6c..396940b13 100644 --- a/app/views/projects/show/_no_analysis_summary.html.haml +++ b/app/views/projects/show/_no_analysis_summary.html.haml @@ -7,17 +7,20 @@ message = t('.message_2') if no_code_available message = t('.message_3') if analysis_not_complete .no_analysis_message - .alert.alert-info.alert-block{ style: 'padding-left: 30px;' } - .title - %h3!= bootstrap_icon('icon-info-sign', message) - .indent{ style: 'margin-top: 15px;' } + .modern-info-card + .info-card-header + .info-icon-wrapper + %i.icon-info-sign + .info-content + %h3.info-title= message + .info-card-body - if no_recognized_code - %p= t('.help_1') + %p.info-text= t('.help_1') - if no_code_available %p= t('.help_2') %p= t('.help_3') %p{ align: 'center' } = link_to t('.add_a_code_location'), new_project_enlistment_path(@project), class: 'btn btn-large btn-primary' - if analysis_not_complete - - link = link_to(t('.check_its_progress'), project_enlistments_path(@project)) - %span.analysis_in_progress!= t('.how_it_computes', link: link) + - link = link_to(t('.check_its_progress'), project_enlistments_path(@project), class: 'info-link') + %p.info-text.analysis_in_progress!= t('.how_it_computes', link: link) diff --git a/app/views/shared/_analysis_timestamp.html.haml b/app/views/shared/_analysis_timestamp.html.haml index a534b9772..39730716d 100644 --- a/app/views/shared/_analysis_timestamp.html.haml +++ b/app/views/shared/_analysis_timestamp.html.haml @@ -1,4 +1,4 @@ -.pull-right.soft#analysis_timestamp +.pull-left.soft#analysis_timestamp %i - unless analysis.nil? %i.icon-time diff --git a/app/views/shared/search_dingus/_commits_or_contributor_search.html.haml b/app/views/shared/search_dingus/_commits_or_contributor_search.html.haml index 9677df480..364bc0934 100644 --- a/app/views/shared/search_dingus/_commits_or_contributor_search.html.haml +++ b/app/views/shared/search_dingus/_commits_or_contributor_search.html.haml @@ -9,15 +9,20 @@ = (@project.best_analysis.oldest_code_set_time - 12.months).to_s(:mdy) — = @project.best_analysis.oldest_code_set_time.to_s(:mdy) -.margin_top_20#search-dingus - %form.form-inline - .row +.search-filter-bar + %form + .search-filter-content = render 'shared/search_dingus/page_entries_info', collection: items, total_count: total_count - .col-md-5.no_padding - %label #{t('shared.search_dingus.search_bar.search_text')}   - = text_field_tag :query, params[:query] - = hidden_field_tag :time_span, params[:time_span] - %button.btn.btn-refresh{ type: 'Submit' } - %i.icon-refresh + + .search-section + %label{for: 'query'}= t('shared.search_dingus.search_bar.search_text') + .search-input-wrapper + %input#query{type: 'text', name: 'query', placeholder: t('shared.search_dingus.search_bar.search_text'), value: params[:query]} + = hidden_field_tag :time_span, params[:time_span] + %button.search-cancel-btn{ type: 'button', style: (params[:query].blank? ? 'display: none;' : '') } + %i.fa.fa-times + %button.search-refresh-btn{ type: 'submit' } + %i.fa.fa-refresh + - if type == :contributions = render 'shared/search_dingus/sort', sort_context: :contributions, filter_type: nil From e0ce79741d26e4f42d07c0453fcffcf953e36616 Mon Sep 17 00:00:00 2001 From: Niharika1117 <101638919+Niharika1117@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:22:57 +0530 Subject: [PATCH 31/53] OTWO-7604 Implement people landing page redesign per figma (#1881) --- app/assets/javascripts/search_dingus.js | 17 + app/assets/stylesheets/account.sass | 648 +++++++++++++----- app/assets/stylesheets/badges.sass | 82 ++- app/assets/stylesheets/base.sass | 17 +- app/assets/stylesheets/dark_theme.sass | 117 +++- app/assets/stylesheets/people.sass | 91 +-- app/assets/stylesheets/search-dingus.sass | 5 + app/views/accounts/_account.html.haml | 123 ++-- app/views/accounts/index.html.haml | 18 +- .../committers/_unclaimed_tile.html.haml | 8 +- app/views/explore/_projects.html.haml | 14 +- app/views/people/_claimed_person.html.haml | 120 ++-- app/views/people/_people.html.haml | 19 +- app/views/people/_unclaimed_person.html.haml | 40 +- app/views/people/index.html.haml | 11 +- app/views/shared/_global_search.html.haml | 16 + .../shared/search_dingus/_sort.html.haml | 4 +- config/locales/shared.en.yml | 5 + 18 files changed, 876 insertions(+), 479 deletions(-) create mode 100644 app/views/shared/_global_search.html.haml diff --git a/app/assets/javascripts/search_dingus.js b/app/assets/javascripts/search_dingus.js index 76f8c255c..140dafccc 100644 --- a/app/assets/javascripts/search_dingus.js +++ b/app/assets/javascripts/search_dingus.js @@ -72,6 +72,23 @@ var ohloh = (function builder($) { $('input[name="query"].search').trigger('click').focus(); }); + // Toggle sort dropdown open/close on button click. + // NOTE: sort button intentionally has NO data-toggle="dropdown" so Bootstrap's + // dropdown plugin does not interfere — both would toggle .open on the same click + // and cancel each other out, keeping the menu permanently closed. + $(document).on('click', '.sort-dropdown-btn', function() { + var $dropdown = $(this).closest('.custom-sort-dropdown'); + $('.custom-sort-dropdown').not($dropdown).removeClass('open'); + $dropdown.toggleClass('open'); + }); + + // Close any open sort dropdown when clicking outside it + $(document).on('click', function(e) { + if (!$(e.target).closest('.custom-sort-dropdown').length) { + $('.custom-sort-dropdown.open').removeClass('open'); + } + }); + // Handle custom sort dropdown $('.custom-sort-dropdown .sort-dropdown-item').on('click', function(e) { e.preventDefault(); diff --git a/app/assets/stylesheets/account.sass b/app/assets/stylesheets/account.sass index cbdaa6d07..7506dff72 100644 --- a/app/assets/stylesheets/account.sass +++ b/app/assets/stylesheets/account.sass @@ -1,85 +1,487 @@ -#accounts_index_page, #people_index_page - .account_details - width: 220px - margin-left: 15px +// ── People page max-width container ────────────────────────────────────────── +.people-page-container + max-width: 1280px + margin: 0 auto + padding: 32px 16px 0 + +.people-page-header + display: flex + flex-direction: column + gap: 16px + margin-bottom: 32px + @media only all and (min-width: 640px) + flex-direction: row + align-items: center + justify-content: space-between + .people-page-title + margin: 0 + +// ── People & Accounts page headings ────────────────────────────────────────── +.people-page-title + font-size: 24px + font-weight: 700 + color: #111827 + margin: 0 + @media only all and (min-width: 640px) + font-size: 30px + @media only all and (min-width: 1024px) + font-size: 36px + +.people-section-title + font-size: 20px !important + font-weight: 700 !important + color: #111827 + margin: 0 0 16px + line-height: 1.3 !important + @media only all and (min-width: 640px) + font-size: 24px !important + margin-bottom: 24px + +.accounts-back-link + display: inline-flex + align-items: center + gap: 6px + color: #4b5563 + text-decoration: none + font-size: 14px + margin-bottom: 24px + transition: color 0.2s + &:hover + color: #111827 + text-decoration: none + +.people-more-btn + display: block + width: 100% + background-color: #5A2A82 + color: #ffffff + border: none + padding: 8px 16px + font-weight: 600 + border-radius: 4px + font-size: 14px + text-align: center + text-decoration: none + margin-top: 12px + margin-bottom: 12px + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + transition: background-color 0.2s, box-shadow 0.2s + &:hover + background-color: #4A1A72 + color: #ffffff + text-decoration: none + @media only all and (min-width: 640px) + display: inline-block + width: auto + padding: 8px 24px + font-size: 16px + margin-top: 12px + +// ── People Card ─────────────────────────────────────────────────────────────── +.people-card + background: #ffffff + border-radius: 16px + border: 1px solid rgba(229, 231, 235, 0.8) + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + transition: border-color 0.3s, box-shadow 0.3s + margin-bottom: 16px + overflow: hidden + cursor: pointer + &:hover + border-color: #5A2A82 + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + +.people-card__body + padding: 16px + +// Card row — mobile: column, desktop: row +.people-card__row + display: flex + flex-direction: column + gap: 12px + +// Avatar + info wrapper — on desktop becomes display:contents +// so avatar & info become direct flex children of .people-card__row +.people-card__avatar-info + display: flex + align-items: flex-start + gap: 16px + + img, .gravatar + width: 80px + height: 80px + border-radius: 4px + object-fit: cover + flex-shrink: 0 + +.people-card__info + flex: 1 + min-width: 0 + +.people-card__name + font-size: 20px + font-weight: 600 + margin: 0 0 4px + line-height: 1.4 + a + color: #5a2a82 !important + text-decoration: none + &:hover + text-decoration: underline + +.people-card__meta + font-size: 13px + color: #6b7280 + margin: 2px 0 + a + color: #6b7280 + text-decoration: none + &:hover + color: #5A2A82 + text-decoration: underline + +// Commits — mobile: top border, desktop: left border +.people-card__commits + display: flex + flex-direction: column + align-items: center + justify-content: center + padding: 8px 0 + border-top: 1px solid #e5e7eb + +.people-card__commits-count + font-size: 30px + font-weight: 700 + color: #111827 + line-height: 1.1 + text-align: center + +.people-card__commits-label + font-size: 11px + color: #6b7280 + text-transform: uppercase + letter-spacing: 0.05em + text-align: center + margin-top: 4px + a + color: #6b7280 + text-decoration: none + font-size: 11px + &:hover + color: #5A2A82 + +// Activity — languages + projects +.people-card__activity + flex: 1 + min-width: 0 + display: flex + flex-direction: column + gap: 15px + +.people-card__row-langs, +.people-card__row-projects + display: flex + align-items: flex-start + gap: 8px + flex-wrap: wrap + +.people-card__label + font-size: 12px + color: #9ca3af + white-space: nowrap + padding-top: 2px + flex-shrink: 0 + +.people-card__lang-tags + display: flex + align-items: center + flex-wrap: wrap + gap: 6px + +.people-card__lang-primary + display: inline-block + padding: 2px 10px + background: linear-gradient(to right, #5A2A82, #7F3FA0) + color: #ffffff + font-size: 12px + font-weight: 600 + border-radius: 4px + a + color: #ffffff !important + text-decoration: none + &:hover + text-decoration: underline + +.people-card__lang-secondary + font-size: 13px + color: #374151 + a + color: #374151 + text-decoration: none + &:hover + color: #5A2A82 + +.people-card__no-lang + font-size: 13px + color: #9ca3af + font-style: italic + +.people-card__project-list + display: flex + align-items: center + flex-wrap: wrap + gap: 8px + +.people-card__project-item + display: flex + align-items: center + gap: 4px + img, .people-card__project-icon + width: 20px + height: 20px + border-radius: 3px + flex-shrink: 0 + +.people-card__project-name + font-size: 13px + color: #5A2A82 + text-decoration: none + &:hover + text-decoration: underline + +.people-card__other-links + font-size: 12px + margin-top: 2px + .people-card__link + color: #5A2A82 + text-decoration: none + &:hover + text-decoration: underline + .people-card__link-dot + color: #d1d5db + margin: 0 2px + +.people-card__no-commits + font-size: 14px + color: #9ca3af + font-style: italic + text-align: center + padding: 20px 0 + +// Kudos — mobile: top border row, desktop: column no border +.people-card__kudos + display: flex + align-items: center + justify-content: center + gap: 12px + padding-top: 8px + border-top: 1px solid #e5e7eb + flex-shrink: 0 + +.people-card__kudos-rank + font-size: 28px + font-weight: 700 + color: #111827 + text-align: center + +.people-card__kudos-badge + display: flex + align-items: center + +// ── Unclaimed Card ──────────────────────────────────────────────────────────── +.unclaimed-card__header + display: flex + align-items: center + justify-content: space-between + gap: 12px + margin-bottom: 16px + flex-wrap: wrap + +.unclaimed-card__name + font-size: 20px + font-weight: 600 + color: #5A2A82 + margin: 0 + line-height: 1.4 + a + color: #5A2A82 + text-decoration: none + &:hover + text-decoration: underline + +.unclaimed-card__btn + background-color: #5A2A82 !important + color: #ffffff !important + border: none !important + padding: 6px 14px !important + font-weight: 600 !important + height: auto !important + line-height: normal !important + border-radius: 6px !important + font-size: 13px !important + white-space: nowrap + transition: background-color 0.2s + &:hover + background-color: #4A1A72 !important + color: #ffffff !important + +// Ensure inputs and buttons with this class inherit font family and weight +input.unclaimed-card__btn, +button.unclaimed-card__btn + font-weight: 600 !important + font-family: inherit !important + -webkit-font-smoothing: antialiased !important + height: auto !important + line-height: normal !important + +.unclaimed-card__grid + display: grid + grid-template-columns: repeat(2, 1fr) + gap: 16px + +.unclaimed-card__item + position: relative + + // Modern tile layout to match OpenHub design (image + absolute commit badge) + display: flex + flex-direction: column + align-items: center + gap: 8px + min-width: 0 + padding: 8px + +.unclaimed-card__more + grid-column: 1 / -1 + display: flex + align-items: center + justify-content: center + a + font-size: 13px + color: #5A2A82 + text-decoration: none + &:hover + text-decoration: underline + +// Unclaimed item inner elements: logo and commit badge +.unclaimed-card__item + .logo position: relative - height: 128px - .commits_summary - @include commits-summary-background-color - width: 470px - margin: 5px 0px - height: 118px - .in - padding: 49px 2px - .project_summary - padding: 5px 10px 0px 7px - width: auto - min-height: 99px - div - height: 23px - .project_name - @include projects-summary-projects-name-background-color - color: white - border: 1px dashed white + img, p + width: 48px !important + height: 48px !important + border-radius: 8px + object-fit: cover + border: 1px solid #e5e7eb !important + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + @media only all and (min-width: 640px) + width: 64px !important + height: 64px !important + + p + // placeholder text inside logo (when no image available) + display: flex + align-items: center + justify-content: center + margin: 0 + font-size: 16px + font-weight: 600 + color: #6b7280 + background: #f3f4f6 + border-radius: 8px + @media only all and (min-width: 640px) font-size: 18px - line-height: 24px - width: 24px - height: 24px - text-align: center - float: left - margin-bottom: 0 - margin-top: 0 - margin-right: 2px - .language_summary - padding: 5px 7px 5px 9px - width: 129px - .language_name - width: 113px - margin-bottom: 5px - height: 28px - line-height: 28px - span - padding-left: 8px - a - color: inherit - .other_language - color: white - font-size: 10px - white-space: nowrap - line-height: 10px - margin-bottom: 0 - a - @include site-link-color - .more_commits_padding - padding-left: 10px - .no_commits - text-align: center - padding: 50px - font-size: 15px - color: black - .member_info + + .commits + // container for commit badge + label; keep in document flow so label sits below logo + font-size: 12px + display: inline-flex + flex-direction: column + align-items: center + justify-content: center + + a + color: #6a7282 !important + text-transform: uppercase + line-height: 1.3 + letter-spacing: 0.3px + + .logo .commit-count + // absolute badge overlayed on the project logo (positioned relative to .logo) position: absolute - bottom: 0px - left: 0px - .kudo_rank - margin-top: 0px - font-size: 30px - text-align: center - padding: 42px 5px 0px 5px - font-weight: 100 - width: 35px - color: #183867 - .commits_count - font-size: 27px - line-height: 30px - display: inline-block - padding: 31px 20px 20px + top: -6px + right: -6px + font-size: 12px + display: inline-flex + align-items: center + justify-content: center + background: linear-gradient(to right, #5A2A82, #7F3FA0) + color: #ffffff + padding: 2px 6px + border-radius: 4px + font-weight: 700 + box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) + @media only all and (min-width: 640px) + top: -8px + right: -8px + padding: 4px 8px + + .project_link + font-size: 12px + line-height: 1.4 text-align: center - width: 115px - div - font-size: 12px - text-align: center !important + width: 100% + overflow-wrap: break-word + word-break: break-word + cursor: pointer + @media only all and (min-width: 640px) + font-size: 14px + + a + color: #5A2A82 + &:hover + text-decoration: underline +// ── Desktop overrides (sm: 576px+) ─────────────────────────────────────────── +@media only all and (min-width: 576px) + .people-card__body + padding: 24px + + .people-card__row + flex-direction: row + align-items: flex-start + gap: 24px + + // Make avatar+info transparent so children become direct row flex items + .people-card__avatar-info + display: contents + img, .gravatar + width: 96px + height: 96px + border-radius: 4px + + // Commits: remove top border, add left border + .people-card__commits + border-top: none + border-left: 1px solid #e5e7eb + padding: 16px 24px + + .people-card__commits-count + font-size: 40px + + // Kudos: remove top border, become a column + .people-card__kudos + flex-direction: column + border-top: none + padding-top: 0 + gap: 4px + + // Unclaimed grid: flex wrap on desktop + .unclaimed-card__grid + display: flex + flex-wrap: wrap + gap: 24px + +// ── Account settings (unchanged) ───────────────────────────────────────────── #analysis_timestamp @include analysis-timestamp-color margin-top: 0px @@ -127,6 +529,7 @@ .settings_specifications width: 380px length: 88px + .verification-buttons-container width: 100% margin-left: auto @@ -142,94 +545,3 @@ .grecaptcha-badge display: none -@media only all and (min-width: 768px) and (max-width: 1024px) - #accounts_index_page - h1 - font-size: 24px - margin-bottom: 0 - margin-top: 10px - h3 - font-size: 18px - margin-bottom: 0 - margin-top: 10px - .account_details - width: 142px - margin-left: 5px - .commits_summary - width: 379px - .commits_count - width: auto - font-size: 15px - padding: 31px 0px 12px - line-height: 20px - .well - min-height: 142px - .language_summary - width: 100px !important - .language_name - width: 85px !important - .kudo_rank - font-size: 18px - padding: 0 - font-weight: 500 - margin-top: 30px !important - width: auto - .kudo_badge - margin-top: 28px !important - padding: 0 - #search-dingus label.paginate - font-size: 10px !important - label - font-size: 10px - #search-dingus button.btn - font-size: 10px -@media only all and (min-width: 320px) and (max-width: 480px) - #accounts_index_page - h1 - font-size: 18px - margin-bottom: 0 - margin-top: 10px - h3 - font-size: 12px - margin-bottom: 0 - margin-top: 10px - .account_details - width: auto - .commits_summary - width: auto - .commits_count - width: auto - font-size: 15px - padding: 31px 0px 12px - line-height: 20px - .kudo_rank - font-size: 24px - padding: 0 - margin-top: 35px - .kudo_badge - margin-top: 28px !important - padding: 0 - .language_name - width: 90px !important - .unclaimed_committers_box .header_row h4 - font-size: 12px - .well - min-height: 142px - .unclaimed_committers_box .header_row .btn-mini - font-size: 8px - .language_summary - width: 100px !important - #more_account - font-size: 10px - #more_unclaimed - font-size: 10px - #search-dingus label.paginate - font-size: 8px !important - label - font-size: 8px - #search-dingus button.btn - font-size: 8px - #search-dingus input - width: auto - #search-dingus select - width: auto diff --git a/app/assets/stylesheets/badges.sass b/app/assets/stylesheets/badges.sass index 0eeafc679..ea3f6a9d4 100644 --- a/app/assets/stylesheets/badges.sass +++ b/app/assets/stylesheets/badges.sass @@ -1,3 +1,20 @@ +.describer-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -48px 0 +.repo-person-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -288px 0 +.project-manager-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -240px 0 +.stacker-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -336px 0 +.org-manager-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -192px 0 +.fos-ser-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -96px 0 +.taxonomist-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat 0 0 +.kudo-rank-badge + background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -144px 0 + .mini-badges-section position: absolute height: 62px @@ -22,38 +39,39 @@ .kudo-rank-badge background: asset_url('badges/48-pixel-badge-sprite.png') no-repeat -144px 0 - .pip-0000 - background: asset_url('badges/pips-7-high.png') no-repeat 0 0 - .pip-0001 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -7px - .pip-0010 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -14px - .pip-0011 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -21px - .pip-0100 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -28px - .pip-0101 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -35px - .pip-0110 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -42px - .pip-0111 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -49px - .pip-1000 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -56px - .pip-1001 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -63px - .pip-1010 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -70px - .pip-1011 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -77px - .pip-1100 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -84px - .pip-1101 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -91px - .pip-1110 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -98px - .pip-1111 - background: asset_url('badges/pips-7-high.png') no-repeat 0 -105px + +.pip-0000 + background: asset_url('badges/pips-7-high.png') no-repeat 0 0 +.pip-0001 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -7px +.pip-0010 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -14px +.pip-0011 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -21px +.pip-0100 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -28px +.pip-0101 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -35px +.pip-0110 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -42px +.pip-0111 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -49px +.pip-1000 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -56px +.pip-1001 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -63px +.pip-1010 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -70px +.pip-1011 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -77px +.pip-1100 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -84px +.pip-1101 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -91px +.pip-1110 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -98px +.pip-1111 + background: asset_url('badges/pips-7-high.png') no-repeat 0 -105px .pips width: 37px diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 5d76be46e..0bada246d 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -1,14 +1,6 @@ @import oh-styles // base mixins -=sans_font - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" -=serif_font - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" -=arial_font - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" -=lucida_font - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" =small_font font-size: .91667em =tiny_font @@ -16,16 +8,9 @@ =link_blue @include site-link-color -// font awesome custom styles -#commits_index_page, -#commits_summary_page, -#mini_account_row - [class^="icon-"], [class*=" icon-"] - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important - body text-rendering: optimizeLegibility - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important a @include site-link-color diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index 8bfdced42..d5203447f 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -41,9 +41,9 @@ html.dark // Buttons (keep primary yellow button, update others) .btn - &:not(.btn-primary):not(.btn-success) - background-color: #ffb91a !important - color: #1D0631 !important + &:not(.btn-primary):not(.btn-success):not(.people-more-btn) + background-color: #334155 !important + color: #e2e8f0 !important &:hover background-color: #ffd966 !important @@ -273,3 +273,114 @@ html.dark // Add project text .add-project-text color: #9ca3af !important + + // ── People & Accounts page ──────────────────────────────────────────────── + .accounts-back-link + color: #9ca3af + &:hover + color: #f1f5f9 + + .people-page-title, + .people-section-title + color: #f1f5f9 !important + + .people-more-btn + background-color: #ffb91a + color: #1D0631 !important + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + &:hover + background-color: #e6a617 + color: #1D0631 + + .people-card + background-color: #2D1548 !important + border-color: rgba(255, 255, 255, 0.1) !important + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) !important + &:hover + border-color: #ffb91a !important + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) !important + + .people-card__name a + color: #ffb91a !important + &:hover + color: #ffb91a !important + + .people-card__meta, + .people-card__meta a + color: #9ca3af !important + + .people-card__commits + border-color: rgba(90, 42, 130, 0.3) !important + + .people-card__commits-count + color: #f1f5f9 !important + + .people-card__commits-label, + .people-card__commits-label a + color: #9ca3af !important + + .people-card__label + color: #9ca3af !important + + .people-card__lang-primary + background: linear-gradient(to right, #5A2A82, #7F3FA0) !important + a + color: #ffffff !important + + .people-card__lang-secondary a + color: #d1d5db !important + &:hover + color: #ffb91a !important + + .people-card__project-name + color: #9F7FBF !important + &:hover + color: #ffb91a !important + + .people-card__link + color: #9F7FBF !important + &:hover + color: #ffb91a !important + + .people-card__link-dot + color: #6b7280 !important + + .people-card__no-commits, + .people-card__no-lang + color: #9ca3af !important + + .people-card__kudos-rank + color: #f1f5f9 !important + + .unclaimed-card__header + border-bottom-color: rgba(90, 42, 130, 0.3) !important + + .unclaimed-card__name, + .unclaimed-card__name a + color: #ffb91a !important + + .unclaimed-card__btn + background-color: #ffb91a !important + color: #1D0631 !important + &:hover + background-color: #e6a617 !important + +// Ensure inputs and buttons with this class inherit font family and weight in dark theme +input.unclaimed-card__btn, +button.unclaimed-card__btn + font-weight: 600 !important + font-family: inherit !important + -webkit-font-smoothing: antialiased !important + height: auto !important + line-height: normal !important + + .unclaimed-card__more a + color: #9F7FBF !important + &:hover + color: #ffb91a !important + + .unclaimed-card__item .project_link a + color: #9F7FBF !important + + .people-card__kudos + border-color: rgba(90, 42, 130, 0.3) !important diff --git a/app/assets/stylesheets/people.sass b/app/assets/stylesheets/people.sass index c1414651b..479b5f0de 100644 --- a/app/assets/stylesheets/people.sass +++ b/app/assets/stylesheets/people.sass @@ -1,73 +1,20 @@ -@media only all and (min-width: 320px) and (max-width: 480px) - #people_index_page - h1 - font-size: 18px - margin-bottom: 0 - margin-top: 10px - h3 - font-size: 12px - margin-bottom: 0 - margin-top: 10px - .account_details - width: auto - .commits_summary - width: auto - .commits_count - width: auto - font-size: 15px - padding: 31px 0px 12px - line-height: 20px - .kudo_rank - font-size: 24px - padding: 0 - margin-top: 35px - .kudo_badge - margin-top: 28px !important - padding: 0 - .language_name - width: 90px !important - .unclaimed_committers_box .header_row h4 - font-size: 12px - .unclaimed_committers_box .header_row .btn-mini - font-size: 8px - .language_summary - width: 100px !important - #more_account - font-size: 10px - #more_unclaimed - font-size: 10px +// Figma-inspired section styling for unclaimed section +.unclaimed-section + margin-top: 40px + +// People index page — responsive container padding (preserves top padding from account.sass) +@media only all and (max-width: 480px) + .people-page-container + padding: 32px 12px 0 + +@media only all and (min-width: 481px) and (max-width: 767px) + .people-page-container + padding: 32px 16px 0 + @media only all and (min-width: 768px) and (max-width: 1024px) - #people_index_page - h1 - font-size: 24px - margin-bottom: 0 - margin-top: 10px - h3 - font-size: 18px - margin-bottom: 0 - margin-top: 10px - .account_details - width: 142px - margin-left: 5px - .commits_summary - width: 379px - .project_summary - padding: 0 - .commits_count - width: auto - font-size: 15px - padding: 31px 0px 12px - line-height: 20px - .language_summary - width: 100px !important - .language_name - width: 85px !important - .kudo_rank - font-size: 18px - padding: 0 - font-weight: 500 - margin-top: 30px !important - width: auto - .kudo_badge - margin-top: 28px !important - padding: 0 + .people-page-container + padding: 32px 24px 0 + +@media only all and (min-width: 1025px) + .people-page-container + padding: 32px 32px 0 diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index f2b1faa6f..777df6626 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -387,7 +387,12 @@ font-size: 12px transition: transform 0.2s ease + &.open + .sort-dropdown-menu + display: block + .sort-dropdown-menu + display: none position: absolute top: 100% left: 0 diff --git a/app/views/accounts/_account.html.haml b/app/views/accounts/_account.html.haml index ba95d19a3..9ca4daf65 100644 --- a/app/views/accounts/_account.html.haml +++ b/app/views/accounts/_account.html.haml @@ -3,73 +3,68 @@ pos_ids, more_pos_count = @cbp_map[account.id] sorted_cbl = account.decorate.sorted_commits_by_language -.well.well-sm.no_padding - .pull-left.avatar_new - = avatar_for account, size: 128 - - .pull-left.account_details - %h3 - = link_to h(truncate(account.name, length: 18)), account_path(account), class: 'margin_top_20', - title: h(account.name) - %p.small.word_break_all= truncate(account.markup.try(:first_line), length: 75) - .small.member_info - - if account.projects.any? - - manages_count_text = t 'people.claimed_person.manages', what: pluralize(account.projects.count, 'project') - %p= link_to manages_count_text, account_projects_path(account) - %p= t '.member_since', since: account.created_at.strftime('%B %Y') - - if only_device? - .pull-left.kudo_rank.signature_color - = account.kudo_rank - - .mini-badges-section.pull-left.kudo_badge - = render 'badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] - .pull-left.commits_summary - - if vf.commits > 0 && @positions_map.any? - .commits_count.pull-left - = vf.commits - - .text-center - = link_to pluralize_without_count(vf.commits, 'Commit'), account_positions_path(account) - .pull-left.in= t('.in') - - if sorted_cbl.empty? - .pull-left.language_summary.no_lang_summary - = t('.no_lang_summary') - - else - .pull-left.language_summary - .pull-left{ style: 'min-height: 99px' } - - sorted_cbl.first(3).each do |lang, lang_hash| - .language_name{ style: "background-color:##{language_color(lang)}; color: ##{language_text_color(lang)}" } - %span= link_to lang_hash[:nice_name], language_path(lang) - .pull-left.other_language +.people-card + .people-card__body + .people-card__row + .people-card__avatar-info + = avatar_for account, size: 96 + .people-card__info + %h3.people-card__name + = link_to h(truncate(account.name, length: 18)), account_path(account), title: h(account.name) + %p.people-card__meta.word_break_all= truncate(account.markup.try(:first_line), length: 75) + - if account.projects.any? + - manages_count_text = t 'people.claimed_person.manages', what: pluralize(account.projects.count, 'project') + %p.people-card__meta= link_to manages_count_text, account_projects_path(account) + %p.people-card__meta= t '.member_since', since: account.created_at.strftime('%B %Y') + .people-card__commits + - if vf.commits > 0 + .people-card__commits-count= vf.commits + .people-card__commits-label + = link_to pluralize_without_count(vf.commits, 'Commit'), account_positions_path(account) + - else + .people-card__commits-count 0 + .people-card__commits-label Commits + .people-card__activity + - if vf.commits > 0 && @positions_map.any? + .people-card__row-langs + %span.people-card__label= t('.in') + .people-card__lang-tags + - if sorted_cbl.empty? + %span.people-card__no-lang= t('.no_lang_summary') + - else + - sorted_cbl.first(3).each_with_index do |(lang, lang_hash), i| + - if i == 0 + %span.people-card__lang-primary + = link_to lang_hash[:nice_name], language_path(lang) + - else + %span.people-card__lang-secondary + = link_to lang_hash[:nice_name], language_path(lang) + .people-card__row-projects + %span.people-card__label= t('.to') + .people-card__project-list + - pos_ids.each do |pos_id| + - project = @positions_map[pos_id].try(:project) + - next unless project + .people-card__project-item + = project.decorate.icon(:small, width: 20) + = link_to h(truncate(project.name, length: 10)), project_path(project), class: 'people-card__project-name' + %p.people-card__other-links - if sorted_cbl.length > 3 - other_langs_count = sorted_cbl.length - 3 - pluaral_term = pluralize_without_count(other_langs_count, 'language') - langs_link = t('.langs_link_text1', other_langs_count: other_langs_count, pluaral_term: pluaral_term) - = link_to langs_link, account_languages_path(account) + = link_to langs_link, account_languages_path(account), class: 'people-card__link' - else - = link_to t('.langs_link_text2'), account_languages_path(account) - - .pull-left.in= t('.to') - .pull-left.project_summary - - pos_ids.each do |pos_id| - - project = @positions_map[pos_id].try(:project) - - next unless project - .margin_bottom_10 - = project.decorate.icon(:small, width: 24) - = link_to h(truncate(project.name, length: 10)), project_path(project), class: 'margin_left_5' - .pull-left.other_language.other_projects - - if more_pos_count > 0 - - pluaral_term1 = pluralize_without_count(other_langs_count, 'project') - = link_to t('.positions_link_text1', more_pos_count: more_pos_count, pluaral_term: pluaral_term1), - account_positions_path(account), class: 'more_commits_padding' + = link_to t('.langs_link_text2'), account_languages_path(account), class: 'people-card__link' + %span.people-card__link-dot • + - if more_pos_count > 0 + - pluaral_term1 = pluralize_without_count(more_pos_count, 'project') + = link_to t('.positions_link_text1', more_pos_count: more_pos_count, pluaral_term: pluaral_term1), account_positions_path(account), class: 'people-card__link' + - else + = link_to t('.positions_link_text2'), account_positions_path(account), class: 'people-card__link' - else - = link_to t('.positions_link_text2'), account_positions_path(account), class: 'more_commits_padding' - - else - %p.no_commits= t('.no_commits') - - unless only_device? - .pull-left.kudo_rank.signature_color - = account.kudo_rank - - .mini-badges-section.pull-left.kudo_badge - = render 'badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] - .clear + %p.people-card__no-commits= t('.no_commits') + .people-card__kudos + .people-card__kudos-rank= account.kudo_rank + .people-card__kudos-badge + = render 'badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] diff --git a/app/views/accounts/index.html.haml b/app/views/accounts/index.html.haml index 4bd342af1..871ef5ba7 100644 --- a/app/views/accounts/index.html.haml +++ b/app/views/accounts/index.html.haml @@ -1,10 +1,18 @@ - content_for(:html_title) { t('.page_title') } - page_context[:select_top_menu_nav] = 'select_people' -%h1.margin_bottom_15= t '.people' -%h3.margin_bottom_15= t '.header' +.people-page-container + = link_to people_path, class: 'accounts-back-link' do + ← Back to contributors -= render partial: 'shared/search_dingus', locals: { collection: @people, sort_context: :people } -= render partial: 'account', collection: @people.map(&:account) + .people-page-header + %h1.people-page-title= t '.people' + = render 'shared/global_search', placeholder: t('shared.global_search.placeholder') -= will_paginate @people + %h2.people-section-title= t '.header' + + = render partial: 'shared/search_dingus', locals: { collection: @people, sort_context: :people } + + = render partial: 'account', collection: @people.map(&:account) + + = will_paginate @people diff --git a/app/views/committers/_unclaimed_tile.html.haml b/app/views/committers/_unclaimed_tile.html.haml index f2852dc5b..2a787602b 100644 --- a/app/views/committers/_unclaimed_tile.html.haml +++ b/app/views/committers/_unclaimed_tile.html.haml @@ -10,12 +10,10 @@ .logo %a{ href: project_path(project), title: project.name } - project_icon(project, :small, class: 'logo') -.commits{ style: "background-color: ##{bg_color}; color: ##{txt_color} !important", title: emails.join("\n") } - %span= name_fact.commits - .clearfix + %span.commit-count= name_fact.commits +.commits{ title: emails.join("\n") } %a{ href: project_contributor_path(project, contributor_id), style: 'display: inline-block;' } - = 'commit'.pluralize(name_fact.commits).to_s -.clearfix + = 'COMMITS' .project_link = link_to h(truncate(project.name, length: 14)), project_path(project), title: project.name diff --git a/app/views/explore/_projects.html.haml b/app/views/explore/_projects.html.haml index 7bfbe527b..b635d120b 100644 --- a/app/views/explore/_projects.html.haml +++ b/app/views/explore/_projects.html.haml @@ -13,19 +13,7 @@ = t('.add_new_project', default: 'Add New Project') / Search Bar with Filters (uses search-dingus styles) - %form.header-search-form{ action: projects_path, method: :get, role: 'search' } - .btn-group.ux-dropdown.header-search-dropdown - %button.search-filter-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } - %span.selection{ val: 'p' }= t('.projects', default: 'Projects') - %i.fa.fa-chevron-down - .dropdown-menu.search-dropdown-menu - %button.dropdown-item.active{ type: 'button', val: 'p' }= t('.projects', default: 'Projects') - %button.dropdown-item{ type: 'button', val: 'people' }= t('.people', default: 'People') - %button.dropdown-item{ type: 'button', val: 'orgs' }= t('.orgs', default: 'Organizations') - = text_field_tag :query, params[:query], placeholder: t('.search_placeholder', default: 'Search projects...'), class: 'header-search-input' - %input.search.hidden{ type: 'hidden', name: 'search_type', id: 'search_type_explore', value: 'projects' } - %button.header-search-btn{ type: 'submit' } - %i.fa.fa-search + = render 'shared/global_search', placeholder: t('.search_placeholder', default: 'Search projects...') / Mobile - Discover More Collapsible .discover-more-mobile diff --git a/app/views/people/_claimed_person.html.haml b/app/views/people/_claimed_person.html.haml index c3d6aed8c..6c7f9c2df 100644 --- a/app/views/people/_claimed_person.html.haml +++ b/app/views/people/_claimed_person.html.haml @@ -10,70 +10,64 @@ res end sorted_cbl = cbl.sort_by { |_, v| v[:commits] }.reverse -.well.no_padding - .pull-left.avatar_new - = avatar_for account, size: 128 - .pull-left.account_details - %h3 - %a.margin_top_20{ href: account_path(account), title: account.name }= truncate(account.name, length: 18) - - markup_raw = account.markup ? account.markup.raw : '' - %p.small.word_break_all= truncate(Markup.create(raw: markup_raw).first_line, length: 75) - .small.member_info - - if account.projects.any? - %p - - what = pluralize(account.projects.count, t('.project')) - %a{ href: account_projects_path(account) }= t('.manages', what: what) - %p= t('.open_hub_member_since', when: account.created_at.strftime('%B %Y')) - -if only_device? - .pull-left.kudo_rank - = claimed_person.kudo_rank || '1' - .mini-badges-section.pull-left.kudo_badge - = render 'accounts/badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] - .pull-left.commits_summary - - if vf && vf.commits > 0 && @positions_map.any? - .commits_count.pull-left - = vf.commits - %br - %span - %a{ href: account_positions_path(account), style: 'font-size: 12px;' } - = pluralize_without_count(vf.commits, t('.commit')) - .pull-left.in= t('.in_dots') - - if sorted_cbl.empty? - .pull-left.language_summary.no_lang_summary!= t('.no_detected_languages') - - else - .pull-left.language_summary - .pull-left{ style: 'min-height: 99px;' } - - sorted_cbl.first(3).each do |lang_name, lang_hash| - - style = "background-color:##{language_color(lang_name)}; color: ##{language_text_color(lang_name)};" - .language_name{ style: style } - %span= link_to lang_hash[:nice_name], language_path(lang_name) - .pull-left.other_language +.people-card + .people-card__body + .people-card__row + .people-card__avatar-info + = avatar_for account, size: 96 + .people-card__info + %h3.people-card__name + = link_to truncate(account.name, length: 18), account_path(account), title: account.name + - if account.projects.any? + %p.people-card__meta + - what = pluralize(account.projects.count, t('.project')) + = link_to t('.manages', what: what), account_projects_path(account) + %p.people-card__meta= t('.open_hub_member_since', when: account.created_at.strftime('%B %Y')) + .people-card__commits + .people-card__commits-count= vf && vf.commits > 0 ? vf.commits : 0 + .people-card__commits-label + - if vf && vf.commits > 0 + = link_to pluralize_without_count(vf.commits, t('.commit')), account_positions_path(account) + - else + = t('.commit').pluralize + .people-card__activity + - if vf && vf.commits > 0 && @positions_map.any? + .people-card__row-langs + %span.people-card__label= t('.in_dots') + .people-card__lang-tags + - if sorted_cbl.empty? + %span.people-card__no-lang= t('.no_detected_languages') + - else + - sorted_cbl.first(3).each_with_index do |(lang_name, lang_hash), i| + - if i == 0 + %span.people-card__lang-primary= link_to lang_hash[:nice_name], language_path(lang_name) + - else + %span.people-card__lang-secondary= link_to lang_hash[:nice_name], language_path(lang_name) + .people-card__row-projects + %span.people-card__label to... + .people-card__project-list + - pos_ids.each do |pos_id| + - project = @positions_map[pos_id].project + - next unless project + .people-card__project-item + %a{ href: project_path(project), title: project.name } + - project_icon(project, :small, width: 20, class: 'people-card__project-icon') + = link_to truncate(project.name, length: 8), project_path(project), class: 'people-card__project-name' + %p.people-card__other-links - if sorted_cbl.length > 3 - pluaral_term = pluralize(sorted_cbl.length - 3, t('.other_language')) - %a{ href: account_languages_path(account) }= t('.and_thing', thing: pluaral_term) + = link_to t('.and_thing', thing: pluaral_term), account_languages_path(account), class: 'people-card__link' - else - %a{ href: account_languages_path(account) }= t('.see_commits_by_languages') - - .pull-left.in to ... - .pull-left.project_summary - - pos_ids.each do |pos_id| - - project = @positions_map[pos_id].project - - next unless project - .margin_bottom_10 - %a{ href: project_path(project), title: project.name } - - project_icon(project, :small, width: 24, class: 'logo') - %a.margin_left_5{ href: project_path(project) }= truncate(project.name, length: 8) - .pull-left.other_language.other_projects - - if more_pos_count > 0 - - pluaral_term = pluralize(more_pos_count, t('.other_project')) - %a.more_commits_padding{ href: account_positions_path(account) }= t('.and_thing', thing: pluaral_term) + = link_to t('.see_commits_by_languages'), account_languages_path(account), class: 'people-card__link' + %span.people-card__link-dot • + - if more_pos_count > 0 + - pluaral_term = pluralize(more_pos_count, t('.other_project')) + = link_to t('.and_thing', thing: pluaral_term), account_positions_path(account), class: 'people-card__link' + - else + = link_to t('.see_commits_by_project'), account_positions_path(account), class: 'people-card__link' - else - %a.more_commits_padding{ href: account_positions_path(account) }= t('.see_commits_by_project') - - else - %p.no_commits No commit data to display - - unless only_device? - .pull-left.kudo_rank - = claimed_person.kudo_rank || '1' - .mini-badges-section.pull-left.kudo_badge - = render 'accounts/badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] - .clear_both + %p.people-card__no-commits No commit data to display + .people-card__kudos + .people-card__kudos-rank= claimed_person.kudo_rank || '1' + .people-card__kudos-badge + = render 'accounts/badges_row', badges: [BadgeDecorator.new(Badge::KudoRankBadge.new(account))] diff --git a/app/views/people/_people.html.haml b/app/views/people/_people.html.haml index 7a64bdde6..028e6af20 100644 --- a/app/views/people/_people.html.haml +++ b/app/views/people/_people.html.haml @@ -1,14 +1,13 @@ - if @claimed_people.any? - %h3.margin_bottom_15= t('.account_holders') + %h2.people-section-title= t('.account_holders') = render partial: 'claimed_person', collection: @claimed_people - - if @claimed_people.total_entries > 3 - %p - %a.btn.btn-mini.btn-primary#more_account{ href: accounts_path(query: params[:query]) }= t('.more_account_holders') + - if @claimed_people.total_pages > 3 + = link_to t('.more_account_holders'), accounts_path(query: params[:query]), class: 'people-more-btn mb-5', id: 'more_account' - if @unclaimed_people.any? - %h3.margin_bottom_15= t('.unclaimed_committer_ids') - - @unclaimed_people.each do |_, people| - = render partial: 'unclaimed_person', locals: { people: people } - - if @unclaimed_people_count > 3 - %p - %a.btn.btn-mini.btn-primary#more_unclaimed{ href: committers_path(query: params[:query]) }= t('.more_commiters') + .unclaimed-section.mt-5 + %h2.people-section-title= t('.unclaimed_committer_ids') + - @unclaimed_people.each do |_, people| + = render partial: 'unclaimed_person', locals: { people: people } + - if @unclaimed_people_count.to_i > 3 + = link_to t('.more_commiters'), committers_path(query: params[:query]), class: 'people-more-btn', id: 'more_unclaimed' diff --git a/app/views/people/_unclaimed_person.html.haml b/app/views/people/_unclaimed_person.html.haml index c2eed4ba9..298408fc1 100644 --- a/app/views/people/_unclaimed_person.html.haml +++ b/app/views/people/_unclaimed_person.html.haml @@ -6,25 +6,21 @@ unclaimed_people ||= people.to_a.first(UNCLAIMED_TILE_LIMIT) = form_tag(claim_committer_path(name_id), method: :post) do - .well.col-md-12.col-sm-12.col-xs-12.unclaimed_committers_box - .header_row{ style: 'padding-bottom: 0;' } - .pull-left - %h4.no_margin_bottom{ style: 'margin: 0;' } - = from_show_page ? name : link_to(h(truncate(name, length: 70)), committer_path(name_id)) - .pull-right - = submit_tag t('.claim_these_contributions'), class: 'btn btn-mini btn-primary' - .clearfix - - .entire_commits_container - - unclaimed_people.each do |unclaimed_person| - - project = unclaimed_person.project - .inner - .single_container - .group - = render 'committers/unclaimed_tile', project: project, name_fact: unclaimed_person.name_fact - %input.hidden{ type: 'checkbox', name: 'project_ids[]', value: project.id, checked: 'checked' } - - if people.length > UNCLAIMED_TILE_LIMIT && !from_show_page - .inner.more - .single_container.show_more - %span - = link_to t('.and_more', length: people.length - UNCLAIMED_TILE_LIMIT), committer_path(name_id) + .people-card.unclaimed-card + .people-card__body + .unclaimed-card__header + %h4.unclaimed-card__name + - if from_show_page + = name + - else + = link_to(h(truncate(name, length: 70)), committer_path(name_id)) + = submit_tag t('.claim_these_contributions'), class: 'btn btn-primary unclaimed-card__btn' + .unclaimed-card__grid + - unclaimed_people.each do |unclaimed_person| + - project = unclaimed_person.project + .unclaimed-card__item + = render 'committers/unclaimed_tile', project: project, name_fact: unclaimed_person.name_fact + %input.hidden{ type: 'checkbox', name: 'project_ids[]', value: project.id, checked: 'checked' } + - if people.length > UNCLAIMED_TILE_LIMIT && !from_show_page + .unclaimed-card__more + = link_to t('.and_more', length: people.length - UNCLAIMED_TILE_LIMIT), committer_path(name_id) diff --git a/app/views/people/index.html.haml b/app/views/people/index.html.haml index 66d359279..f9e40b71d 100644 --- a/app/views/people/index.html.haml +++ b/app/views/people/index.html.haml @@ -2,9 +2,12 @@ - page_context[:select_top_menu_nav] = :select_people %meta{ name: 'ROBOTS', content: 'NOINDEX, NOFOLLOW' } -%h1.margin_bottom_15= t('.people') +.people-page-container + .people-page-header + %h1.people-page-title= t('.people') + = render 'shared/global_search', placeholder: t('shared.global_search.placeholder') -= render_people_list + = render_people_list -- if params[:query].present? && @claimed_people.empty? && @unclaimed_people.empty? - = render partial: 'shared/alert', locals: { message: t('shared.search_dingus.no_match_found.no_match') } + - if params[:query].present? && @claimed_people.empty? && @unclaimed_people.empty? + = render partial: 'shared/alert', locals: { message: t('shared.search_dingus.no_match_found.no_match') } diff --git a/app/views/shared/_global_search.html.haml b/app/views/shared/_global_search.html.haml new file mode 100644 index 000000000..b1a681dc7 --- /dev/null +++ b/app/views/shared/_global_search.html.haml @@ -0,0 +1,16 @@ +/ Global search bar with Projects / People / Organizations filter dropdown +/ Usage: = render 'shared/global_search', placeholder: 'Search projects...' +- search_placeholder = local_assigns.fetch(:placeholder, t('shared.global_search.placeholder')) + +%form.header-search-form{ action: projects_path, method: :get, role: 'search' } + .btn-group.ux-dropdown.header-search-dropdown + %button.search-filter-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %span.selection{ val: 'p' }= t('shared.global_search.projects') + %i.fa.fa-chevron-down + .dropdown-menu.search-dropdown-menu + %button.dropdown-item.active{ type: 'button', val: 'p' }= t('shared.global_search.projects') + %button.dropdown-item{ type: 'button', val: 'people' }= t('shared.global_search.people') + %button.dropdown-item{ type: 'button', val: 'orgs' }= t('shared.global_search.organizations') + = text_field_tag :query, params[:query], placeholder: search_placeholder, class: 'header-search-input' + %button.header-search-btn{ type: 'submit' } + %i.fa.fa-search diff --git a/app/views/shared/search_dingus/_sort.html.haml b/app/views/shared/search_dingus/_sort.html.haml index 792727a04..24b0a5cdb 100644 --- a/app/views/shared/search_dingus/_sort.html.haml +++ b/app/views/shared/search_dingus/_sort.html.haml @@ -12,7 +12,7 @@ current_sort = params[:sort] || "updated_at" selected_label = sort_options.find { |opt| opt[1] == current_sort }&.first || sort_options.first.first .custom-sort-dropdown - %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %button.sort-dropdown-btn{ type: 'button' } %span.selection{ data: { value: current_sort } }= selected_label %i.fa.fa-chevron-down .dropdown-menu.sort-dropdown-menu @@ -36,7 +36,7 @@ current_sort = params[:sort] || SORT_OPTIONS[sort_context][:default] selected_label = SORT_OPTIONS[sort_context][:options].find { |opt| opt[1] == current_sort }&.first || SORT_OPTIONS[sort_context][:options].first.first .custom-sort-dropdown - %button.sort-dropdown-btn.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } + %button.sort-dropdown-btn{ type: 'button' } %span.selection{ data: { value: current_sort } }= selected_label %i.fa.fa-chevron-down .dropdown-menu.sort-dropdown-menu diff --git a/config/locales/shared.en.yml b/config/locales/shared.en.yml index c46cc5689..9b65e0a14 100644 --- a/config/locales/shared.en.yml +++ b/config/locales/shared.en.yml @@ -41,6 +41,11 @@ en: analyzed: 'Analyzed' based_on: 'based on code collected' project_may_be_current: 'project data may be more current' + global_search: + projects: 'Projects' + people: 'People' + organizations: 'Organizations' + placeholder: 'Search...' api_outage: heading: Under Maintenance description_1: This feature is currently under maintenance. From 52ffd2008d6254aafcd34de76c48902f96571b55 Mon Sep 17 00:00:00 2001 From: kumarin Date: Tue, 7 Apr 2026 20:02:00 +0530 Subject: [PATCH 32/53] OTWO-7607 Fix coverage issue and failed test cases --- app/views/links/index.html.haml | 3 + app/views/permissions/show.html.haml | 2 + app/views/project_licenses/index.html.haml | 1 + app/views/project_licenses/new.html.haml | 2 + test/controllers/commits_controller_test.rb | 44 ++++++ .../project_tags_controller_test.rb | 2 +- test/helpers/edits_helper_test.rb | 2 +- test/helpers/explore_helper_test.rb | 31 +++- test/helpers/factoids_helper_test.rb | 142 ++++++++++++++++++ test/helpers/projects_helper_test.rb | 140 +++++++++++++++++ test/helpers/tags_helper_test.rb | 70 +++++++++ test/models/clump_test.rb | 20 +++ test/models/fis_job_test.rb | 83 ++++++++++ test/test_helper.rb | 2 +- 14 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 test/helpers/factoids_helper_test.rb create mode 100644 test/helpers/tags_helper_test.rb create mode 100644 test/models/clump_test.rb create mode 100644 test/models/fis_job_test.rb diff --git a/app/views/links/index.html.haml b/app/views/links/index.html.haml index 71092d2f0..7247421e1 100644 --- a/app/views/links/index.html.haml +++ b/app/views/links/index.html.haml @@ -1,9 +1,12 @@ + :ruby has_permission = logged_in? && @project.edit_authorized? content_for(:html_title) { t('.page_title', name: @project.name) } page_context[:select_top_menu_nav] = 'select_projects' page_context[:select_footer_nav] = nil += render partial: 'layouts/partials/alert' + .project_content_title %h2.pull-left = link_to t(:settings), settings_project_path(@project) diff --git a/app/views/permissions/show.html.haml b/app/views/permissions/show.html.haml index f9239221f..fba78773b 100644 --- a/app/views/permissions/show.html.haml +++ b/app/views/permissions/show.html.haml @@ -2,6 +2,8 @@ - page_context[:select_footer_nav] = nil = javascript_include_tag 'permissions.js' += render partial: 'layouts/partials/alert' + .project_content_title %h2.float_left = link_to t(:settings), [:settings, @parent] diff --git a/app/views/project_licenses/index.html.haml b/app/views/project_licenses/index.html.haml index 84a480942..0449a60cf 100644 --- a/app/views/project_licenses/index.html.haml +++ b/app/views/project_licenses/index.html.haml @@ -40,3 +40,4 @@ = yield page_context[:header_title] if page_context[:header_title] = render partial: 'project_licenses/about' += render partial: 'layouts/partials/alert' diff --git a/app/views/project_licenses/new.html.haml b/app/views/project_licenses/new.html.haml index a279d5e62..2579b67c5 100644 --- a/app/views/project_licenses/new.html.haml +++ b/app/views/project_licenses/new.html.haml @@ -21,6 +21,8 @@  : #{t(:new)} = project_analysis_timestamp(@project) += render partial: 'layouts/partials/alert' + #error{ style: 'display: none;' } .alert.alert-info %i.icon-exclamation-sign diff --git a/test/controllers/commits_controller_test.rb b/test/controllers/commits_controller_test.rb index 63585db36..d66df6e79 100644 --- a/test/controllers/commits_controller_test.rb +++ b/test/controllers/commits_controller_test.rb @@ -157,6 +157,11 @@ class CommitsControllerTest < ActionController::TestCase _(assigns(:commits).first).must_equal @commit1 end + it 'should populate commit_contributors' do + get :summary, params: { project_id: @project.id } + _(assigns(:commit_contributors)).wont_be_nil + end + it 'should show no code_location when enlistment is empty' do Project.any_instance.stubs(:best_analysis).returns(NilAnalysis.new) get :summary, params: { project_id: @project.id } @@ -186,6 +191,19 @@ class CommitsControllerTest < ActionController::TestCase _(assigns(:daily_commits).first.time.to_a).must_equal @commit1.time.to_a _(assigns(:daily_commits).first.comment).must_equal @commit1.comment end + + it 'should render no analysis error when analysis is empty' do + Project.any_instance.stubs(:best_analysis).returns(NilAnalysis.new) + get :events, params: { project_id: @project.id, id: @commit1.id, contributor_id: @name1.id }, format: :xml + assert_response :not_found + end + + it 'should render 404 when contributor_fact is not found' do + create(:name_fact, analysis: @project.best_analysis, name: @name1) + Airbrake.stubs(:notify) + get :events, params: { project_id: @project.id, id: @commit1.id, contributor_id: 999_999 }, format: :xml + assert_response :not_found + end end describe 'event_details' do @@ -196,6 +214,32 @@ class CommitsControllerTest < ActionController::TestCase _(assigns(:commits).count).must_equal 1 _(assigns(:commits).first).must_equal @commit1 end + + it 'should handle plain time param without commit_ prefix' do + create(:name_fact, analysis: @project.best_analysis, name: @name1) + get :event_details, params: { contributor_id: @name1.id, id: @commit1.id, + project_id: @project.id, time: @commit1.time.to_i.to_s } + assert_response :ok + end + end + + describe 'redirect_to_message_if_oversized_project' do + it 'should redirect to root for oversized projects' do + CommitsController.any_instance.stubs(:oversized_project?).returns(true) + get :index, params: { project_id: @project.id } + assert_redirected_to root_path + _(flash[:notice]).must_equal I18n.t('commits.project_temporarily_disabled') + end + end + + describe 'individual_named_commits' do + it 'should return empty commits when commit_contributor is nil' do + Project.any_instance.stubs(:commit_contributors).returns(stub(find_by: nil)) + contribution = @project.contributions.first + get :index, params: { project_id: @project.id, contributor_id: contribution.id } + assert_response :ok + _(assigns(:commits)).must_equal [] + end end private diff --git a/test/controllers/project_tags_controller_test.rb b/test/controllers/project_tags_controller_test.rb index 50e02cffe..2bb1d2ca1 100644 --- a/test/controllers/project_tags_controller_test.rb +++ b/test/controllers/project_tags_controller_test.rb @@ -23,7 +23,7 @@ class ProjectTagsControllerTest < ActionController::TestCase project = create(:project) get :index, params: { project_id: project.to_param } assert_response :success - assert_select '.alert', 2 + assert_select '.alert', 1 _(response.body).must_match I18n.t('project_tags.index.no_other_projects') end end diff --git a/test/helpers/edits_helper_test.rb b/test/helpers/edits_helper_test.rb index cabc389c6..2a295bf4b 100644 --- a/test/helpers/edits_helper_test.rb +++ b/test/helpers/edits_helper_test.rb @@ -16,7 +16,7 @@ class EditsHelperTest < ActionView::TestCase it 'drop the year if it was this year' do other_time = 17.days.ago dont_fail_around_new_years_date = Time.new(Time.current.year, other_time.month, other_time.day).in_time_zone - _(edit_humanize_datetime(dont_fail_around_new_years_date)).wont_match Time.current.year + _(edit_humanize_datetime(dont_fail_around_new_years_date)).wont_match Time.current.year.to_s end it 'includes the year if it was before this year' do diff --git a/test/helpers/explore_helper_test.rb b/test/helpers/explore_helper_test.rb index 9f5310cdb..95276a90c 100644 --- a/test/helpers/explore_helper_test.rb +++ b/test/helpers/explore_helper_test.rb @@ -2,7 +2,7 @@ require 'test_helper' -class ExploreHelperTest < ActiveSupport::TestCase +class ExploreHelperTest < ActionView::TestCase include ExploreHelper describe 'scale_to' do @@ -14,4 +14,33 @@ class ExploreHelperTest < ActiveSupport::TestCase _(scale_to(94, 1000)).must_equal 1000 end end + + describe 'compare_project_inputs' do + it 'should return an array of input hashes for compare projects' do + stubs(:t).with('.enter_project').returns('Enter project') + inputs = compare_project_inputs + _(inputs.length).must_equal 3 + _(inputs.first[:name]).must_equal 'project_0' + _(inputs.first[:type]).must_equal 'text' + _(inputs.first[:id]).must_equal 'project_0' + _(inputs.last[:name]).must_equal 'project_2' + end + end + + describe 'cache_projects_explore_page' do + it 'should call render with projects when language is present' do + @language = create(:language) + expects(:render).with('projects').returns('projects') + result = cache_projects_explore_page + _(result).must_equal 'projects' + end + + it 'should use cache when language is blank' do + @language = nil + Rails.cache.clear + expects(:render).with('projects').returns('cached') + result = cache_projects_explore_page + _(result).must_equal 'cached' + end + end end diff --git a/test/helpers/factoids_helper_test.rb b/test/helpers/factoids_helper_test.rb new file mode 100644 index 000000000..36437ade9 --- /dev/null +++ b/test/helpers/factoids_helper_test.rb @@ -0,0 +1,142 @@ +# frozen_string_literal: true + +require 'test_helper' + +class FactoidsHelperTest < ActionView::TestCase + include FactoidsHelper + + before do + @project = create(:project) + @analysis = @project.best_analysis + end + + describe 'get_factoid_type' do + it 'should return nil when no matching factoid exists' do + @analysis.stubs(:factoids).returns([]) + result = send(:get_factoid_type, :comments) + _(result).must_be_nil + end + + it 'should return matching factoid for comments' do + factoid = Factoid.find(create(:factoid, analysis: @analysis, type: 'FactoidCommentsVeryHigh').id) + @analysis.stubs(:factoids).returns([factoid]) + result = send(:get_factoid_type, :comments) + _(result).must_equal factoid + end + + it 'should return matching factoid for activity' do + factoid = Factoid.find(create(:factoid, analysis: @analysis, type: 'FactoidActivityIncreasing').id) + @analysis.stubs(:factoids).returns([factoid]) + result = send(:get_factoid_type, :activity) + _(result).must_equal factoid + end + + it 'should return matching factoid for team' do + factoid = Factoid.find(create(:factoid, analysis: @analysis, type: 'FactoidTeamSizeOne').id) + @analysis.stubs(:factoids).returns([factoid]) + result = send(:get_factoid_type, :team) + _(result).must_equal factoid + end + + it 'should return matching factoid for age' do + factoid = Factoid.find(create(:factoid, analysis: @analysis, type: 'FactoidAgeEstablished').id) + @analysis.stubs(:factoids).returns([factoid]) + result = send(:get_factoid_type, :age) + _(result).must_equal factoid + end + end + + describe 'factiod_info' do + it 'should return factoid inline, category, and path when factoid exists' do + factoid = create(:factoid, analysis: @analysis, type: 'FactoidCommentsVeryHigh') + factoid = Factoid.find(factoid.id) + @analysis.stubs(:factoids).returns([factoid]) + text, type, url = send(:factiod_info, :comments) + _(text).must_equal factoid.inline + _(type).must_equal factoid.category + _(url).must_equal project_factoids_path(@project, anchor: factoid.type) + end + + it 'should return unknown info for comments when no factoid exists' do + @analysis.stubs(:factoids).returns([]) + text, type, url = send(:factiod_info, :comments) + _(text).must_equal I18n.t('factoids.comments_unknown_inline') + _(type).must_equal :warning + _(url).must_be_nil + end + + it 'should return unknown info for activity when no factoid exists' do + @analysis.stubs(:factoids).returns([]) + text, type, url = send(:factiod_info, :activity) + _(text).must_equal I18n.t('factoids.activity_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + + it 'should return unknown info for team when no factoid exists' do + @analysis.stubs(:factoids).returns([]) + text, type, url = send(:factiod_info, :team) + _(text).must_equal I18n.t('factoids.team_size_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + + it 'should return unknown info for age when no factoid exists' do + @analysis.stubs(:factoids).returns([]) + text, type, url = send(:factiod_info, :age) + _(text).must_equal I18n.t('factoids.age_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + end + + describe 'factoid_no_factoid_info' do + it 'should return comments unknown info' do + text, type, url = send(:factoid_no_factoid_info, :comments) + _(text).must_equal I18n.t('factoids.comments_unknown_inline') + _(type).must_equal :warning + _(url).must_be_nil + end + + it 'should return activity unknown info' do + text, type, url = send(:factoid_no_factoid_info, :activity) + _(text).must_equal I18n.t('factoids.activity_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + + it 'should return team unknown info' do + text, type, url = send(:factoid_no_factoid_info, :team) + _(text).must_equal I18n.t('factoids.team_size_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + + it 'should return age unknown info' do + text, type, url = send(:factoid_no_factoid_info, :age) + _(text).must_equal I18n.t('factoids.age_unknown_inline') + _(type).must_equal :info + _(url).must_be_nil + end + end + + describe 'get_factoid_display' do + it 'should call haml_tag with correct class for existing factoid' do + factoid = create(:factoid, analysis: @analysis, type: 'FactoidCommentsVeryHigh') + factoid = Factoid.find(factoid.id) + @analysis.stubs(:factoids).returns([factoid]) + + stubs(:haml_tag) + stubs(:concat) + get_factoid_display(:comments) + end + + it 'should call haml_tag with correct class for unknown factoid' do + @analysis.stubs(:factoids).returns([]) + + stubs(:haml_tag) + stubs(:concat) + get_factoid_display(:comments) + end + end +end diff --git a/test/helpers/projects_helper_test.rb b/test/helpers/projects_helper_test.rb index 13e9c613a..fc6aeca70 100644 --- a/test/helpers/projects_helper_test.rb +++ b/test/helpers/projects_helper_test.rb @@ -155,6 +155,17 @@ class ProjectsHelperTest < ActionView::TestCase @project = create(:project_with_less_summary) _(project_description_size_breached?(@project)).must_equal false end + + it 'should return true if project description size is greater than 800' do + @project = create(:project_with_invalid_description) + _(project_description_size_breached?(@project)).must_equal true + end + + it 'should return nil if project description is nil' do + @project = create(:project) + @project.stubs(:description).returns(nil) + _(project_description_size_breached?(@project)).must_be_nil + end end describe 'project_separator_text method' do @@ -164,4 +175,133 @@ class ProjectsHelperTest < ActionView::TestCase assert_equal ' | ', result end end + + describe 'project_activity_css_class' do + it 'should return correct css class' do + @project = create(:project) + @project.best_analysis.stubs(:activity_level).returns(:high) + _(send(:project_activity_css_class, @project, 'large')).must_equal 'large_project_activity_level_high' + end + end + + describe 'project_activity_level_text_class' do + it 'should return correct text class' do + _(send(:project_activity_level_text_class, 'small')).must_equal 'small_project_activity_text' + end + end + + describe 'project_activity_level' do + it 'should return the activity level from best analysis' do + @project = create(:project) + @project.best_analysis.stubs(:activity_level).returns(:moderate) + _(send(:project_activity_level, @project)).must_equal :moderate + end + end + + describe 'project_activity_level_class' do + it 'should call haml_tag with correct css class' do + @project = create(:project) + @project.best_analysis.stubs(:activity_level).returns(:high) + stubs(:haml_tag) + project_activity_level_class(@project, 'large') + end + end + + describe 'project_activity_level_text' do + it 'should call haml_tag with activity text' do + @project = create(:project) + @project.best_analysis.stubs(:activity_level).returns(:high) + stubs(:haml_tag) + project_activity_level_text(@project, 'small') + end + end + + describe 'project_iusethis_button' do + it 'should call haml_tag with correct attributes' do + @project = create(:project) + stubs(:haml_tag) + stubs(:concat) + stubs(:needs_login_or_verification_or_default).returns('new-stack-entry') + project_iusethis_button(@project) + end + end + + describe 'project_twitter_description' do + before do + @project = create(:project) + end + + it 'should return description when analysis is nil' do + @project.stubs(:description).returns('A test project') + _(project_twitter_description(@project, nil)).must_equal 'A test project' + end + + it 'should return empty string when analysis is nil and description is empty' do + @project.stubs(:description).returns('') + _(project_twitter_description(@project, nil)).must_equal '' + end + + it 'should return formatted string when analysis is present' do + analysis = @project.best_analysis + analysis.stubs(:code_total).returns(10_000) + analysis.stubs(:committers_all_time).returns(50) + @project.best_analysis.stubs(:activity_level).returns(:high) + @project.stubs(:user_count).returns(100) + result = project_twitter_description(@project, analysis) + _(result).must_match 'lines of code' + _(result).must_match 'contributors' + _(result).must_match '100 users' + end + end + + describe 'show_badges' do + it 'should return div with badge images' do + @project = create(:project) + badge = stub(badge_url: 'https://example.com/badge.png') + @project.stubs(:badges_summary).returns([badge]) + result = show_badges + _(result).must_match 'badges' + _(result).must_match 'https://example.com/badge.png' + end + + it 'should return empty div when no badges' do + @project = create(:project) + @project.stubs(:badges_summary).returns([]) + result = show_badges + _(result).must_match 'badges' + end + end + + describe 'more_badges_link' do + it 'should return nil when badges count is within limit' do + @project = create(:project) + @project.stubs(:project_badges).returns(stub(active: stub(count: 1))) + _(more_badges_link).must_be_nil + end + + it 'should return link when badges exceed limit' do + @project = create(:project) + @project.stubs(:project_badges).returns(stub(active: stub(count: ProjectBadge::SUMMARY_LIMIT + 1))) + result = more_badges_link + _(result).must_match 'more' + end + end + + describe 'project_compare_button' do + it 'should call haml_tag for project compare form' do + @project = create(:project) + @session_projects = [] + stubs(:haml_tag) + stubs(:concat) + project_compare_button(@project) + end + + it 'should handle selected session project' do + @project = create(:project) + @session_projects = [@project] + stubs(:haml_tag) + stubs(:concat) + project_compare_button(@project) + end + end end diff --git a/test/helpers/tags_helper_test.rb b/test/helpers/tags_helper_test.rb new file mode 100644 index 000000000..ca1add627 --- /dev/null +++ b/test/helpers/tags_helper_test.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +require 'test_helper' + +class TagsHelperTest < ActionView::TestCase + include TagsHelper + include ERB::Util + + describe 'tag_icon_link' do + it 'should return link when project is edit authorized' do + project = create(:project) + project.stubs(:edit_authorized?).returns(true) + stubs(:t).with('.tags').returns('Tags') + result = tag_icon_link(project) + _(result).must_match project_tags_path(project) + end + + it 'should return text without link when project is not edit authorized' do + project = create(:project) + project.stubs(:edit_authorized?).returns(false) + stubs(:t).with('.tags').returns('Tags') + result = tag_icon_link(project) + _(result).wont_match ' Date: Wed, 8 Apr 2026 12:22:01 +0530 Subject: [PATCH 33/53] OTWO-7617 Implementation of Project enlistments page (#1882) * OTWO-7617 Implementation of Project enlistments page * OTWO-7617 Implementation of Project search index page * OTWO-7617 Implementation of Project enlistments page * OTWO-7617 fixed toggle issue for cards --- app/assets/javascripts/contributors_cards.js | 35 ++- app/assets/stylesheets/bootstrap.sass | 3 +- app/assets/stylesheets/dark_theme.sass | 13 +- app/assets/stylesheets/enlistments.sass | 242 +++++++++++++++++- app/assets/stylesheets/oh-styles.sass | 4 +- app/assets/stylesheets/search-dingus.sass | 2 + app/views/contributions/index.html.haml | 2 +- .../_about_code_locations.html.haml | 53 ++-- app/views/enlistments/index.html.haml | 153 +++++++---- app/views/projects/index.html.haml | 26 +- app/views/shared/_modern_pagination.html.haml | 24 ++ config/locales/enlistments.en.yml | 11 + 12 files changed, 444 insertions(+), 124 deletions(-) create mode 100644 app/views/shared/_modern_pagination.html.haml diff --git a/app/assets/javascripts/contributors_cards.js b/app/assets/javascripts/contributors_cards.js index c0fcf560e..18c57331c 100644 --- a/app/assets/javascripts/contributors_cards.js +++ b/app/assets/javascripts/contributors_cards.js @@ -1,20 +1,19 @@ -// Contributors mobile card expand/collapse functionality -document.addEventListener('DOMContentLoaded', function() { - var contributorCards = document.querySelectorAll('.contributor-card-item'); - - contributorCards.forEach(function(card) { - var header = card.querySelector('.card-item-header'); - - if (header) { - header.addEventListener('click', function(e) { - // Don't toggle if clicking on the avatar/name link - if (e.target.closest('.contributor-link')) { - return; - } - - e.preventDefault(); - card.classList.toggle('expanded'); - }); +document.addEventListener('click', function(e) { + // Handle contributor and enlistment card headers + var cardHeader = e.target.closest('.contributor-card-item .card-item-header, .enlistment-card-item .card-item-header'); + if (cardHeader) { + // Skip if clicking a link + if (e.target.closest('a, .contributor-link')) { + return; } - }); + e.preventDefault(); + cardHeader.closest('.contributor-card-item, .enlistment-card-item').classList.toggle('expanded'); + return; + } + + // Handle about code locations card header + var aboutHeader = e.target.closest('.about-code-locations-card .card-header'); + if (aboutHeader) { + aboutHeader.closest('.about-code-locations-card').classList.toggle('expanded'); + } }); diff --git a/app/assets/stylesheets/bootstrap.sass b/app/assets/stylesheets/bootstrap.sass index 9804cfce4..bd2d8870c 100644 --- a/app/assets/stylesheets/bootstrap.sass +++ b/app/assets/stylesheets/bootstrap.sass @@ -8,7 +8,8 @@ .alert h4 border: 0px !important .alert - padding: 8px 35px 8px 14px + border-radius: 16px + padding: 8px 14px 8px 14px margin-bottom: 20px font-size: 15px .alert h4 diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index d5203447f..e7fc0f198 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -41,9 +41,9 @@ html.dark // Buttons (keep primary yellow button, update others) .btn - &:not(.btn-primary):not(.btn-success):not(.people-more-btn) - background-color: #334155 !important - color: #e2e8f0 !important + &:not(.btn-primary):not(.btn-success):not(.btn-danger) + background-color: #ffb91a !important + color: #1D0631 !important &:hover background-color: #ffd966 !important @@ -69,13 +69,11 @@ html.dark color: #e2e8f0 !important // Headings - h1, h2, h3, h4, h5, h6 - color: #f1f5f9 !important + // Alerts .alert background-color: #1D0631 !important - color: #e2e8f0 !important border-color: #4b5563 !important // Dropdown menus @@ -274,6 +272,9 @@ html.dark .add-project-text color: #9ca3af !important + // Soft text (about/description text) + .soft, h1.soft, h2.soft, h3.soft, h4.soft, h5.soft, h6.soft, p.soft, li.soft, dd.soft, dt.soft, span.soft, a.soft, abbr.soft, td.soft, th.soft + color: #9ca3af !important // ── People & Accounts page ──────────────────────────────────────────────── .accounts-back-link color: #9ca3af diff --git a/app/assets/stylesheets/enlistments.sass b/app/assets/stylesheets/enlistments.sass index af434d400..8d590995a 100644 --- a/app/assets/stylesheets/enlistments.sass +++ b/app/assets/stylesheets/enlistments.sass @@ -1,18 +1,160 @@ -tbody tr.enlistment - &:nth-child(odd) - @include site-well-color - .enlistment .last + display: flex + flex-direction: column + align-items: flex-start + a - margin-bottom: 4px + margin-bottom: 7px !important width: 148px + + @media (max-width: 768px) + flex-direction: row + gap: 8px + + a + margin-bottom: 0 !important + flex: 1 #dingus-row #search-dingus margin: 0px !important .alert display: none +.search-filter-bar + .alert + display: none + + label.checkbox + margin-left: -12px !important + + input[type="checkbox"] + position: relative + +// Mobile enlistments cards styling (based on contributions.sass pattern) +.enlistments-cards-container + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + padding: 0 + margin-bottom: 24px + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .enlistment-card-item + border-bottom: 1px solid #f3f4f6 + transition: background-color 0.2s + + html.dark & + border-bottom-color: rgba(90, 42, 130, 0.2) + + &:last-child + border-bottom: none + + .card-item-header + display: flex + align-items: center + justify-content: space-between + padding: 16px 24px + cursor: pointer + + &:hover + background-color: #f9fafb + + html.dark & + background-color: rgba(255, 255, 255, 0.05) + + .repository-url + font-size: 14px + font-weight: 600 + color: #111827 + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + flex: 1 + min-width: 0 + + html.dark & + color: #ffffff + + a + color: #5A2A82 + text-decoration: none + + html.dark & + color: #ffb91a + + .expand-toggle + background: none + border: none + padding: 8px + cursor: pointer + color: #6b7280 + transition: transform 0.2s + margin-left: 12px + + html.dark & + color: #9ca3af + + i + font-size: 16px + + .card-item-content + display: none + padding: 0 24px 16px + flex-direction: column + gap: 12px + + .content-row + display: grid + grid-template-columns: 1fr 1fr + gap: 12px + + &.full-width + grid-template-columns: 1fr + + .content-item + display: flex + flex-direction: column + gap: 4px + text-align: center + + .label + font-size: 11px + font-weight: 600 + color: #6b7280 + text-transform: uppercase + letter-spacing: 0.05em + + html.dark & + color: #9ca3af + + .value + font-size: 14px + color: #111827 + font-weight: 500 + + html.dark & + color: #e2e8f0 + + a + color: #5A2A82 + text-decoration: none + + html.dark & + color: #ffb91a + + &.expanded + .card-item-header + .expand-toggle + transform: rotate(180deg) + + .card-item-content + display: flex + .github span border-radius: 0px @@ -20,3 +162,93 @@ tbody tr.enlistment width: 70% .field_with_errors display: block + +// About Code Locations collapsible card +.about-code-locations-card + background: #ffffff + border-radius: 16px + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + overflow: hidden + margin-top: 24px + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + .card-header + display: flex + align-items: center + justify-content: space-between + padding: 16px 24px + cursor: pointer + transition: background-color 0.2s + + &:hover + background-color: #f9fafb + + html.dark & + background-color: rgba(255, 255, 255, 0.05) + + .card-title + margin: 0 + font-size: 16px + font-weight: 600 + color: #5A2A82 + flex: 1 + + html.dark & + color: #ffb91a + + .soft + margin-right: 4px + + .expand-toggle + background: none + border: none + padding: 8px + cursor: pointer + color: #6b7280 + transition: transform 0.2s + margin-left: 12px + + html.dark & + color: #9ca3af + + i + font-size: 16px + + .card-content + display: none + padding: 0 24px 16px + + &.expanded + .card-header + .expand-toggle + transform: rotate(180deg) + + .card-content + display: block + + .about-content-wrapper + display: flex + gap: 40px + + @media (max-width: 768px) + flex-direction: column + gap: 24px + + ul + list-style: disc + padding-left: 20px + + li + margin-bottom: 8px + line-height: 1.6 + + strong + font-weight: 600 + +.new-code-location-container + display: flex + justify-content: center + margin-top: 24px diff --git a/app/assets/stylesheets/oh-styles.sass b/app/assets/stylesheets/oh-styles.sass index 8455fce86..0ee861f0e 100644 --- a/app/assets/stylesheets/oh-styles.sass +++ b/app/assets/stylesheets/oh-styles.sass @@ -393,8 +393,10 @@ margin-top: 32px .page-btn - width: 36px + min-width: 36px + width: auto height: 36px + padding: 0 12px border-radius: 8px border: 1px solid #e5e7eb background-color: #ffffff !important diff --git a/app/assets/stylesheets/search-dingus.sass b/app/assets/stylesheets/search-dingus.sass index 777df6626..eee9edf40 100644 --- a/app/assets/stylesheets/search-dingus.sass +++ b/app/assets/stylesheets/search-dingus.sass @@ -55,6 +55,7 @@ .search-dropdown-menu width: 160px margin-top: 4px + padding: 0 background-color: #ffffff border: 1px solid #d1d5db border-radius: 8px @@ -395,6 +396,7 @@ display: none position: absolute top: 100% + padding: 0 left: 0 margin-top: 4px min-width: 180px diff --git a/app/views/contributions/index.html.haml b/app/views/contributions/index.html.haml index 34981c36a..b09e1288f 100644 --- a/app/views/contributions/index.html.haml +++ b/app/views/contributions/index.html.haml @@ -13,7 +13,7 @@ - if @project.best_analysis.present? = render 'shared/search_dingus/commits_or_contributor_search', items: @contributions, type: :contributions, total_count: @contributions.total_entries = render partial: 'contributions', locals: { contributions: @contributions } - = will_paginate @contributions + = render 'shared/modern_pagination', collection: @contributions - else = render partial: 'projects/show/no_analysis_summary', locals: { analysis: @project.best_analysis } diff --git a/app/views/enlistments/_about_code_locations.html.haml b/app/views/enlistments/_about_code_locations.html.haml index a8135276c..c819dff4d 100644 --- a/app/views/enlistments/_about_code_locations.html.haml +++ b/app/views/enlistments/_about_code_locations.html.haml @@ -1,23 +1,30 @@ -%h4.margin_top_10.margin_bottom_0 - %span.soft= t('.about') - = t('.code_locations') -.col-md-5.margin_left_30 - %ul - %li= t('.description1') - %li= t('.description2') - %li= t('.description3') - %li= t('.description4') - %li - = t('.description5') - %strong= t('.description6') - = t('.description7') -.col-md-5.margin_left_20 - %ul - %li - = t('.description8') - %ul.margin_left_30 - %li - = t('.description9') - %li - = t('.description10') - %li= t('.description11') +.about-code-locations-card + .card-header + %h4.card-title + %span.soft= t('.about') + = t('.code_locations') + %button.expand-toggle{ type: 'button', 'aria-label' => 'Expand about section' } + %i.fa.fa-chevron-down + + .card-content + .about-content-wrapper + .col-md-5.margin_left_30 + %ul + %li= t('.description1') + %li= t('.description2') + %li= t('.description3') + %li= t('.description4') + %li + = t('.description5') + %strong= t('.description6') + = t('.description7') + .col-md-5.margin_left_20 + %ul + %li + = t('.description8') + %ul.margin_left_30 + %li + = t('.description9') + %li + = t('.description10') + %li= t('.description11') diff --git a/app/views/enlistments/index.html.haml b/app/views/enlistments/index.html.haml index 3af2e23ee..b43f97b13 100644 --- a/app/views/enlistments/index.html.haml +++ b/app/views/enlistments/index.html.haml @@ -10,7 +10,6 @@ - if current_user_is_admin? = link_to code_location_ids_admin_url(@enlistments.pluck(:code_location_id)), target: '_blank', title: 'Code locations admin for current page', rel: 'tooltip' do %span.glyphicon.glyphicon-link - = project_analysis_timestamp(@project) .clearfix - if sidekiq_work_in_progress? @@ -42,59 +41,123 @@ %li.font-1em= t(key) if I18n.exists?(key) - if @project.enlistments.exists? && api_access_available - %table.table.table-striped - %thead - %tr{ id: 'dingus-row' } - %td{ colspan: '7' } - .margin_top_10.margin_bottom_10 - %form.form-inline - .form-group.margin_right_10.padding_left_10 - - if @enlistments.respond_to?(:current_page) && !@enlistments.length.zero? - %label.paginate= "Showing page #{number_with_delimiter(@enlistments.current_page)} of #{number_with_delimiter(@enlistments.total_pages)}" - - elsif @enlistments.size.zero? - %label.paginate= t('.count_status', current_count: @enlistments.length, - total_count: number_with_delimiter(0)) - .form-group.margin_right_10 - %label Search   - = text_field_tag :query, params[:query] - - %button.btn.btn-refresh{ type: 'Submit', style: 'height: 34px; border-top: 10px' } - %i.icon-refresh - .form-group.margin_right_10 - %label - = check_box_tag(:dnf, '1', (params[:dnf] == '1' ? true : false), style: 'margin-top: 1px', onChange: 'submit()') - = label_tag(:dnf, 'Show unprocessable sources') - - .form-group.pull-right.padding_right_10 - #sort_by - %label= 'Sort by' - = select_tag :sort, options_for_select(SORT_OPTIONS['enlistments'][:options], - params[:sort] || SORT_OPTIONS['enlistments'][:default]), - class: 'chzn-select', onchange: 'this.form.submit()', style: 'width: 150px;' - - %tr - %th= t('.repository_url') - %th= t('.scm_type') - %th= t('.update_status') - %th= t('.ignored_files') - %th= t('.allowed_files') - %th= t('.actions') - %tbody - = render partial: 'enlistment', collection: @enlistments, locals: { has_permission: has_permission } + .search-filter-bar + %form + .search-filter-content + = render 'shared/search_dingus/page_entries_info', collection: @enlistments, total_count: @enlistments.total_entries + + = render 'shared/search_dingus/search_bar', search_type: :default + + %label.checkbox + = check_box_tag(:dnf, '1', (params[:dnf] == '1' ? true : false), onChange: 'this.form.submit()') + = t('.show_unprocessable_sources') + + = render 'shared/search_dingus/sort', sort_context: :enlistments, filter_type: nil + + // Desktop table view + .desktop-only + %table.table + %thead + %tr + %th= t('.repository_url') + %th= t('.scm_type') + %th= t('.update_status') + %th= t('.ignored_files') + %th= t('.allowed_files') + %th= t('.actions') + %tbody + = render partial: 'enlistment', collection: @enlistments, locals: { has_permission: has_permission } + + // Mobile card view + .enlistments-cards-container.mobile-only + - @enlistments.each do |enlistment| + - url = enlistment.code_location.url + - job_id = enlistment.fis_code_location.last_job_id + - fis_job = FisJob.find_by(id: job_id) + - description = job_id ? FailureGroupApi.failure_group_description(job_id) : '' + - ass = enlistment.analysis_sloc_set + .enlistment-card-item + .card-item-header + .repository-url + = url =~ /^http/ ? link_to(url, url, target: '_blank') : url + - if current_user_is_admin? + = link_to code_location_admin_url(enlistment.code_location_id), target: '_blank' do + %span.glyphicon.glyphicon-link + - if enlistment.code_location.do_not_fetch + - if description.present? + %p.text-danger Error: #{description} + - else + %p.text-danger= t('.dnf') + - elsif !enlistment.code_location.do_not_fetch && fis_job && fis_job.retry_count > 1 && fis_job.wait_until != nil + - if description.present? + %p.text-danger Error: #{description} + %p= enlistment.code_location.branch + %button.expand-toggle{ type: 'button', 'aria-label' => 'Expand details' } + %i.fa.fa-chevron-down + + .card-item-content + .content-row + .content-item + .label= t('.scm_type') + .value + - scm_type = enlistment.code_location.scm_name_in_english + - if scm_type.present? + = scm_type + + .content-item + .label= t('.update_status') + .value + - status = CodeLocationJobProgress.new(enlistment).message + - if status.present? + = status + + .content-row + .content-item + .label= t('.ignored_files') + .value + - if ass + - if ass.ignored_fyle_count.to_i.zero? + = t('.description3') + - else + = t('.description1') + = pluralize(ass.ignored_fyle_count, t('.file')) + = t('.description2') + + .content-item + .label= t('.allowed_files') + .value + - if ass + - if ass.allowed_fyle_count.to_i.zero? + = t('.description3') + - else + = t('.description4') + = pluralize(ass.allowed_fyle_count, t('.file')) + = t('.description2') + + .content-row.full-width + - if has_permission + = icon_button(edit_project_enlistment_path(@project, enlistment), icon: 'pencil', text: t('.edit_ignored'), size: 'mini', type: 'primary') + - if current_user_is_admin? + = icon_button(edit_allowed_files_project_enlistment_path(@project, enlistment), icon: 'pencil', text: t('.edit_allowed'), size: 'mini', type: 'primary') + = icon_button(project_enlistment_path(@project, enlistment), icon: 'trash', text: t('.remove'), size: 'mini', type: 'danger', method: :delete, data: { confirm: t('.confirm') }) + - else + = disabled_button bootstrap_icon('icon-pencil', t('.edit_ignored')), class: 'btn-mini btn-primary' + = disabled_button bootstrap_icon('icon-trash', t('.remove')), class: 'btn-mini btn-danger pull-right' - elsif api_access_available = render '/enlistments/no_enlistment' - else = render partial: 'shared/api_outage' -= will_paginate @enlistments += render 'shared/modern_pagination', collection: @enlistments - if @enlistments.empty? && params[:query].present? = render partial: 'shared/alert', locals: { message: t('.no_match') } - if @project.enlistments.exists? && api_access_available - - if logged_in? && @project.edit_authorized? - = link_to t('.new_code_location'), new_project_enlistment_path(@project), class: 'btn btn-primary' - - else - = icon_button(new_project_enlistment_url(@project), text: t('.new_code_location'), class: 'btn btn-primary') + .new-code-location-container + - if logged_in? && @project.edit_authorized? + = link_to t('.new_code_location'), new_project_enlistment_path(@project), class: 'btn btn-primary' + - else + = icon_button(new_project_enlistment_url(@project), text: t('.new_code_location'), class: 'btn btn-primary') = render partial: 'about_code_locations' diff --git a/app/views/projects/index.html.haml b/app/views/projects/index.html.haml index 5447eaf32..8708f2fa5 100644 --- a/app/views/projects/index.html.haml +++ b/app/views/projects/index.html.haml @@ -29,27 +29,5 @@ = render partial: 'project_index', collection: @projects, locals: { compare: true, show_active_committers: true } / Modern Pagination - - if @projects.total_pages > 1 - .modern-pagination - - current_page = @projects.current_page - - total_pages = @projects.total_pages - %button.page-btn{disabled: current_page == 1, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page - 1)).to_query}'"} - %i.fa.fa-chevron-left - - visible_pages = [] - - window_size = 2 - - visible_pages << 1 - - if total_pages > 1 - - left = [current_page - window_size, 2].max - - right = [current_page + window_size, total_pages - 1].min - - visible_pages << :gap if left > 2 - - visible_pages.concat((left..right).to_a) if left <= right - - visible_pages << :gap if right < total_pages - 1 - - visible_pages << total_pages - - visible_pages.each do |page| - - if page == :gap - %span.page-ellipsis … - - else - %button.page-btn{class: ('active' if page == current_page), onclick: "window.location.href='?#{request.query_parameters.merge(page: page).to_query}'"} - = page - %button.page-btn{disabled: current_page == total_pages, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page + 1)).to_query}'"} - %i.fa.fa-chevron-right + = render 'shared/modern_pagination', collection: @projects + \ No newline at end of file diff --git a/app/views/shared/_modern_pagination.html.haml b/app/views/shared/_modern_pagination.html.haml new file mode 100644 index 000000000..461c2eeb2 --- /dev/null +++ b/app/views/shared/_modern_pagination.html.haml @@ -0,0 +1,24 @@ +- if collection.total_pages > 1 + .modern-pagination + - current_page = collection.current_page + - total_pages = collection.total_pages + %button.page-btn{disabled: current_page == 1, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page - 1)).to_query}'"} + %i.fa.fa-chevron-left + - visible_pages = [] + - window_size = 2 + - visible_pages << 1 + - if total_pages > 1 + - left = [current_page - window_size, 2].max + - right = [current_page + window_size, total_pages - 1].min + - visible_pages << :gap if left > 2 + - visible_pages.concat((left..right).to_a) if left <= right + - visible_pages << :gap if right < total_pages - 1 + - visible_pages << total_pages + - visible_pages.each do |page| + - if page == :gap + %span.page-ellipsis … + - else + %button.page-btn{class: ('active' if page == current_page), onclick: "window.location.href='?#{request.query_parameters.merge(page: page).to_query}'"} + = page + %button.page-btn{disabled: current_page == total_pages, onclick: "window.location.href='?#{request.query_parameters.merge(page: (current_page + 1)).to_query}'"} + %i.fa.fa-chevron-right diff --git a/config/locales/enlistments.en.yml b/config/locales/enlistments.en.yml index 4573c7e27..a2941789c 100644 --- a/config/locales/enlistments.en.yml +++ b/config/locales/enlistments.en.yml @@ -23,7 +23,18 @@ en: allowed_files: 'Allowed Files' actions: 'Actions' new_code_location: 'New Code Location' + show_unprocessable_sources: 'Show unprocessable sources' currently_importing: 'We are currently working on importing your GitHub Repositories. This might take some time. Please refresh the page or check back momentarily.' + file: 'file' + description1: 'Open Hub ignored' + description2: 'during processing.' + description3: 'All files included.' + description4: 'Open Hub allowed' + edit_ignored: 'Edit ignored files' + edit_allowed: 'Edit allowed files' + remove: 'Remove' + confirm: 'Are you sure you want to delete this code location?' + dnf: "Error: This Code Location is flagged as Do Not Fetch. Please contact us for assistance" new: page_title: 'The %{name} Open Source Project on Open Hub : New Code Location Page' code_locations: 'Code Locations' From a6ff57795144a2b6e4534f5eccfe5cbcfab63a3f Mon Sep 17 00:00:00 2001 From: bd-vaibhav Date: Tue, 14 Apr 2026 23:39:19 +0530 Subject: [PATCH 34/53] OTWO-7618 Implement figma design for organizations show page (#1883) * OTWO-7618 Implement figma design for organizations show page * OTWO-7618 Implement figma design for organizations show page * Open OTWO-7618 Implement figma design for organizations show page * Fixed the test errors --- app/assets/javascripts/orgs.js | 73 +- app/assets/stylesheets/base.sass | 53 ++ app/assets/stylesheets/dark_theme.sass | 22 +- app/assets/stylesheets/explore.sass | 1 + app/assets/stylesheets/orgs.sass | 661 ++++++++++++++++-- app/assets/stylesheets/projects.sass | 44 +- app/controllers/explore_controller.rb | 2 +- app/decorators/icon.rb | 29 +- app/decorators/organization_decorator.rb | 5 +- app/decorators/project_decorator.rb | 4 +- app/views/explore/_most_active_orgs.html.haml | 42 +- app/views/explore/_newest_orgs.html.haml | 32 +- .../_orgs_by_30_day_commit_volume.html.haml | 56 +- app/views/explore/_stats_by_sector.html.haml | 6 +- app/views/explore/orgs.html.haml | 20 +- .../orgs_by_thirty_day_commit_volume.js.haml | 33 +- app/views/organizations/show.html.haml | 2 +- app/views/projects/_project_index.html.haml | 7 +- config/locales/explore.en.yml | 1 + config/shared/sort_options.yml | 29 + test/controllers/explore_controller_test.rb | 6 +- test/decorators/icon_test.rb | 27 +- 22 files changed, 959 insertions(+), 196 deletions(-) diff --git a/app/assets/javascripts/orgs.js b/app/assets/javascripts/orgs.js index bb71ec81a..70f493696 100644 --- a/app/assets/javascripts/orgs.js +++ b/app/assets/javascripts/orgs.js @@ -1,4 +1,6 @@ var GaugeProgress = { + themeListenerAttached: false, + init: function(){ var scope = this; var page_ids = ['explore_orgs_page']; @@ -14,12 +16,29 @@ var GaugeProgress = { scope.orgs_progress_bar() } }) + + if (!this.themeListenerAttached) { + var themeToggleBtn = document.getElementById('theme-toggle'); + if (themeToggleBtn) { + themeToggleBtn.addEventListener('click', function() { + setTimeout(function() { + scope.init(); + }, 1); + }); + this.themeListenerAttached = true; + } + } }, config: function(data){ + var isDarkTheme = $('html').hasClass('dark'); + var bgColor = isDarkTheme ? '#2D1548' : '#FFF'; + var labelColor = isDarkTheme ? '#FFF' : '#000'; + return { chart: { type: 'solidgauge', + backgroundColor: bgColor, }, title: null, pane: { @@ -28,7 +47,7 @@ var GaugeProgress = { startAngle: -90, endAngle: 90, background: { - backgroundColor: '#FFF', + backgroundColor: bgColor, innerRadius: '100%', outerRadius: '45%', shape: 'arc' @@ -65,7 +84,7 @@ var GaugeProgress = { } } }, - series: [{data: [data], dataLabels: { format: '

{y}

'} }], + series: [{data: [data], dataLabels: { format: '

{y}

'} }], exporting: { enabled: false } @@ -86,18 +105,50 @@ var GaugeProgress = { var OrgsFilter = { init: function(){ - $('#explore_orgs_page .chzn-select').chosen().change(function(){ - $('.busy#commit_volume_loader').toggleClass('hidden') - $('#orgs_by_30_days_volume table').toggleClass('hidden') + var filterOrgs = function(filterValue) { + $('.busy#commit_volume_loader').removeClass('hidden') $.ajax({ - url: '/explore/orgs_by_thirty_day_commit_volume?format=js&filter='+ $(this).val(), + url: '/explore/orgs_by_thirty_day_commit_volume?format=js&org_type='+ filterValue, type: "GET", success: function(){ - $('#orgs_by_30_days_volume table').toggleClass('hidden') - $('.busy#commit_volume_loader').toggleClass('hidden') + $('.busy#commit_volume_loader').addClass('hidden') } }) - }) + }; + + // Legacy chosen dropdown support + $('#explore_orgs_page .chzn-select').chosen().change(function(){ + filterOrgs($(this).val()); + }); + + // Custom dropdown for org type filter - handle button toggle and aria-expanded + var $orgDropdown = $('#orgs_by_30_days_volume .custom-sort-dropdown'); + var $orgButton = $orgDropdown.find('.sort-dropdown-btn'); + + $orgButton.on('click', function() { + var isOpen = $orgDropdown.hasClass('open'); + $orgButton.attr('aria-expanded', !isOpen); + }); + + // Close dropdown when clicking outside + $(document).on('click', function(e) { + if (!$(e.target).closest('#orgs_by_30_days_volume .custom-sort-dropdown').length) { + $orgDropdown.removeClass('open'); + $orgButton.attr('aria-expanded', 'false'); + } + }); + + // Handle dropdown item clicks + $('#orgs_by_30_days_volume .sort-dropdown-item').on('click', function(e) { + e.preventDefault(); + var value = $(this).data('value'); + + // Close dropdown and update aria-expanded + $orgDropdown.removeClass('open'); + $orgButton.attr('aria-expanded', 'false'); + + filterOrgs(value); + }); } } @@ -120,5 +171,7 @@ var OrgClaimProject = { } } $(document).ready(function() { - OrgClaimProject.init() + GaugeProgress.init(); + OrgsFilter.init(); + OrgClaimProject.init(); }); diff --git a/app/assets/stylesheets/base.sass b/app/assets/stylesheets/base.sass index 0bada246d..2e3ed7050 100644 --- a/app/assets/stylesheets/base.sass +++ b/app/assets/stylesheets/base.sass @@ -165,3 +165,56 @@ ul.unstyled, ol.unstyled .font-1em font-size: 1em + +// Override any third-party CSS that sets display: inline on icon elements +a [class^="icon-"], +a [class*=" icon-"] + &:not(.icon-letter) + display: inline-flex !important + +// Icon Container - Global Baseline Styling +.icon-container + display: inline-flex !important + align-items: center + justify-content: center + width: 48px + height: 48px + border-radius: 12px + overflow: hidden + background-color: #f9fafb + border: 1px solid rgba(229, 231, 235, 0.8) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) + flex-shrink: 0 + + html.dark & + background-color: #1D0631 + border-color: rgba(255, 255, 255, 0.1) + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.24) + + &.has-logo + background-color: transparent + border: none + box-shadow: none + + img + width: 100% + height: 100% + object-fit: contain + display: block + + @media (min-width: 640px) + width: 56px + height: 56px + .icon-letter + font-size: 26px + +// Icon Letter Fallback - Global Styling +.icon-letter + font-size: 22px + font-weight: 700 + color: #5A2A82 + text-transform: uppercase + user-select: none + + html.dark & + color: #ffb91a diff --git a/app/assets/stylesheets/dark_theme.sass b/app/assets/stylesheets/dark_theme.sass index e7fc0f198..a8b30a2af 100644 --- a/app/assets/stylesheets/dark_theme.sass +++ b/app/assets/stylesheets/dark_theme.sass @@ -23,14 +23,14 @@ html.dark .well background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important // Tables table background-color: #1D0631 color: #e2e8f0 td, th - border-color: #4b5563 !important + border-color: #5A2A82 !important color: #e2e8f0 !important // Links @@ -51,7 +51,7 @@ html.dark input, textarea, select background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important &::placeholder color: #94a3b8 !important @@ -74,12 +74,12 @@ html.dark // Alerts .alert background-color: #1D0631 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important // Dropdown menus .dropdown-menu background-color: #1D0631 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important li a color: #e2e8f0 !important &:hover @@ -102,7 +102,7 @@ html.dark .for_search_all_code background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important // Badges .badge @@ -112,7 +112,7 @@ html.dark // Panels .panel background-color: #1D0631 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important .panel-heading background-color: rgba(90, 42, 130, 0.5) !important color: #e2e8f0 !important @@ -124,14 +124,14 @@ html.dark pre, code background-color: #1D0631 !important color: #e2e8f0 !important - border-color: #4b5563 !important + border-color: #5A2A82 !important // Borders .mezzo - border-top-color: #4b5563 !important + border-top-color: #5A2A82 !important .right_border - border-right-color: #4b5563 !important + border-right-color: #5A2A82 !important // Text colors .signature_color @@ -153,7 +153,7 @@ html.dark .well background-color: #1D0631 !important - border: 1px solid #4b5563 !important + border: 1px solid #5A2A82 !important box-shadow: none !important h4 diff --git a/app/assets/stylesheets/explore.sass b/app/assets/stylesheets/explore.sass index 03615a878..9d65f6817 100644 --- a/app/assets/stylesheets/explore.sass +++ b/app/assets/stylesheets/explore.sass @@ -423,6 +423,7 @@ $shadow-dark-hover: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) align-items: flex-start gap: 16px margin-bottom: 24px + padding-top: 24px flex-wrap: wrap @media (max-width: 1024px) diff --git a/app/assets/stylesheets/orgs.sass b/app/assets/stylesheets/orgs.sass index da69909ae..a2918d32a 100644 --- a/app/assets/stylesheets/orgs.sass +++ b/app/assets/stylesheets/orgs.sass @@ -1,11 +1,38 @@ #explore_orgs_page h1 - margin-left: -10px + font-size: 36px + font-weight: 700 + color: #1a202c + margin: 0 + letter-spacing: normal + line-height: 1.2 + + html.dark & + color: #ffffff + + h2 + font-weight: 700 !important + color: #1a202c !important + margin: 0 0 16px 0 !important + line-height: 1.2 !important + + html.dark & + color: #ffffff !important + #orgs_by_30_days_volume label#filter margin-top: 4px + td + vertical-align: middle + &:first-child + text-align: left + padding-left: 12px td a line-height: 29px + &.logo + display: inline-block + line-height: 0 + vertical-align: middle .busy position: absolute top: 185px @@ -13,58 +40,564 @@ width: 16px height: 16px #header th - border-top: 1px solid #ddd border-bottom: 0px - background-color: #f9f9f9 - tbody tr + background-color: #5b5d6237 + color: #000 + padding: 10px 12px + + html.dark & + background-color: #3A1D58 + color: #ffffff + + .sort-section + display: flex + align-items: center + gap: 8px + margin-bottom: 5px + + label + font-size: 14px + color: #6b7280 + white-space: nowrap + + html.dark & + color: #d1d5db + + .custom-sort-dropdown + position: relative + display: inline-block + + .sort-dropdown-btn + display: flex + align-items: center + gap: 8px + height: 40px + padding: 0 12px + background-color: #f9fafb + border: 1px solid #d1d5db + border-radius: 8px + color: #111827 + font-size: 14px + font-weight: 400 + min-width: 180px + cursor: pointer + transition: background 0.15s ease, border-color 0.15s ease + white-space: nowrap + box-sizing: border-box + + &:hover + background-color: #f3f4f6 + border-color: #5A2A82 + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + color: #ffffff + + &:hover + background-color: rgba(29, 6, 49, 0.8) + border-color: #ffb91a + + .selection + flex: 1 + text-align: left + + i + font-size: 12px + transition: transform 0.2s ease + + &.open + .sort-dropdown-menu + display: block + + .sort-dropdown-menu + display: none + position: absolute + top: 100% + padding: 0 + left: 0 + margin-top: 4px + min-width: 180px + background-color: #ffffff + border: 1px solid #d1d5db + border-radius: 8px + overflow: hidden + z-index: 1000 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23) + + html.dark & + background-color: #1D0631 + border-color: #5A2A82 + box-shadow: 0 10px 20px rgba(0, 0, 0, 0.38), 0 6px 6px rgba(0, 0, 0, 0.46) + + .sort-dropdown-item + display: block + width: 100% + padding: 8px 16px + color: #111827 + font-size: 14px + font-weight: 400 + text-align: left + text-decoration: none + background: transparent + border: none + box-sizing: border-box + transition: background 0.15s ease, color 0.15s ease + cursor: pointer + + &:hover + background: #f3f4f6 + + &.active + background-color: #5A2A82 !important + color: #ffffff + font-weight: 400 + + &:hover + background-color: #5A2A82 + + html.dark & + color: #ffffff + + &:hover + background: rgba(255, 255, 255, 0.1) + + // Desktop Header (visible on both mobile and desktop) + .desktop-header + display: block + + // Mobile Container + .mobile-org-container + display: block + + @media (min-width: 768px) + display: none + + .orgs-card + background: #ffffff + border-radius: 16px + overflow: hidden + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + transition: box-shadow 0.3s ease + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + + html.dark & + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + + .card-body-mobile + padding: 16px + display: flex + flex-direction: column + gap: 12px + + .org-card + background: #f9fafb + border: 1px solid #e5e7eb + border-radius: 8px + padding: 12px + margin-bottom: 12px + + html.dark & + background: rgba(29, 6, 49, 0.5) + border-color: rgba(90, 42, 130, 0.3) + &:last-child - border-bottom: 1px solid #ddd - &:nth-child(even) - background-color: #f9f9f9 + margin-bottom: 0 + + .org-card-header + display: flex + align-items: center + gap: 8px + margin-bottom: 12px + + .org-card-icon + flex-shrink: 0 + line-height: 0 + + .org-card-name + flex: 1 + color: #5A2A82 + font-weight: 500 + font-size: 14px + text-decoration: none + + html.dark & + color: #ffb91a + + &:hover + text-decoration: underline + + .org-card-size + display: inline-flex + align-items: center + justify-content: center + min-width: 24px + height: 24px + padding: 0 6px + background: #e5e7eb + color: #1a202c + font-weight: 700 + font-size: 12px + border-radius: 4px + + html.dark & + background: rgba(90, 42, 130, 0.3) + color: #ffffff + + .org-card-body + display: grid + grid-template-columns: 1fr 1fr + gap: 8px 16px + font-size: 12px + + .org-card-row + display: flex + justify-content: space-between + align-items: center + + .org-card-label + color: #6b7280 + font-size: 11px + + html.dark & + color: #9ca3af + + .org-card-value + color: #374151 + font-weight: 500 + + html.dark & + color: #d1d5db + + // Desktop Table View + .desktop-org-table + display: none + + @media (min-width: 768px) + display: table + + .orgs-layout + display: flex + flex-direction: column + gap: 20px + align-items: flex-start + + @media (min-width: 768px) + flex-direction: row + + .left-column + width: 100% + display: flex + flex-direction: column + gap: 20px + + @media (min-width: 768px) + width: 32% + + .right-column + width: 100% + display: flex + flex-direction: column + gap: 20px + + @media (min-width: 768px) + width: 68% #newest-orgs - width: 32% - .row - margin-bottom: 15px - margin-left: 0px - padding-top: 15px - .col-md-3 - margin-left: -2px - padding-left: 0px - p - margin-left: 5px - .col-md-9 a - @include site-link-color - font-weight: bold - font-size: 15px + width: 100% + + .newest-orgs-card + background: #ffffff + border-radius: 16px + overflow: hidden + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + transition: box-shadow 0.3s ease + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + + html.dark & + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + + .card-header + padding: 16px + border-bottom: 2px solid #e2e8f0 + background: #ffffff + + html.dark & + border-bottom-color: rgba(90, 42, 130, 0.3) + background: #2D1548 + + h2 + margin: 0 !important + padding: 0 !important + font-size: 16px !important + font-weight: 700 !important + + .card-body + padding: 16px + display: flex + flex-direction: column + gap: 12px + + .org-item + display: flex + align-items: flex-start + gap: 12px + padding: 8px 0 + + .org-logo + flex-shrink: 0 + text-decoration: none + + a + display: inline-block + line-height: 0 + + .org-info + flex: 1 + min-width: 0 + + .org-name + display: block + color: #5A2A82 + font-weight: 600 + font-size: 14px + text-decoration: none + margin-bottom: 6px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + html.dark & + color: #ffb91a + + &:hover + text-decoration: underline + + .org-stats + display: flex + flex-direction: column + gap: 2px + font-size: 12px + + .projects-count + .projects-value + color: #1a202c + font-weight: 600 + text-decoration: none + + html.dark & + color: #ffffff + + &:hover + text-decoration: underline + + .added-time + color: #718096 + font-size: 11px + font-style: italic + + html.dark & + color: #9ca3af + + .card-footer + padding: 12px 16px + border-top: 2px solid #e2e8f0 + background: #f8f9fa + text-align: left + + html.dark & + border-top-color: rgba(90, 42, 130, 0.3) + background: #2D1548 + + .more-link + color: #5A2A82 + font-size: 12px + text-decoration: none + font-weight: 500 + + html.dark & + color: #ffb91a + + &:hover + text-decoration: underline #orgs_by_30_days_volume min-height: 260px - width: 64% - margin-left: 20px + width: 100% #most-active-orgs - width: 32% - padding: 0px 0px 0px 10px - border-radius: 0 - margin-bottom: 0px - .title - margin-top: 7px - margin-bottom: 15px - .row - margin-bottom: 15px - .col-md-3 a - margin: 5px 0px 0px 0px - .col-md-9 a - @include site-link-color - font-weight: bold - font-size: 15px - .row#active-org-timestamp - font-size: 9px - font-style: italic - margin-bottom: 0px + width: 100% + + .most-active-card + background: #ffffff + border-radius: 16px + overflow: hidden + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24) + transition: box-shadow 0.3s ease + + html.dark & + background: #2D1548 + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.24), 0 1px 2px rgba(0, 0, 0, 0.48) + + &:hover + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23) + + html.dark & + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.32), 0 3px 6px rgba(0, 0, 0, 0.46) + + .card-header + padding: 16px + border-bottom: 2px solid #e2e8f0 + background: #ffffff + + html.dark & + border-bottom-color: rgba(90, 42, 130, 0.3) + background: #2D1548 + + h2 + margin: 0 !important + padding: 0 !important + font-size: 16px !important + font-weight: 700 !important + + .card-body + padding: 16px + display: flex + flex-direction: column + gap: 12px + + .org-item + display: flex + align-items: flex-start + gap: 12px + padding: 8px 0 + + .org-logo + flex-shrink: 0 + text-decoration: none + + a + display: inline-block + line-height: 0 + + .org-info + flex: 1 + min-width: 0 + + .org-name + display: block + color: #5A2A82 + font-weight: 600 + font-size: 14px + text-decoration: none + margin-bottom: 6px + white-space: nowrap + overflow: hidden + text-overflow: ellipsis + + html.dark & + color: #ffb91a + + &:hover + text-decoration: underline + + .org-stats + display: flex + align-items: center + gap: 6px + font-size: 12px + + .commits-value + color: #1a202c + font-weight: 600 + text-decoration: none + + html.dark & + color: #ffffff + + .stat-label + color: #718096 + font-size: 11px + + html.dark & + color: #9ca3af + + .card-footer + display: flex + justify-content: space-between + align-items: center + padding: 12px 16px + border-top: 2px solid #e2e8f0 + background: #f8f9fa + font-size: 12px + + html.dark & + border-top-color: rgba(90, 42, 130, 0.3) + background: #2D1548 + + p + margin: 0 + color: #718096 + + html.dark & + color: #9ca3af + + .footer-text + flex: 0 0 auto + + p + font-style: italic + font-size: 11px + + .footer-timestamp + flex: 0 0 auto + text-align: right + + p + font-style: italic + font-size: 11px #stats-by-sector + overflow-x: auto + + table + font-size: 12px + + @media (min-width: 768px) + font-size: 14px + + thead th + padding: 8px 6px !important + font-size: 11px + background-color: #5b5d6237 + color: #000 + + html.dark & + background-color: #3A1D58 + color: #ffffff + + @media (min-width: 768px) + padding: 12px !important + font-size: 14px + + tbody td + padding: 8px 6px !important + + @media (min-width: 768px) + padding: 12px !important + .gauge-1 //commercial .highcharts-color-0 fill: #7b559b @@ -82,11 +615,36 @@ fill: #482268 stroke: #482268 + html.dark & + .gauge-1 // Commercial - bright yellow + .highcharts-color-0 + fill: #ffb91a !important + stroke: #ffb91a !important + + .gauge-2 // Education - lighter yellow + .highcharts-color-0 + fill: #ffd966 !important + stroke: #ffd966 !important + + .gauge-3 // Government - light golden + .highcharts-color-0 + fill: #ffd966 !important + stroke: #ffd966 !important + + .gauge-4 // Non-Profit - deeper orange-yellow + .highcharts-color-0 + fill: #e6a617 !important + stroke: #e6a617 !important + #commit-gauge - width: 220px - height: 80px + width: 120px + height: 60px margin: 0 auto + @media (min-width: 768px) + width: 220px + height: 80px + #org_filter_no_result width: 620px height: 260px @@ -98,11 +656,22 @@ line-height: 210px .progress#progress-container - width: 180px + width: 100px border: 1px solid #afd5eb border-radius: 0 + @media (min-width: 768px) + width: 180px + #progress-bar - background-color: color-gradient($SECONDARY_SLATE_BLUE, 120) + background-color: #482268 color: white padding: 0 0 0 3px + font-size: 11px + + @media (min-width: 768px) + font-size: 14px + + html.dark & + background-color: #ffb91a + color: #000 diff --git a/app/assets/stylesheets/projects.sass b/app/assets/stylesheets/projects.sass index 4f470adcc..10655c722 100644 --- a/app/assets/stylesheets/projects.sass +++ b/app/assets/stylesheets/projects.sass @@ -954,47 +954,6 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) gap: 12px margin-bottom: 12px - .project-icon - flex-shrink: 0 - width: 48px - height: 48px - border-radius: 12px - display: flex - align-items: center - justify-content: center - font-size: 24px - overflow: hidden - background-color: $bg-color - border: 1px solid $border-color - box-shadow: 0 1px 2px rgba(0,0,0,0.05) - html.dark & - background-color: $dark-bg-color - border-color: $dark-border-color - &.has-logo - background-color: transparent - border: none - box-shadow: none - html.dark & - background-color: transparent - border: none - img - width: 100% - height: 100% - object-fit: contain - .project-icon-letter - font-size: 22px - font-weight: 700 - color: $primary-color - text-transform: uppercase - user-select: none - html.dark & - color: $dark-primary-color - - @media (min-width: 640px) - width: 56px - height: 56px - font-size: 30px - .project-description flex: 1 font-size: 14px @@ -1055,6 +1014,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) border-top: 1px solid $border-color padding-left: 0 padding-top: 16px + margin-bottom: 16px .stats-grid display: grid @@ -1187,7 +1147,7 @@ $dark-shadow-lg: 0 8px 16px rgba(0,0,0,0.48) background-color: rgba(255, 185, 26, 0.3) html.dark & background-color: #ffb91a - color: #1D0631 + color: #1D0631 !important .more-tags color: $primary-color diff --git a/app/controllers/explore_controller.rb b/app/controllers/explore_controller.rb index 687b3f2f5..c3d75c112 100644 --- a/app/controllers/explore_controller.rb +++ b/app/controllers/explore_controller.rb @@ -23,7 +23,7 @@ def orgs end def orgs_by_thirty_day_commit_volume - @org_by_30_day_commits = OrgThirtyDayActivity.filter(params[:filter]) + @org_by_30_day_commits = OrgThirtyDayActivity.filter(params[:org_type]) end def projects; end diff --git a/app/decorators/icon.rb b/app/decorators/icon.rb index ea423e418..64dc77e80 100644 --- a/app/decorators/icon.rb +++ b/app/decorators/icon.rb @@ -9,18 +9,31 @@ class Icon < Cherry::Decorator delegate :logo, :name, to: :object - def image(with_dimensions: true) - if logo - width_and_height = with_dimensions ? dimensions : '' - css_style = "#{width_and_height} border:0 none;" - image_tag(logo.attachment.url(size), style: css_style, itemprop: 'image', alt: 'img avatar') - else - content_tag :p, name.first.capitalize, style: default_style - end + def image(with_dimensions: true, container_class: 'icon-container') + has_attachment = logo&.attachment&.file? + container_classes = has_attachment ? "#{container_class} has-logo" : container_class + content = has_attachment ? logo_with_fallback(with_dimensions) : letter_only + content_tag :div, content, class: container_classes end private + def logo_with_fallback(with_dimensions) + css_style = "#{dimensions if with_dimensions} border:0 none;" + onerror_handler = "this.style.display='none'; this.nextElementSibling.style.display='flex'; " \ + "this.parentElement.classList.remove('has-logo');" + img = image_tag(logo.attachment.url(size), + style: css_style, + itemprop: 'image', + alt: name, + onerror: onerror_handler) + img + content_tag(:span, name.first.upcase, class: 'icon-letter', style: 'display:none') + end + + def letter_only + content_tag :span, name.first.upcase, class: 'icon-letter' + end + def size @context[:size] || :small end diff --git a/app/decorators/organization_decorator.rb b/app/decorators/organization_decorator.rb index 4dd8ba2ae..7d3de56d0 100644 --- a/app/decorators/organization_decorator.rb +++ b/app/decorators/organization_decorator.rb @@ -16,8 +16,9 @@ def sidebar ] end - def icon(size = :small, opts = {}) - Icon.new(organization, context: { size: size, options: opts }).image(with_dimensions: false) + def icon(size = :small, container_class: 'icon-container', **opts) + Icon.new(organization, context: { size: size, options: opts }).image(with_dimensions: false, + container_class: container_class) end class << self diff --git a/app/decorators/project_decorator.rb b/app/decorators/project_decorator.rb index ac9436238..766717280 100644 --- a/app/decorators/project_decorator.rb +++ b/app/decorators/project_decorator.rb @@ -5,12 +5,12 @@ class ProjectDecorator < Cherry::Decorator delegate :main_language, :links, to: :project - def icon(size = :small, opts = {}) + def icon(size = :small, container_class: 'icon-container', **opts) opts[:color] = language_text_color(main_language) opts[:bg] = language_color(main_language) icon = Icon.new(project, context: { size: size, options: opts }) - icon.image + icon.image(with_dimensions: true, container_class: container_class) end def sorted_link_list diff --git a/app/views/explore/_most_active_orgs.html.haml b/app/views/explore/_most_active_orgs.html.haml index b1d3eb16f..8e69e55f8 100644 --- a/app/views/explore/_most_active_orgs.html.haml +++ b/app/views/explore/_most_active_orgs.html.haml @@ -1,20 +1,22 @@ -.well.col-md-4.margin_left_5#most-active-orgs - %h2.title= t('.title') - - @most_active_orgs.each do |org_thirty_day_activity| - - org = org_thirty_day_activity.organization - .row - .col-md-3 - = link_to org.decorate.icon(:med), organization_path(org), title: org.name, - style: 'text-decoration: none', class: 'logo' - .col-md-9 - = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name - .margin_top_10 - = link_to org_thirty_day_activity.commits_per_affiliate, - organization_path(org, view: 'affiliated_committers') - %i.small= t('.commits_or_affiliates') - .row.no_margin_bottom#active-org-timestamp - .col-md-6 - %p= t('.within_30_days') - .col-md-6.pull-right - %p.pull-right.margin_right_10 - = t '.last_calculated', duration: time_ago_in_words(@most_active_orgs.first.created_at) if @most_active_orgs.first +#most-active-orgs + .most-active-card + .card-header + %h2= t('.title') + .card-body + - @most_active_orgs.each do |org_thirty_day_activity| + - org = org_thirty_day_activity.organization + .org-item + .org-logo + = link_to org.decorate.icon(:med), organization_path(org), title: org.name, class: 'logo' + .org-info + = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name, class: 'org-name' + .org-stats + = link_to org_thirty_day_activity.commits_per_affiliate, + organization_path(org, view: 'affiliated_committers'), class: 'commits-value' + %span.stat-label= t('.commits_or_affiliates') + .card-footer + .footer-text + %p= t('.within_30_days') + .footer-timestamp + %p + = t '.last_calculated', duration: time_ago_in_words(@most_active_orgs.first.created_at) if @most_active_orgs.first diff --git a/app/views/explore/_newest_orgs.html.haml b/app/views/explore/_newest_orgs.html.haml index ed45ccd0b..ccf1ac945 100644 --- a/app/views/explore/_newest_orgs.html.haml +++ b/app/views/explore/_newest_orgs.html.haml @@ -1,14 +1,18 @@ -.col-md-4#newest-orgs - %h2= t '.title' - - @newest_orgs.each do |org| - .row - .col-md-3 - = link_to org.decorate.icon(:med), organization_path(org), style: 'text-decoration: none', title: org.name - .col-md-9 - = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name - .margin_top_10 - = link_to t('.projects_count', count: org.projects_count), - organization_path(org, view: 'portfolio_projects') - %span.small= t('.added', duration: time_ago_in_words(org.updated_at)) - %p - = link_to t('.more_html'), organizations_path(sort: 'newest') +#newest-orgs + .newest-orgs-card + .card-header + %h2= t('.title') + .card-body + - @newest_orgs.each do |org| + .org-item + .org-logo + = link_to org.decorate.icon(:med), organization_path(org), title: org.name, class: 'logo' + .org-info + = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name, class: 'org-name' + .org-stats + %span.projects-count + = link_to t('.projects_count', count: org.projects_count), + organization_path(org, view: 'portfolio_projects'), class: 'projects-value' + %span.added-time= t('.added', duration: time_ago_in_words(org.updated_at)) + .card-footer + = link_to t('.more_html'), organizations_path(sort: 'newest'), class: 'more-link' diff --git a/app/views/explore/_orgs_by_30_day_commit_volume.html.haml b/app/views/explore/_orgs_by_30_day_commit_volume.html.haml index d020ca4d3..c1345bc1a 100644 --- a/app/views/explore/_orgs_by_30_day_commit_volume.html.haml +++ b/app/views/explore/_orgs_by_30_day_commit_volume.html.haml @@ -1,16 +1,56 @@ -.col-md-8.no_padding#orgs_by_30_days_volume - %h2.pull-left= t('.title') - .pull-right.nav-select-container#sort_by - %label.pull-left#filter!= t('.filter') - = select_tag(:org_type, options_for_select(OrgThirtyDayActivity::SORT_TYPES), - class: 'chzn-select pull-right', style: 'width:126px') - .clearfix +#orgs_by_30_days_volume + -# Desktop Title and Filter + .desktop-header + %h2.pull-left= t('.title') + .pull-right#sort_by + .sort-section + %label!= t('.filter') + :ruby + current_filter = params[:org_type] || 'all_orgs' + selected_label = OrgThirtyDayActivity::SORT_TYPES.find { |opt| opt[1] == current_filter }&.first || OrgThirtyDayActivity::SORT_TYPES.first.first + .custom-sort-dropdown + %button.sort-dropdown-btn{ type: 'button', aria: { expanded: 'false', controls: 'org-type-menu', label: t('.filter_type_label') } } + %span.selection{ data: { value: current_filter } }= selected_label + %i.fa.fa-chevron-down + .dropdown-menu.sort-dropdown-menu{ id: 'org-type-menu', role: 'menu' } + - OrgThirtyDayActivity::SORT_TYPES.each do |label, value| + %button.sort-dropdown-item{ type: 'button', data: { value: value }, role: 'menuitem', class: (value == current_filter ? 'active' : '') }= label + %input{ type: 'hidden', name: 'org_type', value: current_filter, class: 'sort-value-input', id: 'org_type' } + .clearfix + #org_filter_no_result{ class: @org_by_30_day_commits.empty? ? '' : 'hidden' } %h5 %i.icon-warning-sign = t '.no_results' .busy.hidden#commit_volume_loader - %table.table.table-condensed{ class: @org_by_30_day_commits.empty? ? 'hidden' : '' } + + -# Mobile Card View + .mobile-org-container{ class: @org_by_30_day_commits.empty? ? 'hidden' : '' } + .orgs-card + .card-body-mobile + - @org_by_30_day_commits.each do |org_30_day_activity| + - org = org_30_day_activity.organization + .org-card + .org-card-header + = link_to org.decorate.icon(:small), organization_path(org), title: org.name, class: 'org-card-icon' + = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name, class: 'org-card-name' + %span.org-card-size= org_30_day_activity.decorate.project_count_text + .org-card-body + .org-card-row + .org-card-label= t '.type' + .org-card-value= org.org_type_label + .org-card-row + .org-card-label= t '.projects' + .org-card-value= org_30_day_activity.project_count + .org-card-row + .org-card-label= t '.affiliates' + .org-card-value= org_30_day_activity.affiliate_count + .org-card-row + .org-card-label= t '30_day_commits' + .org-card-value= org_30_day_activity.thirty_day_commit_count + + -# Desktop Table View + %table.table.table-condensed.desktop-org-table{ class: @org_by_30_day_commits.empty? ? 'hidden' : '' } .margin_top_15 %thead %tr#header diff --git a/app/views/explore/_stats_by_sector.html.haml b/app/views/explore/_stats_by_sector.html.haml index e62d9e6ac..0f209b6aa 100644 --- a/app/views/explore/_stats_by_sector.html.haml +++ b/app/views/explore/_stats_by_sector.html.haml @@ -4,17 +4,17 @@ avg_commits = @stats_by_sector.map(&:average_commits).max gauge_scale = scale_to(avg_commits, 1000) -.col-md-8.margin_left_10#stats-by-sector +#stats-by-sector %h2.margin_bottom_20= t '.title' %table.table{ style: 'border: 1px solid #ccc' } %thead - %tr{ style: 'background-color: #ccc' } + %tr %th.strong= t '.sector' %th.strong.center= t '.average' %th.strong.center= t '.orgs' %tbody - @stats_by_sector.each do |orgs| - %tr{ style: 'background-color: #fff' } + %tr %td.strong= Organization::ORG_TYPES.invert[orgs.org_type] %td #commit-gauge{ 'data-gauge' => orgs.average_commits.to_s, 'data-gauge-max' => gauge_scale.to_s, class: "gauge-#{orgs.org_type}" } diff --git a/app/views/explore/orgs.html.haml b/app/views/explore/orgs.html.haml index 03473713d..aefc10c8b 100644 --- a/app/views/explore/orgs.html.haml +++ b/app/views/explore/orgs.html.haml @@ -2,11 +2,15 @@ - content_for :html_title do = t('.page_title') -%h1.margin_bottom_20= t('.title') -.row - = render partial: 'most_active_orgs' - = render partial: 'orgs_by_30_day_commit_volume' -.margin_bottom_20 -.row - = render partial: 'newest_orgs' - = render partial: 'stats_by_sector' +.explore-page-wrapper + .explore-header-top + %h1= t('.title') + = render 'shared/search' + + .orgs-layout + .left-column + = render partial: 'most_active_orgs' + = render partial: 'newest_orgs' + .right-column + = render partial: 'orgs_by_30_day_commit_volume' + = render partial: 'stats_by_sector' diff --git a/app/views/explore/orgs_by_thirty_day_commit_volume.js.haml b/app/views/explore/orgs_by_thirty_day_commit_volume.js.haml index 518a3ffd7..092201bf0 100644 --- a/app/views/explore/orgs_by_thirty_day_commit_volume.js.haml +++ b/app/views/explore/orgs_by_thirty_day_commit_volume.js.haml @@ -1,12 +1,37 @@ - if @org_by_30_day_commits.empty? :plain $('#org_filter_no_result').removeClass('hidden'); - $('#30_day_commit_volume').addClass('hidden'); - $('#header').addClass('hidden'); + $('#orgs_by_30_days_volume .desktop-org-table').addClass('hidden'); + $('.mobile-org-container').addClass('hidden'); - else + - mobile_cards_html = capture do + - @org_by_30_day_commits.each do |org_30_day_activity| + - org = org_30_day_activity.organization + .org-card + .org-card-header + = link_to org.decorate.icon(:small), organization_path(org), title: org.name, class: 'org-card-icon' + = link_to h(truncate(org.name, length: 24)), organization_path(org), title: org.name, class: 'org-card-name' + %span.org-card-size= org_30_day_activity.decorate.project_count_text + .org-card-body + .org-card-row + .org-card-label= t '.type' + .org-card-value= org.org_type_label + .org-card-row + .org-card-label= t '.projects' + .org-card-value= org_30_day_activity.project_count + .org-card-row + .org-card-label= t '.affiliates' + .org-card-value= org_30_day_activity.affiliate_count + .org-card-row + .org-card-label= t '30_day_commits' + .org-card-value= org_30_day_activity.thirty_day_commit_count + :plain - $('#header').removeClass('hidden'); $('#org_filter_no_result').addClass('hidden'); - $('#30_day_commit_volume').removeClass('hidden'); + $('#orgs_by_30_days_volume .desktop-org-table').removeClass('hidden'); var partial = "#{ j render(partial: 'org_30_day_activity', collection: @org_by_30_day_commits) }" $('#30_day_commit_volume').html(partial); + + $('.mobile-org-container').removeClass('hidden'); + var mobileCards = "#{ j mobile_cards_html }"; + $('.card-body-mobile').html(mobileCards); diff --git a/app/views/organizations/show.html.haml b/app/views/organizations/show.html.haml index 02c7e33f3..03d7ccb3d 100644 --- a/app/views/organizations/show.html.haml +++ b/app/views/organizations/show.html.haml @@ -4,12 +4,12 @@ %h2= t('.title') .col-md-5.pull_left#org_summary + = render partial: '/shared/add_this', locals: { text: "#{@organization.name} organization" } - if @organization.description.blank? = t('.no_desc') = link_to t('.add_desc'), edit_organization_path(@organization) - else = simple_format @organization.description.strip_tags - = render partial: '/shared/add_this', locals: { text: "#{@organization.name} organization" } .col-md-6.col-md-offset-1 = render partial: 'organizations/show/quick_reference' diff --git a/app/views/projects/_project_index.html.haml b/app/views/projects/_project_index.html.haml index 0119696c1..8ff27d5f4 100644 --- a/app/views/projects/_project_index.html.haml +++ b/app/views/projects/_project_index.html.haml @@ -35,12 +35,7 @@ .project-main / Icon and Description .project-icon-desc - .project-icon{ class: project.logo.present? ? 'has-logo' : nil } - - if project.logo.present? - = image_tag project.logo.attachment.url(:med), alt: project.name, onerror: "this.style.display='none'; this.nextElementSibling.style.display='flex'; this.parentElement.classList.remove('has-logo');" - %span.project-icon-letter{ style: 'display:none' }= project.name.first.upcase - - else - %span.project-icon-letter= project.name.first.upcase + = project.decorate.icon(:med) .project-description - if project.description && project.description.strip.size > 220 diff --git a/config/locales/explore.en.yml b/config/locales/explore.en.yml index 39fdd6b03..8a980b582 100644 --- a/config/locales/explore.en.yml +++ b/config/locales/explore.en.yml @@ -12,6 +12,7 @@ en: title: 'Orgs by 30 Day Commit Volume' no_results: 'No Results found matching the filter.' filter: 'Filter by: ' + filter_type_label: 'Filter by organization type' org: 'Organization' type: 'Type' size: 'Size' diff --git a/config/shared/sort_options.yml b/config/shared/sort_options.yml index def90df4e..3ad81dbc4 100644 --- a/config/shared/sort_options.yml +++ b/config/shared/sort_options.yml @@ -181,6 +181,35 @@ organizations: default: projects +org_types: + options: + - + - All Organizations + - all_orgs + - + - Commercial + - commercial + - + - Education + - educational + - + - Government + - government + - + - Non-Profit + - non_profit + - + - Large + - large + - + - Medium + - medium + - + - Small + - small + default: + all_orgs + claim_projects_list: options: - diff --git a/test/controllers/explore_controller_test.rb b/test/controllers/explore_controller_test.rb index 4b6c52be3..fc2922499 100644 --- a/test/controllers/explore_controller_test.rb +++ b/test/controllers/explore_controller_test.rb @@ -39,7 +39,7 @@ class ExploreControllerTest < ActionController::TestCase describe 'orgs_by_thirty_day_commit_volume' do it 'should return json of filtered record when filter is all_orgs' do - get :orgs_by_thirty_day_commit_volume, params: { filter: 'all_orgs' }, format: :js, xhr: true + get :orgs_by_thirty_day_commit_volume, params: { org_type: 'all_orgs' }, format: :js, xhr: true assert_response :ok _(assigns(:org_by_30_day_commits)).must_equal [ota5, ota4, ota3, ota2, ota1] @@ -47,14 +47,14 @@ class ExploreControllerTest < ActionController::TestCase it 'should return json of filtered record when filter is government' do OrgThirtyDayActivity.where(id: [ota5.id, ota4.id, ota3.id]).update_all(org_type: 3) - get :orgs_by_thirty_day_commit_volume, params: { filter: 'government' }, format: 'js', xhr: true + get :orgs_by_thirty_day_commit_volume, params: { org_type: 'government' }, format: 'js', xhr: true assert_response :ok _(assigns(:org_by_30_day_commits)).must_equal [ota5, ota4, ota3] end it 'should return json of filtered record when filter is all' do - get :orgs_by_thirty_day_commit_volume, params: { filter: 'all_orgs' }, format: :js, xhr: true + get :orgs_by_thirty_day_commit_volume, params: { org_type: 'all_orgs' }, format: :js, xhr: true assert_response :ok _(assigns(:org_by_30_day_commits)).must_equal [ota5, ota4, ota3, ota2, ota1] diff --git a/test/decorators/icon_test.rb b/test/decorators/icon_test.rb index 59882f2ae..9cf340527 100644 --- a/test/decorators/icon_test.rb +++ b/test/decorators/icon_test.rb @@ -8,17 +8,30 @@ class IconTest < ActiveSupport::TestCase logo = create(:attachment) project = create(:project, logo_id: logo.id) - image = '\"img" - _(Icon.new(project).image).must_equal image + icon = Icon.new(project) + result = icon.image + + # Check that it's wrapped in a div with icon-container and has-logo classes + _(result).must_match(/