6 Commits

10 changed files with 38 additions and 23 deletions

View File

@@ -50,9 +50,10 @@ class Vehicle < ActiveRecord::Base
write_attribute(:vin, val) write_attribute(:vin, val)
end end
# search for a vin # search for a vehicle by vin, make, model, or year
def self.search(search) def self.search(query)
where("vin LIKE ?", "%#{search}%") q = sanitize_sql_like(query)
where("vin LIKE ? OR make LIKE ? OR model LIKE ? OR year LIKE ?", "%#{q}%", "%#{q}%", "%#{q}%", "%#{q}%")
end end
# decodes a vin and updates self # decodes a vin and updates self

View File

@@ -1,5 +1,5 @@
<h4><%=t(:field_vehicles)%>:</h4> <h4><%=t(:field_vehicles)%>:</h4>
<%= render partial: 'vehicles/list', locals: { vehicles: customer.vehicles.paginate(page: params[:page]) } %> <%= render partial: 'vehicles/list', locals: { vehicles: customer.vehicles.paginate(page: params[:page]), show_customer: false, show_checkbox: false } %>
<div style="float: right;"> <div style="float: right;">
<%= button_to t(:button_new_vehicle), new_customer_vehicle_path(customer), method: :get %> <%= button_to t(:button_new_vehicle), new_customer_vehicle_path(customer), method: :get %>
</div> </div>

View File

@@ -6,7 +6,7 @@
<div class="vehicle_vin attribute"> <div class="vehicle_vin attribute">
<div class="label"><%=t(:field_vin)%>:</div> <div class="label"><%=t(:field_vin)%>:</div>
<div class="value" id="vin"> <div class="value" id="vin">
<a href="#" id="copyLink" onclick="handleCopy(event)"><%=split_vin[0] if split_vin%><b><%=split_vin[1] if split_vin%></b></a> <div id="copyLink" onclick="handleCopy(event)"><%=split_vin[0] if split_vin%><b><%=split_vin[1] if split_vin%></b></div>
</div> </div>
</div> </div>

View File

@@ -18,8 +18,8 @@
<tr> <tr>
<th><%= t(:field_vin) %></th> <th><%= t(:field_vin) %></th>
<td id="vin"> <td>
<a href="#" onclick="handleCopy(event)"><%= @vin[0] if @vin %><b><%=@vin[1] if @vin%></b></a> <div onclick="handleCopy(event)"><%= @vin[0] if @vin %><b><%=@vin[1] if @vin%></b></div>
</td> </td>
</tr> </tr>

View File

@@ -5,14 +5,23 @@
<div class="container"> <div class="container">
<%= check_box_tag "vehicle_ids[]", vehicle.id, false, onchange: "updateLink()", data: { url: vehicle_path(vehicle).html_safe, text: vehicle.to_s }, class: "appointment checkbox" %> <% if show_checkbox %>
<%= check_box_tag "vehicle_ids[]", vehicle.id, false, onchange: "updateLink()", data: { url: vehicle_path(vehicle).html_safe, text: vehicle.to_s }, class: "appointment checkbox" %>
<% else %>
<div class='checkbox'>
</div>
<% end %>
<div class='label-main'> <div class='label-main'>
<%= link_to vehicle.to_s, vehicle_path(vehicle) %> <%= link_to vehicle.to_s, vehicle_path(vehicle) %>
</div> </div>
<div class="label-sub"> <div class="label-sub">
<%= vehicle.vin.scan(/.{1,9}/)[0] if vehicle.vin %><b><%=vehicle.vin.scan(/.{1,9}/)[1] if vehicle.vin%></b> <div onclick="handleCopy(event)"><%= vehicle.vin.scan(/.{1,9}/)[0] if vehicle.vin %><b><%=vehicle.vin.scan(/.{1,9}/)[1] if vehicle.vin%></b></div>
<% if show_customer %>
<br/>
<%= vehicle.customer %>
<% end %>
</div> </div>
</div> </div>

View File

@@ -1,4 +1,4 @@
<%= form_tag(vehicles_path, method: "get", id: "search-form") do %> <%= form_tag(vehicles_path, method: "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: t(:label_search_vin), autocomplete: "off" %> <%= text_field_tag :search, params[:search], placeholder: t(:label_search), autocomplete: "off" %>
<%= submit_tag t(:label_search) %> <%= submit_tag t(:label_search) %>
<% end %> <% end %>

View File

@@ -1,4 +1,4 @@
<h2><%=t(:label_customer_vehicles)%> <span style="float:right"> <%= render partial: 'vehicles/search' %> </span> </h2> <h2><%=t(:label_customer_vehicles)%> <span style="float:right"> <%= render partial: 'vehicles/search' %> </span> </h2>
<br/> <br/>
<%= render partial: 'vehicles/list', locals: {vehicles: @vehicles} %> <%= render partial: 'vehicles/list', locals: {vehicles: @vehicles, show_customer: true, show_checkbox: false} %>

View File

@@ -1,22 +1,27 @@
async function handleCopy(event) { async function handleCopy(event) {
console.log("Copy link clicked"); console.log("Copy link clicked");
// 1. Prevent the link from actually navigating
event.preventDefault(); let text;
let link;
// 2. Grab the text from our span // Grab the text from our clicked link
const text = document.getElementById('vin').innerText; if(event.target.tagName.toLowerCase() === 'b'){
text = event.target.parentElement.innerText;
link = event.target.parentElement;
} else {
text = event.target.innerText;
link = event.target;
}
try { try {
// 3. Write to clipboard // Write to clipboard
await navigator.clipboard.writeText(text); await navigator.clipboard.writeText(text);
// 4. Update the UI to show it worked // Update the UI to show it worked
const link = event.target;
const originalText = link.innerText; const originalText = link.innerText;
link.innerHTML = "<b>Copied!</b>"; link.innerHTML = "<b>Copied!</b>";
link.style.color = "#4CAF50"; // Turn green link.style.color = "#4CAF50"; // Turn green
// 5. Reset after 2 seconds // Reset after 2 seconds
setTimeout(() => { setTimeout(() => {
// Check if the text is long enough to prevent errors // Check if the text is long enough to prevent errors
if (originalText.length >= 8) { if (originalText.length >= 8) {

View File

@@ -28,7 +28,7 @@ en:
label_model: "Model" label_model: "Model"
label_new_vehicle: "New Customer Vehicle" label_new_vehicle: "New Customer Vehicle"
label_no_vehicles: "There are no vehicles containing the term(s)" label_no_vehicles: "There are no vehicles containing the term(s)"
label_search_vin: "Search Vehicles by VIN" label_search: "Search Vehicles"
label_year: "Year" label_year: "Year"
no_customer: "Customer no longer exists" no_customer: "Customer no longer exists"
notice_vehicle_created: "Vehicle was successfully created." notice_vehicle_created: "Vehicle was successfully created."

View File

@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo_vehicles do
name 'Redmine QBO Vehicles plugin' name 'Redmine QBO Vehicles plugin'
author 'Rick Barrette' author 'Rick Barrette'
description 'This is a plugin for Redmine to intergrate with the redmine_qbo plugin to provide vehicle data tracking' description 'This is a plugin for Redmine to intergrate with the redmine_qbo plugin to provide vehicle data tracking'
version '2026.2.3' version '2026.2.5'
url 'https://github.com/rickbarrette/redmine_qbo_vehicles' url 'https://github.com/rickbarrette/redmine_qbo_vehicles'
author_url 'https://barrettefabrication.com' author_url 'https://barrettefabrication.com'
requires_redmine version_or_higher: '6.1.0' requires_redmine version_or_higher: '6.1.0'