diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index daf0402..2742241 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -726,6 +726,12 @@ div[style*="color: red"], box-sizing: border-box; } +.opportunity-form-page .form-group textarea.technology-picker-input { + max-width: none !important; + height: auto !important; + padding: 0.5rem !important; +} + /* Skill Badges */ .skill-badge { display: inline-block; diff --git a/app/controllers/opportunities_controller.rb b/app/controllers/opportunities_controller.rb index 77a9a06..7521d59 100644 --- a/app/controllers/opportunities_controller.rb +++ b/app/controllers/opportunities_controller.rb @@ -37,6 +37,8 @@ def index @opportunities = @opportunities.order("#{sort_column} #{sort_direction}") elsif sort_column == "company" @opportunities = @opportunities.joins(:company).order("companies.name #{sort_direction}") + elsif sort_column == "industry" + @opportunities = @opportunities.joins(:company).order("companies.industry #{sort_direction}") end else @opportunities = @opportunities.order(:application_date).reverse_order diff --git a/app/javascript/controllers/technology_picker_controller.js b/app/javascript/controllers/technology_picker_controller.js new file mode 100644 index 0000000..4a7a126 --- /dev/null +++ b/app/javascript/controllers/technology_picker_controller.js @@ -0,0 +1,42 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = [ "input", "checkbox", "result" ] + + apply() { + const pastedTechnologies = this.inputTarget.value + .split(/[;,\n]+/) + .map((technology) => technology.trim()) + .filter(Boolean) + + const checkboxesByName = new Map( + this.checkboxTargets.map((checkbox) => [ + this.normalize(checkbox.dataset.technologyName), + checkbox + ]) + ) + const unmatched = [] + let selected = 0 + + pastedTechnologies.forEach((technology) => { + const checkbox = checkboxesByName.get(this.normalize(technology)) + + if (checkbox) { + checkbox.checked = true + selected += 1 + } else { + unmatched.push(technology) + } + }) + + const messages = [] + if (selected > 0) messages.push(`${selected} ${selected === 1 ? "technology" : "technologies"} selected`) + if (unmatched.length) messages.push(`Not found: ${unmatched.join(", ")}`) + + this.resultTarget.textContent = messages.join(". ") || "Paste one or more technologies to match the catalog." + } + + normalize(technology) { + return technology.toLowerCase().replace(/\s+/g, " ").trim() + } +} \ No newline at end of file diff --git a/app/models/company.rb b/app/models/company.rb index 0de5dc6..52af848 100644 --- a/app/models/company.rb +++ b/app/models/company.rb @@ -9,7 +9,7 @@ class Company < ApplicationRecord has_many :resource_sheets, dependent: :nullify before_validation :normalize_name - before_save :shorten_urls, :sanitize_size + before_save :sanitize_size # Validations validates :name, presence: true @@ -77,21 +77,6 @@ def normalize_name self.name = name.to_s.squish.presence end - def shorten_urls - if website_changed? && website.present? && !website.include?("is.gd") - shortened = UrlShortenerService.shorten(website) - self.website = shortened if shortened.present? - end - - if linkedin_changed? && linkedin.present? && !linkedin.include?("is.gd") - shortened = UrlShortenerService.shorten(linkedin) - self.linkedin = shortened if shortened.present? - end - rescue StandardError => e - Rails.logger.error("URL shortening failed in Company#shorten_urls: #{e.message}") - # Continue with save even if shortening fails - end - def sanitize_size self.size = size&.gsub(",", "") if size.present? end diff --git a/app/models/technology.rb b/app/models/technology.rb index 6459b9a..31848be 100644 --- a/app/models/technology.rb +++ b/app/models/technology.rb @@ -13,6 +13,9 @@ class Technology < ApplicationRecord "Testing", "DevOps/Infrastructure", "API/Integration", + "AI/LLM", + "Observability", + "Event/Messaging", "Other" ].freeze end diff --git a/app/services/opportunities_csv_exporter.rb b/app/services/opportunities_csv_exporter.rb index c496b18..d0b2762 100644 --- a/app/services/opportunities_csv_exporter.rb +++ b/app/services/opportunities_csv_exporter.rb @@ -17,19 +17,13 @@ def generate csv << headers @opportunities.each do |opportunity| - # Combine structured technologies with other_tech_stack - tech_list = opportunity.technologies.order(:category, :name).pluck(:name).join(", ") - tech_list += ", #{opportunity.other_tech_stack}" if opportunity.other_tech_stack.present? - tech_list = tech_list.presence || opportunity.tech_stack # Fallback to old field if no structured data - csv << [ + opportunity.role_type, opportunity.company.name, - opportunity.company.company_type || "", - opportunity.company.size || "", + opportunity.company.industry, opportunity.position_title, opportunity.application_date, opportunity.status, - tech_list, opportunity.salary_range, opportunity.chatgpt_match, opportunity.jobright_match, @@ -43,13 +37,12 @@ def generate def headers [ + "Role Type", "Company", - "Company Type", - "Company Size", + "Industry", "Position Title", "Application Date", "Status", - "Tech Stack", "Salary Range", "ChatGPT Match", "Jobright Match", diff --git a/app/services/tech_stack_analyzer.rb b/app/services/tech_stack_analyzer.rb index 134d1f5..eb79466 100644 --- a/app/services/tech_stack_analyzer.rb +++ b/app/services/tech_stack_analyzer.rb @@ -39,7 +39,7 @@ def analyze_main_stack_combinations combination_counts = Hash.new(0) # Main framework technologies to track - backend_frameworks = [ "Ruby on Rails", "Python", "Django", "Node.js", "Laravel", "Express" ] + backend_frameworks = [ "Ruby on Rails", "Python", "Django", "Node.js", "Express", "FastAPI" ] frontend_frameworks = [ "React", "Vue", "Angular", "Stimulus", "Hotwire" ] @opportunities.each do |opp| diff --git a/app/views/companies/_form.html.erb b/app/views/companies/_form.html.erb index 533a9a5..239a309 100644 --- a/app/views/companies/_form.html.erb +++ b/app/views/companies/_form.html.erb @@ -60,11 +60,18 @@ <%= form.url_field :linkedin, placeholder: "https://linkedin.com/...", class: "form-input" %> -
Technologies this company uses (from research, website, job postings, etc.)
+ +