diff --git a/.gitignore b/.gitignore index cffa3d5..da6672e 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ pickle-email-*.html # TODO Comment out these rules if you are OK with secrets being uploaded to the repo config/initializers/secret_token.rb config/secrets.yml +config/credentials* config/master.key ## Environment normalisation: diff --git a/Gemfile b/Gemfile index 9fed27e..3e04279 100644 --- a/Gemfile +++ b/Gemfile @@ -33,12 +33,11 @@ gem 'spring', group: :development # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' - gem 'unicorn' gem 'font-awesome-rails' -gem 'owlcarousel-rails' -# gem 'magnific-popup-rails' -# gem 'newrelic_rpm' +gem 'flipper' +gem 'flipper-active_record' +gem 'flipper-ui' gem 'dotenv-rails' gem 'extra_print' gem 'puma' @@ -46,11 +45,9 @@ gem 'haml-rails' gem "recaptcha" gem 'lograge' -# gem 'bootstrap-modal-rails' group :development, :test do gem 'capistrano-rails' - # gem 'debugger' gem 'pry' gem 'rspec-rails' gem 'factory_bot_rails' @@ -60,8 +57,6 @@ group :development, :test do gem 'guard-livereload' gem 'guard-spork' gem 'ruby_gntp' - # gem 'spork', '~> 1.0rc' - # gem 'spork-rails' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 9ea14fb..e15cfb1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -84,8 +84,8 @@ GEM sshkit (>= 1.6.1, != 1.7.0) amazing_print (1.8.1) base64 (0.3.0) - benchmark (0.4.1) - bigdecimal (3.2.3) + benchmark (0.5.0) + bigdecimal (3.3.1) builder (3.3.0) capistrano (3.19.2) airbrussh (>= 1.0.0) @@ -135,7 +135,7 @@ GEM em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) - erb (5.0.2) + erb (5.1.1) erubi (1.13.1) event_stream_parser (1.0.0) eventmachine (1.2.7) @@ -167,6 +167,17 @@ GEM ffi (1.17.2-x86_64-darwin) ffi (1.17.2-x86_64-linux-gnu) ffi (1.17.2-x86_64-linux-musl) + flipper (0.26.0) + concurrent-ruby (< 2) + flipper-active_record (0.26.0) + activerecord (>= 4.2, < 8) + flipper (~> 0.26.0) + flipper-ui (0.26.0) + erubi (>= 1.0.0, < 2.0.0) + flipper (~> 0.26.0) + rack (>= 1.4, < 3) + rack-protection (>= 1.5.3, <= 4.0.0) + sanitize (< 7) font-awesome-rails (4.7.0.9) railties (>= 3.2, < 9.0) formatador (1.1.0) @@ -251,7 +262,7 @@ GEM matrix (0.4.2) method_source (1.1.0) mini_mime (1.1.5) - minitest (5.25.5) + minitest (5.26.0) multi_json (1.15.0) multipart-post (2.4.1) mutex_m (0.3.0) @@ -293,9 +304,8 @@ GEM nenv (~> 0.1) shellany (~> 0.0) ostruct (0.6.1) - owlcarousel-rails (2.2.3.5) pg (1.5.9) - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) pry (0.15.2) @@ -308,14 +318,16 @@ GEM puma (6.5.0) nio4r (~> 2.0) racc (1.8.1) - rack (3.2.1) - rack-session (2.1.1) - base64 (>= 0.1.0) - rack (>= 3.0.0) + rack (2.2.15) + rack-protection (3.0.5) + rack + rack-session (1.0.2) + rack (< 3) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) - rack (>= 3) + rackup (1.0.1) + rack (< 3) + webrick rails (7.1.5.2) actioncable (= 7.1.5.2) actionmailbox (= 7.1.5.2) @@ -393,6 +405,9 @@ GEM marcel (~> 1.0) zeitwerk (~> 2) rubyzip (2.4.1) + sanitize (6.0.2) + crass (~> 1.0.2) + nokogiri (>= 1.12.0) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) @@ -449,6 +464,7 @@ GEM kgio (~> 2.6) raindrops (~> 0.7) uri (1.0.3) + webrick (1.8.2) websocket (1.2.11) websocket-driver (0.8.0) base64 @@ -478,6 +494,9 @@ DEPENDENCIES extra_print factory_bot_rails faker + flipper + flipper-active_record + flipper-ui font-awesome-rails guard-livereload guard-rspec @@ -488,7 +507,6 @@ DEPENDENCIES jquery-rails launchy lograge - owlcarousel-rails pg pry puma diff --git a/app/assets/images/ai3.png b/app/assets/images/ai3.png new file mode 100644 index 0000000..d546b2a Binary files /dev/null and b/app/assets/images/ai3.png differ diff --git a/app/assets/images/ai4.png b/app/assets/images/ai4.png new file mode 100644 index 0000000..31a1793 Binary files /dev/null and b/app/assets/images/ai4.png differ diff --git a/app/assets/images/project_screenshots/grab-bandalou.png b/app/assets/images/project_screenshots/grab-bandalou.png new file mode 100644 index 0000000..9842d85 Binary files /dev/null and b/app/assets/images/project_screenshots/grab-bandalou.png differ diff --git a/app/assets/images/project_screenshots/grab-boot-badger.png b/app/assets/images/project_screenshots/grab-boot-badger.png index 45d33f3..2109a06 100644 Binary files a/app/assets/images/project_screenshots/grab-boot-badger.png and b/app/assets/images/project_screenshots/grab-boot-badger.png differ diff --git a/app/assets/images/project_screenshots/grab-bootcoder.png b/app/assets/images/project_screenshots/grab-bootcoder.png index b18021b..f130f91 100644 Binary files a/app/assets/images/project_screenshots/grab-bootcoder.png and b/app/assets/images/project_screenshots/grab-bootcoder.png differ diff --git a/app/assets/images/project_screenshots/grab-dbcode.jpg b/app/assets/images/project_screenshots/grab-dbcode.jpg deleted file mode 100644 index 885c8c9..0000000 Binary files a/app/assets/images/project_screenshots/grab-dbcode.jpg and /dev/null differ diff --git a/app/assets/images/project_screenshots/grab-dbcode.png b/app/assets/images/project_screenshots/grab-dbcode.png new file mode 100644 index 0000000..3dcd5a6 Binary files /dev/null and b/app/assets/images/project_screenshots/grab-dbcode.png differ diff --git a/app/assets/images/project_screenshots/grab-doppel.png b/app/assets/images/project_screenshots/grab-doppel.png index 8a51cdb..772ad81 100644 Binary files a/app/assets/images/project_screenshots/grab-doppel.png and b/app/assets/images/project_screenshots/grab-doppel.png differ diff --git a/app/assets/images/project_screenshots/grab-extra_print.png b/app/assets/images/project_screenshots/grab-extra_print.png index 707ca75..6414adb 100644 Binary files a/app/assets/images/project_screenshots/grab-extra_print.png and b/app/assets/images/project_screenshots/grab-extra_print.png differ diff --git a/app/assets/images/project_screenshots/grab-homebase.png b/app/assets/images/project_screenshots/grab-homebase.png index 2fbe16b..f134b61 100644 Binary files a/app/assets/images/project_screenshots/grab-homebase.png and b/app/assets/images/project_screenshots/grab-homebase.png differ diff --git a/app/assets/images/project_screenshots/grab-infowall.png b/app/assets/images/project_screenshots/grab-infowall.png index 0c25a1d..8050725 100644 Binary files a/app/assets/images/project_screenshots/grab-infowall.png and b/app/assets/images/project_screenshots/grab-infowall.png differ diff --git a/app/assets/images/project_screenshots/grab-metagame.jpg b/app/assets/images/project_screenshots/grab-metagame.jpg deleted file mode 100644 index 90754e9..0000000 Binary files a/app/assets/images/project_screenshots/grab-metagame.jpg and /dev/null differ diff --git a/app/assets/images/project_screenshots/grab-metagame.png b/app/assets/images/project_screenshots/grab-metagame.png new file mode 100644 index 0000000..d078457 Binary files /dev/null and b/app/assets/images/project_screenshots/grab-metagame.png differ diff --git a/app/assets/images/project_screenshots/grab-quest.png b/app/assets/images/project_screenshots/grab-quest.png index a8e7a1f..1a4c75f 100644 Binary files a/app/assets/images/project_screenshots/grab-quest.png and b/app/assets/images/project_screenshots/grab-quest.png differ diff --git a/app/assets/images/project_screenshots/grab-rivals.png b/app/assets/images/project_screenshots/grab-rivals.png new file mode 100644 index 0000000..3c2c614 Binary files /dev/null and b/app/assets/images/project_screenshots/grab-rivals.png differ diff --git a/app/assets/images/project_screenshots/grab-schema-designer.png b/app/assets/images/project_screenshots/grab-schema-designer.png index c7aa4cf..2f97fc2 100644 Binary files a/app/assets/images/project_screenshots/grab-schema-designer.png and b/app/assets/images/project_screenshots/grab-schema-designer.png differ diff --git a/app/assets/images/project_screenshots/grab-techlx.png b/app/assets/images/project_screenshots/grab-techlx.png index 08e9b1c..ef5de5e 100644 Binary files a/app/assets/images/project_screenshots/grab-techlx.png and b/app/assets/images/project_screenshots/grab-techlx.png differ diff --git a/app/assets/javascripts/grayscale.js b/app/assets/javascripts/grayscale.js index e5e67fa..3bc63f0 100755 --- a/app/assets/javascripts/grayscale.js +++ b/app/assets/javascripts/grayscale.js @@ -17,157 +17,10 @@ $(window).scroll(function() { $(function() { $('a.page-scroll').bind('click', function(event) { var $anchor = $(this); + var ref = $($anchor.attr('ref')); $('html, body').stop().animate({ - scrollTop: $($anchor.attr('href')).offset().top + scrollTop: ref.offset().top }, 1500, 'easeInOutExpo'); event.preventDefault(); }); }); - -// Google Maps Scripts -// When the window has finished loading create our google map below -// google.maps.event.addDomListener(window, 'load', init); - -function init() { - // Basic options for a simple Google Map - // For more options see: https://developers.google.com/maps/documentation/javascript/reference#MapOptions - var mapOptions = { - // How zoomed in you want the map to start at (always required) - zoom: 15, - - // The latitude and longitude to center the map (always required) - center: new google.maps.LatLng(40.6700, -73.9400), // New York - - // Disables the default Google Maps UI components - disableDefaultUI: true, - scrollwheel: false, - draggable: false, - - // How you would like to style the map. - // This is where you would paste any style found on Snazzy Maps. - styles: [{ - "featureType": "water", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 17 - }] - }, { - "featureType": "landscape", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 20 - }] - }, { - "featureType": "road.highway", - "elementType": "geometry.fill", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 17 - }] - }, { - "featureType": "road.highway", - "elementType": "geometry.stroke", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 29 - }, { - "weight": 0.2 - }] - }, { - "featureType": "road.arterial", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 18 - }] - }, { - "featureType": "road.local", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 16 - }] - }, { - "featureType": "poi", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 21 - }] - }, { - "elementType": "labels.text.stroke", - "stylers": [{ - "visibility": "on" - }, { - "color": "#000000" - }, { - "lightness": 16 - }] - }, { - "elementType": "labels.text.fill", - "stylers": [{ - "saturation": 36 - }, { - "color": "#000000" - }, { - "lightness": 40 - }] - }, { - "elementType": "labels.icon", - "stylers": [{ - "visibility": "off" - }] - }, { - "featureType": "transit", - "elementType": "geometry", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 19 - }] - }, { - "featureType": "administrative", - "elementType": "geometry.fill", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 20 - }] - }, { - "featureType": "administrative", - "elementType": "geometry.stroke", - "stylers": [{ - "color": "#000000" - }, { - "lightness": 17 - }, { - "weight": 1.2 - }] - }] - }; - - // Get the HTML DOM element that will contain your map - // We are using a div with id="map" seen below in the
- var mapElement = document.getElementById('map'); - - // Create the Google Map using out element and options defined above - var map = new google.maps.Map(mapElement, mapOptions); - - // Custom Map Marker Icon - Customize the map-marker.png file to customize your icon - var image = 'img/map-marker.png'; - var myLatLng = new google.maps.LatLng(40.6700, -73.9400); - var beachMarker = new google.maps.Marker({ - position: myLatLng, - map: map, - icon: image - }); -} diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index b44e34e..295cc4f 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -2,49 +2,64 @@ $(document).ready(function(){ console.log("Document Ready"); $('.popup').on('click', function(event){ - console.log("CLICKDD ME!"); + console.log("CLICKED Popup!"); event.preventDefault(); }) - // $(function () { - // console.log('init tooltip'); - // $('[data-toggle="tooltip"]').tooltip(); - // }) + $('.navbar-nav>li>a').on('click', function(){ + $('.navbar-collapse').collapse('hide'); + }); - $('.tech-item').hover( - function(){ - var techId = this.id.split('-').at(-1); - var techTip = $(`#tech-tip-${techId}`); - var techImg = $(`#tech-img-${techId}`); + if (window.innerWidth < 768) { + $('.navbar-brand').text('HTC'); + } + + $('#debug-toggle').on('click', function(event){ + console.log('TOGGLE CSS BUGGER'); + event.preventDefault(); + var viewPortWidth = window.innerWidth + " Pixels"; + var docBody = $('#debuggable'); + + $('#view-port-width-bugger').text(viewPortWidth) + $(docBody).toggleClass('debug'); + $('#bs-bugger').slideToggle(500, 'swing'); + }) - techImg.addClass('tech-img-shrunken') - techTip.addClass('tech-tip-enlarged'); - }, + if (window.innerWidth > 960) { + $('#tech-img-00').addClass('pulse'); - function(){ - var techId = this.id.split('-').at(-1); - var techTip = $(`#tech-tip-${techId}`); - var techImg = $(`#tech-img-${techId}`); + $('.tech-item').hover( + function(){ + var techId = this.id.split('-').at(-1); + var techTip = $(`#tech-tip-${techId}`); + var techImg = $(`#tech-img-${techId}`); - techTip.removeClass('tech-tip-enlarged'); - techImg.removeClass('tech-img-shrunken') - } - ) + $('.pulse').removeClass('pulse') + techTip.toggleClass('tech-tip-enlarged'); + techImg.toggleClass('tech-img-shrunken') + }, + + function(){ + var techId = this.id.split('-').at(-1); + var techTip = $(`#tech-tip-${techId}`); + var techImg = $(`#tech-img-${techId}`); + + techTip.toggleClass('tech-tip-enlarged'); + techImg.toggleClass('tech-img-shrunken') + } + ) + } if ( $(window).width() < 1023 ) { - // $('.bg-space').removeClass('bg-space black') $('.bg-space').addClass('gray') $('.box-team').removeClass('wow') $('.service-box').removeClass('wow') $('.mobile-js').removeClass('max-400') $('.mobile-js').removeClass('max-500') - // $('.mobile-js').addClass('max-90P') - // $('.mobile-js').addClass('max-75P') $('.mobile-js').addClass('pad-5') $('.mobile-js').addClass('white') $('.gallery').removeClass('bg-site') - } }); diff --git a/app/assets/javascripts/paralax_projects.js b/app/assets/javascripts/paralax_projects.js new file mode 100644 index 0000000..b5db298 --- /dev/null +++ b/app/assets/javascripts/paralax_projects.js @@ -0,0 +1,97 @@ +// BootCoder Projects — Parallax rows + 3D tilt (Bootstrap 5 friendly) +(function () { + function init() { + var root = document.querySelector('#projects.bc-projects'); + if (!root) return; + + var rows = Array.prototype.slice.call(root.querySelectorAll('.bc-parallax-row')); + var cards = Array.prototype.slice.call(root.querySelectorAll('.project-3d')); + var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; + + // --- Row parallax on scroll (translateX based on section position) + if (!reduceMotion) { + var ticking = false; + + function updateParallax() { + var vh = window.innerHeight; + rows.forEach(function (row) { + var r = row.getBoundingClientRect(); + if (r.bottom < 0 || r.top > vh) return; // off-screen + var center = (r.top + r.height / 2) - vh / 2; + var speed = parseFloat(row.dataset.speed || 0.2); + row.style.transform = 'translateX(' + (-center * speed * 0.05) + 'px)'; + }); + ticking = false; + } + + function onScroll() { + if (!ticking) { + requestAnimationFrame(updateParallax); + ticking = true; + } + } + + window.addEventListener('scroll', onScroll, { passive: true }); + window.addEventListener('resize', updateParallax, { passive: true }); + updateParallax(); + } + + // --- Card tilt (pointer-driven 3D) + if (!reduceMotion) { + cards.forEach(function (card) { + var rect; + + function onMove(e) { + rect = rect || card.getBoundingClientRect(); + var x = (e.clientX - rect.left) / rect.width; // 0..1 + var y = (e.clientY - rect.top) / rect.height; // 0..1 + var rx = (0.5 - y) * 8; // deg + var ry = (x - 0.5) * 12; // deg + card.style.transform = 'perspective(900px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg)'; + } + + function reset() { + rect = null; + card.style.transform = 'perspective(900px) rotateX(0) rotateY(0)'; + } + + card.addEventListener('mousemove', onMove); + card.addEventListener('mouseleave', reset); + // Disable tilt on touch (to avoid sticky states) + card.addEventListener('touchstart', function(){ card.style.transform = 'none'; }, { passive: true }); + }); + } + } + + // Bootstrap 5 and Turbolinks/Hotwire friendly init + if (document.readyState === 'loading') { + console.log('DOM Loading') + document.addEventListener('DOMContentLoaded', init); + } else { + console.log('DOM Loaded') + init(); + } + document.addEventListener('turbo:load', init); +// Delegated modal opener that works on BS 5.0+ (no getOrCreateInstance required) + document.addEventListener('click', function (e) { + console.log("Project Click Event"); + const trigger = e.target.closest('[data-bs-toggle="modal"][data-bs-target]'); + if (!trigger) return; + + const targetSel = trigger.getAttribute('data-bs-target'); + const modalEl = document.querySelector(targetSel); + if (!modalEl) return; + + // Prevent the #hash jump on + e.preventDefault(); + + // Bootstrap version-agnostic instance getter/creator + const Modal = bootstrap.Modal; + let instance = (Modal.getInstance && Modal.getInstance(modalEl)) || null; + if (!instance) { + instance = new Modal(modalEl); // works in 5.0+ + } + if (!modalEl.classList.contains('show')) instance.show(); + }, { passive: false }); + +})(); diff --git a/app/assets/javascripts/techs.js.coffee b/app/assets/javascripts/techs.js.coffee deleted file mode 100644 index 24f83d1..0000000 --- a/app/assets/javascripts/techs.js.coffee +++ /dev/null @@ -1,3 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/grayscale.css b/app/assets/stylesheets/grayscale.css index 75c60de..db90ea9 100755 --- a/app/assets/stylesheets/grayscale.css +++ b/app/assets/stylesheets/grayscale.css @@ -29,12 +29,12 @@ h6 { font-weight: 700; letter-spacing: 1px; } - +/* p { margin: 0 0 25px; font-size: 18px; line-height: 1.5; -} +} */ @media(min-width:767px) { p { @@ -214,7 +214,7 @@ a:focus { -moz-animation-timing-function: linear; } -@-webkit-keyframes pulse { +@-webkit-keyframes pulse { 0 { -webkit-transform: scale(1); transform: scale(1); @@ -231,7 +231,7 @@ a:focus { } } -@-moz-keyframes pulse { +@-moz-keyframes pulse { 0 { -moz-transform: scale(1); transform: scale(1); @@ -360,4 +360,4 @@ img::-moz-selection { body { webkit-tap-highlight-color: rgba(255,255,255,.2); -} \ No newline at end of file +} diff --git a/app/assets/stylesheets/paralax_projects.css b/app/assets/stylesheets/paralax_projects.css new file mode 100644 index 0000000..4eb928f --- /dev/null +++ b/app/assets/stylesheets/paralax_projects.css @@ -0,0 +1,52 @@ +/* ---- BootCoder Projects (namespaced) ---- */ +.bc-projects { + /* section spacing */ + &.py-6 { padding-top: 4.5rem; padding-bottom: 4.5rem; } + + .bc-parallax-row { will-change: transform; } + + + .parallax-row{ + overflow: auto; /* Ensures content remains scrollable */ + scrollbar-width: none; /* For Firefox */ + -ms-overflow-style: none; /* For IE and Edge */ + } + + .parallax-row::-webkit-scrollbar { + display: none; /* For WebKit browsers (Chrome, Safari, Opera) */ + } + + /* horizontal scroll niceties on mobile */ + .row.flex-nowrap { + scroll-snap-type: x mandatory; + } + .row.flex-nowrap > [class*="col-"] { + scroll-snap-align: start; + } + + /* 3D card tilt */ + .project-3d { + transform-style: preserve-3d; + transform: perspective(900px) rotateX(0) rotateY(0); + transition: transform .2s ease, box-shadow .2s ease; + will-change: transform; + } + .project-3d .card-img-top { + object-fit: cover; + } + @media (hover:hover) { + .project-3d:hover { + box-shadow: 0 .75rem 2rem rgba(0,0,0,.15); + } + } + + /* respect reduced motion */ + @media (prefers-reduced-motion: reduce) { + .bc-parallax-row { transform: none !important; } + .project-3d { transition: none !important; transform: none !important; } + } +} + +/* Stop parallax/tilt while a modal is open so stacking/positioning is sane */ +/* body.modal-open .bc-projects .bc-parallax-row { transform: none !important; } +body.modal-open .bc-projects .project-3d { transform: none !important; } */ diff --git a/app/assets/stylesheets/style.css b/app/assets/stylesheets/style.css index 23e47be..24282f7 100755 --- a/app/assets/stylesheets/style.css +++ b/app/assets/stylesheets/style.css @@ -1,6 +1,11 @@ /* * { outline: 1px solid blue; } */ + +/* Styles applied when the checkbox is checked */ +.debug * { + outline: 1px solid blue; +} /* ================================ Imports ================================= */ @@ -83,6 +88,10 @@ button.navbar-toggle { color: #fff; } +.responsive-navbar { + width: 100%; +} + .btn-theme,.btn-theme:hover,.btn-theme:focus{ color: #fff; } @@ -102,7 +111,16 @@ button.navbar-toggle { #bs-bugger { - position: sticky + z-index: 1000; + display: none; + width: 50%; + position: absolute; + left: 25%; +} + +.magenta-text-box { + background-color: magenta; + color: white; } /* ================================ @@ -124,6 +142,15 @@ margin, padding .mar-top40 { margin-top: 40px; } +.mar-top-5p { + margin-top: 5%; +} +.mar-top-10p { + margin-top: 10%; +} +.mar-top-50p { + margin-top: 50%; +} @@ -312,6 +339,7 @@ Intro font-weight: 300; } + /* ================================ About ================================= */ @@ -588,6 +616,10 @@ x:-o-prefocus, .lb-overlay img { height: 100%; } +#comic-text { + font-family: "Comic Sans MS", "Comic Sans", cursive; +} + /* ================================ Techs @@ -627,7 +659,7 @@ Techs color: black; } -.tech-tag-line hr { +.red-breaker-bar { border: none; height: 2px; margin: 0; @@ -681,6 +713,33 @@ Techs .white h2 { color: white; } + + .box-team { + padding-bottom: 5%; + } +} + +@media (min-width: 768px) { + .pulse { + border-radius: 50%; + box-shadow: 0 0 0 0 rgb(1, 234, 255); + transform: scale(1); + animation: pulse 2s infinite; + } +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(1, 145, 171, 0.7); + } + + 70% { + box-shadow: 0 0 0 15px rgba(222, 84, 72, 0); + } + + 100% { + box-shadow: 0 0 0 0 rgba(222, 84, 72, 0); + } } /* ================================ @@ -690,6 +749,23 @@ Resume / Recaptcha outline: 1px solid gray; } +.verification-container .resume-label { + padding-top: 5%; +} +.verification-container .resume-submit { + margin-bottom: 5%; +} + +.btn-bootcoder { + color: white; + background-color: #219ab3 +} + +.btn-bootcoder:hover { + color: greenyellow; + background-color: #219ab3 +} + .recaptcha-form-height { height: 110px; } @@ -712,7 +788,6 @@ Projects display: block; max-width: 100%; } */ -} /* ================================ @@ -723,6 +798,10 @@ Contact /* ================================ Bottom widget ================================= */ +.social-network { + margin-bottom: 5%; +} + ul.social-network { list-style: none; } @@ -731,13 +810,7 @@ ul.social-network li { display: inline; } -ul.social-network li a:hover { - color: #01a0b5; -} -ul.social-network li a:hover span .fa-circle { - color: #757272; -} /* ================================ Footer @@ -748,6 +821,28 @@ footer { padding: 50px 0 30px; } +.footer-link-text { + transition: color 1.5s ease; +} + +.footer-link-icon { + opacity: 0; + transition: opacity 1.5s ease; +} + +.footer-link:hover .footer-link-icon { + opacity: 1; +} + +.footer-link:hover .footer-link-text { + color: white; +} + +@media (max-width: 768px) { + .footer-link-icon { + opacity: 1; + } +} /* ================================ more media queries @@ -822,17 +917,17 @@ more media queries font:normal 20pt Arial; color: white; text-shadow: 0 1px 0 #ccc, - 0 2px 0 gray, - 0 3px 0 #000000, - 0 4px 0 #000000, - 0 5px 0 #000000, - 0 6px 1px rgba(0,0,0,.1), - 0 0 5px rgba(0,0,0,.1), - 0 1px 3px rgba(0,0,0,.3), - 0 3px 5px rgba(0,0,0,.2), - 0 5px 10px rgba(0,0,0,.25), - 0 10px 10px rgba(0,0,0,.2), - 0 20px 20px rgba(0,0,0,.15); + 0 2px 0 gray, + 0 3px 0 #000000, + 0 4px 0 #000000, + 0 5px 0 #000000, + 0 6px 1px rgba(0,0,0,.1), + 0 0 5px rgba(0,0,0,.1), + 0 1px 3px rgba(0,0,0,.3), + 0 3px 5px rgba(0,0,0,.2), + 0 5px 10px rgba(0,0,0,.25), + 0 10px 10px rgba(0,0,0,.2), + 0 20px 20px rgba(0,0,0,.15); } .glow2 { @@ -871,16 +966,25 @@ more media queries } .curved-6 { - width: 50%; + width: 85%; outline: 2px solid gray; + overflow: hidden; -moz-border-radius: 1em 4em 1em 4em; border-radius: 1em 4em 1em 4em; } +.curved-6:hover { + outline: 4px solid cyan; +} + .bg-black-border { border: 2px solid black; } +.bg-white-border { + border: 2px solid white; +} + .white { color: white; } @@ -923,6 +1027,10 @@ more media queries padding-top: 2%; } +.pad-3P-down { + padding-top: 3%; +} + .pad-5P-down { padding-top: 5%; } @@ -959,7 +1067,7 @@ more media queries padding-top: 20px; } -.margin-5p { +.margin-1p { margin: 1%; } @@ -1048,7 +1156,41 @@ more media queries float: left; } - +.burning-text { + /* color: magenta; */ + /* background-image: linear-gradient(90deg, rgba(255, 255, 255, 1) 0%, rgba(212, 147, 0, 1) 55%, rgba(255, 59, 0, 1) 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + color: transparent !important; */ + color: #ff6b00; + text-shadow: + 0 0 5px yellow, + 0 0 10px orange, + 0 0 20px red; + animation: flicker 1.5s infinite alternate; /* Adjust duration and timing */ +} + +@keyframes flicker { + 0% { + text-shadow: + 0 0 5px yellow, + 0 0 10px orange, + 0 0 20px red; + } + 50% { + text-shadow: + 0 0 8px yellow, + 0 0 15px orange, + 0 0 25px red; + } + 100% { + text-shadow: + 0 0 6px yellow, + 0 0 12px orange, + 0 0 22px red; + } +} .window { display: none; diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 7e03fe3..246bcdd 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -5,7 +5,8 @@ class UsersController < ApplicationController # GET /users.json def index @users = User.all - @techs = Tech.all.order(:title) + @techs = Tech.where('rating > ?', 0).order(:title) + @projects = Project.all end # GET /users/1 @@ -25,19 +26,28 @@ def resume @user.source = 'resume' unless @user.persisted? # https://github.com/ambethia/recaptcha - if verify_recaptcha(model: @user) && @user.save - Rails.logger.info("Resume_Request Success: Email: #{email_param}") - require 'open-uri' - URI.open(I18n.t('resume_link')) do |pdf| - tmpfile = Tempfile.new("tmp.pdf") - File.open(tmpfile.path, 'wb') { |f| f.write(pdf.read) } - send_file(tmpfile.path, :filename => "non_standard_resume_hunter_chapman.pdf") + begin + unless Flipper.enabled?(:resume_download) + Rails.logger.error("Resume_Request Disabled: Email: #{email_param} User: #{@user.errors.full_messages}") + return render(file: "public/412.html", layout: false) end - redirect_to(root_path) - else - Rails.logger.error("Resume_Request Failed: Email: #{email_param} User: #{@user.errors.full_messages}") - render(file: "public/412.html", layout: false) + if verify_recaptcha(model: @user) && @user.save + require 'open-uri' + URI.open(I18n.t('resume_link')) do |pdf| + @tmpfile = Tempfile.new("tmp.pdf") + @file = File.open(@tmpfile.path, 'wb') { |f| f.write(pdf.read) } + send_file(@tmpfile.path, :filename => "non_standard_resume_hunter_chapman.pdf") + end + + Rails.logger.info("Resume_Request Success: Email: #{email_param} File KB: #{@file.kilobytes}") + redirect_to(root_path) + else + Rails.logger.error("Resume_Request Failed: Email: #{email_param} User: #{@user.errors.full_messages}") + render(file: "public/412.html", layout: false) + end + rescue => e + Rails.logger.error("Resume_Request Rescued: Email: #{email_param} User: #{@user.errors.full_messages} File KB: #{@file.kilobytes} Error #{e.message}") end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 6ac3deb..081f58d 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -2,5 +2,12 @@ module ProjectsHelper def bc_projects @projects = Project.all end -end + def rows_by_tens(records) + records + .sort_by { |r| r.display_order.to_i } # ensure display order + .group_by { |r| r.display_order.to_i / 10 } # 0..9 => 0, 10..19 => 1, etc. + .sort_by { |bucket, _| bucket } # row 0, row 1, ... + .map { |_, items| items } # keep just the items per row + end +end diff --git a/app/models/project.rb b/app/models/project.rb index c37ee6e..f13e42b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,9 +1,11 @@ class Project < ApplicationRecord validates :project_description, presence: true, uniqueness: true - validates :project_url, presence: true, uniqueness: true - validates :project_source_url, presence: true, uniqueness: true + validates :project_url, presence: true + validates :project_source_url, presence: true validates :project_img, presence: true, uniqueness: true validates :project_feature_lang, presence: true validates :project_state, presence: true + has_many :project_techs + has_many :techs, through: :project_techs end diff --git a/app/models/project_tech.rb b/app/models/project_tech.rb new file mode 100644 index 0000000..40ecb37 --- /dev/null +++ b/app/models/project_tech.rb @@ -0,0 +1,4 @@ +class ProjectTech < ApplicationRecord + belongs_to :project + belongs_to :tech +end diff --git a/app/models/tech.rb b/app/models/tech.rb index d3d9bee..e806632 100644 --- a/app/models/tech.rb +++ b/app/models/tech.rb @@ -1,4 +1,41 @@ class Tech < ApplicationRecord self.table_name = 'techs' validates :rating, :inclusion => { :in => 0..5 } + + has_many :project_techs + has_many :projects, through: :project_techs + + def self.smart_find(identifier) + target = nil + return target if identifier.blank? || identifier.nil? + return target unless identifier.is_a?(String) + + target ||= find_by('lower(title) = ?', identifier.downcase) + target ||= find_by('lower(aka) = ?', identifier.downcase) + target ||= nil + target + end + + def self.smart_find_or_create(identifier) + target = nil + # First Case -- Hash Args + if identifier.is_a?(Hash) + identifier.each do |k,v| + target = smart_find(v) + end + end + + # Second Case -- String Args if no target present + target = smart_find(identifier) if identifier.is_a?(String) && target.nil? + return target if target + + + target = if identifier.respond_to?(:keys) + create(identifier.merge(rating: 0)) + elsif identifier.is_a?(String) + create(title: identifier, rating: 0) + end + + target + end end diff --git a/app/views/layouts/_bs_bugger.html.haml b/app/views/layouts/_bs_bugger.html.haml index b980b89..155332b 100644 --- a/app/views/layouts/_bs_bugger.html.haml +++ b/app/views/layouts/_bs_bugger.html.haml @@ -1,8 +1,8 @@ -.container - .row - .col-lg-12#bs-bugger.alert.alert-info - .d-none.d-xl-block.font-weight-bold X-LARGE (XL) - .d-none.d-lg-block.d-xl-none.font-weight-bold LARGE (LG) - .d-none.d-md-block.d-lg-none.font-weight-bold MEDIUM (M) - .d-none.d-sm-block.d-md-none.font-weight-bold SMALL (SM) - .d-block.d-sm-none.alert.font-weight-bold X-SMALL (Defaut) +#bs-bugger.hero-slide + .magenta-text-box + #view-port-width-bugger + .d-none.d-xl-block X-LARGE (XL) + .d-none.d-lg-block.d-xl-none LARGE (LG) + .d-none.d-md-block.d-lg-none MEDIUM (M) + .d-none.d-sm-block.d-md-none SMALL (SM) + .d-block.d-sm-none X-SMALL (Defaut) diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index b1451e2..0645133 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -1,42 +1,73 @@ -<%# render partial: 'layouts/bs_bugger' %> -