mirror of
https://github.com/ryankazokas/turbovault-app.git
synced 2026-04-16 22:12:53 +00:00
385 lines
12 KiB
Ruby
385 lines
12 KiB
Ruby
class GamesController < ApplicationController
|
|
before_action :require_authentication
|
|
before_action :set_game, only: [ :show, :edit, :update, :destroy ]
|
|
|
|
def index
|
|
@games = current_user.games.includes(:platform, :genres, :collections)
|
|
|
|
# Filtering
|
|
@games = @games.by_platform(params[:platform_id]) if params[:platform_id].present?
|
|
@games = @games.by_genre(params[:genre_id]) if params[:genre_id].present?
|
|
@games = @games.where(format: params[:format]) if params[:format].present?
|
|
@games = @games.where(completion_status: params[:completion_status]) if params[:completion_status].present?
|
|
@games = @games.search(params[:search]) if params[:search].present?
|
|
|
|
# Sorting
|
|
@games = case params[:sort]
|
|
when "alphabetical" then @games.alphabetical
|
|
when "recent" then @games.recent
|
|
when "rated" then @games.rated
|
|
else @games.alphabetical
|
|
end
|
|
|
|
@games = @games.page(params[:page]).per(25)
|
|
|
|
# For filters
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
end
|
|
|
|
def show
|
|
end
|
|
|
|
def new
|
|
@game = current_user.games.build
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
end
|
|
|
|
def create
|
|
@game = current_user.games.build(game_params)
|
|
|
|
if @game.save
|
|
# If game was created with IGDB ID, sync the metadata
|
|
sync_igdb_metadata_after_create if @game.igdb_id.present?
|
|
|
|
redirect_to @game, notice: "Game was successfully created."
|
|
else
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
render :new, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def edit
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
end
|
|
|
|
def update
|
|
if @game.update(game_params)
|
|
redirect_to @game, notice: "Game was successfully updated."
|
|
else
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
render :edit, status: :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
def destroy
|
|
@game.destroy
|
|
redirect_to games_path, notice: "Game was successfully deleted."
|
|
end
|
|
|
|
def import
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
end
|
|
|
|
def bulk_edit
|
|
@game_ids = params[:game_ids] || []
|
|
|
|
if @game_ids.empty?
|
|
redirect_to games_path, alert: "Please select at least one game to edit."
|
|
return
|
|
end
|
|
|
|
@games = current_user.games.where(id: @game_ids)
|
|
@platforms = Platform.order(:name)
|
|
@genres = Genre.order(:name)
|
|
@collections = current_user.collections.order(:name)
|
|
end
|
|
|
|
def bulk_update
|
|
@game_ids = params[:game_ids] || []
|
|
|
|
if @game_ids.empty?
|
|
redirect_to games_path, alert: "No games selected."
|
|
return
|
|
end
|
|
|
|
@games = current_user.games.where(id: @game_ids)
|
|
updated_count = 0
|
|
|
|
@games.each do |game|
|
|
updates = {}
|
|
|
|
# Only update fields that have values provided
|
|
updates[:completion_status] = params[:completion_status] if params[:completion_status].present?
|
|
updates[:location] = params[:location] if params[:location].present?
|
|
updates[:condition] = params[:condition] if params[:condition].present?
|
|
|
|
# Handle collection assignment
|
|
if params[:collection_action].present?
|
|
case params[:collection_action]
|
|
when "add"
|
|
if params[:collection_ids].present?
|
|
game.collection_ids = (game.collection_ids + params[:collection_ids].map(&:to_i)).uniq
|
|
end
|
|
when "remove"
|
|
if params[:collection_ids].present?
|
|
game.collection_ids = game.collection_ids - params[:collection_ids].map(&:to_i)
|
|
end
|
|
when "replace"
|
|
game.collection_ids = params[:collection_ids] if params[:collection_ids].present?
|
|
end
|
|
end
|
|
|
|
# Handle genre assignment
|
|
if params[:genre_action].present?
|
|
case params[:genre_action]
|
|
when "add"
|
|
if params[:genre_ids].present?
|
|
game.genre_ids = (game.genre_ids + params[:genre_ids].map(&:to_i)).uniq
|
|
end
|
|
when "remove"
|
|
if params[:genre_ids].present?
|
|
game.genre_ids = game.genre_ids - params[:genre_ids].map(&:to_i)
|
|
end
|
|
when "replace"
|
|
game.genre_ids = params[:genre_ids] if params[:genre_ids].present?
|
|
end
|
|
end
|
|
|
|
if game.update(updates)
|
|
updated_count += 1
|
|
end
|
|
end
|
|
|
|
redirect_to games_path, notice: "Successfully updated #{updated_count} game(s)."
|
|
end
|
|
|
|
def bulk_create
|
|
require "csv"
|
|
|
|
results = { created: 0, failed: 0, errors: [] }
|
|
|
|
if params[:csv_file].present?
|
|
csv_text = params[:csv_file].read
|
|
csv = CSV.parse(csv_text, headers: true)
|
|
|
|
csv.each_with_index do |row, index|
|
|
platform = Platform.find_by(name: row["platform"]) || Platform.find_by(abbreviation: row["platform"])
|
|
|
|
unless platform
|
|
results[:failed] += 1
|
|
results[:errors] << "Row #{index + 2}: Platform '#{row['platform']}' not found"
|
|
next
|
|
end
|
|
|
|
game = current_user.games.build(
|
|
title: row["title"],
|
|
platform: platform,
|
|
format: row["format"]&.downcase || "physical",
|
|
date_added: row["date_added"] || Date.current,
|
|
completion_status: row["completion_status"]&.downcase,
|
|
user_rating: row["user_rating"],
|
|
condition: row["condition"]&.downcase,
|
|
price_paid: row["price_paid"],
|
|
location: row["location"],
|
|
digital_store: row["digital_store"],
|
|
notes: row["notes"]
|
|
)
|
|
|
|
# Handle genres
|
|
if row["genres"].present?
|
|
genre_names = row["genres"].split("|").map(&:strip)
|
|
genres = Genre.where(name: genre_names)
|
|
game.genres = genres
|
|
end
|
|
|
|
if game.save
|
|
results[:created] += 1
|
|
else
|
|
results[:failed] += 1
|
|
results[:errors] << "Row #{index + 2}: #{game.errors.full_messages.join(", ")}"
|
|
end
|
|
end
|
|
|
|
flash[:notice] = "Created #{results[:created]} games. Failed: #{results[:failed]}"
|
|
flash[:alert] = results[:errors].join("<br>").html_safe if results[:errors].any?
|
|
redirect_to games_path
|
|
else
|
|
redirect_to import_games_path, alert: "Please select a CSV file to upload."
|
|
end
|
|
end
|
|
|
|
def search_igdb
|
|
query = params[:q].to_s.strip
|
|
platform_id = params[:platform_id]
|
|
|
|
if query.length < 2
|
|
render json: []
|
|
return
|
|
end
|
|
|
|
begin
|
|
service = IgdbService.new
|
|
platform = platform_id.present? ? Platform.find_by(id: platform_id) : nil
|
|
|
|
# Search IGDB (limit to 10 results for autocomplete)
|
|
results = service.search_game(query, platform, 10)
|
|
|
|
# Format results for autocomplete
|
|
formatted_results = results.map do |result|
|
|
# Map IGDB genres to our local genre IDs
|
|
genre_ids = map_igdb_genres_to_ids(result[:genres] || [])
|
|
|
|
{
|
|
igdb_id: result[:igdb_id],
|
|
name: result[:name],
|
|
platform: result[:platform_name],
|
|
year: result[:release_year],
|
|
cover_url: result[:cover_url],
|
|
summary: result[:summary],
|
|
genres: result[:genres],
|
|
genre_ids: genre_ids,
|
|
confidence: result[:confidence_score]
|
|
}
|
|
end
|
|
|
|
render json: formatted_results
|
|
rescue => e
|
|
Rails.logger.error("IGDB search error: #{e.message}")
|
|
render json: [], status: :internal_server_error
|
|
end
|
|
end
|
|
|
|
def search_locations
|
|
query = params[:q].to_s.strip
|
|
|
|
# Get unique locations from user's games that match the query
|
|
locations = current_user.games
|
|
.where.not(location: [nil, ""])
|
|
.where("location ILIKE ?", "%#{query}%")
|
|
.select(:location)
|
|
.distinct
|
|
.order(:location)
|
|
.limit(10)
|
|
.pluck(:location)
|
|
|
|
render json: locations
|
|
end
|
|
|
|
def search_stores
|
|
query = params[:q].to_s.strip
|
|
|
|
# Get unique digital stores from user's games that match the query
|
|
stores = current_user.games
|
|
.where.not(digital_store: [nil, ""])
|
|
.where("digital_store ILIKE ?", "%#{query}%")
|
|
.select(:digital_store)
|
|
.distinct
|
|
.order(:digital_store)
|
|
.limit(10)
|
|
.pluck(:digital_store)
|
|
|
|
render json: stores
|
|
end
|
|
|
|
private
|
|
|
|
def set_game
|
|
@game = current_user.games.includes(:igdb_game).find(params[:id])
|
|
end
|
|
|
|
def sync_igdb_metadata_after_create
|
|
# Fetch full game data from IGDB
|
|
service = IgdbService.new
|
|
igdb_data = service.get_game(@game.igdb_id)
|
|
|
|
return unless igdb_data
|
|
|
|
# Create or update IgdbGame record
|
|
igdb_game = IgdbGame.find_or_create_by!(igdb_id: @game.igdb_id) do |ig|
|
|
ig.name = igdb_data["name"]
|
|
ig.slug = igdb_data["slug"]
|
|
ig.summary = igdb_data["summary"]
|
|
ig.first_release_date = igdb_data["first_release_date"] ? Time.at(igdb_data["first_release_date"]).to_date : nil
|
|
|
|
# Extract cover URL
|
|
cover_url = igdb_data.dig("cover", "url")&.split("/")&.last&.sub(".jpg", "")
|
|
ig.cover_url = cover_url
|
|
|
|
ig.last_synced_at = Time.current
|
|
end
|
|
|
|
igdb_game.increment_match_count!
|
|
|
|
# Update game with IGDB metadata
|
|
@game.update(
|
|
igdb_matched_at: Time.current,
|
|
igdb_match_status: "matched",
|
|
igdb_match_confidence: 100.0
|
|
)
|
|
|
|
# Map and assign genres
|
|
if igdb_data["genres"].present?
|
|
genre_names = igdb_data["genres"].map { |g| g["name"] }
|
|
assign_igdb_genres_to_game(genre_names)
|
|
end
|
|
rescue => e
|
|
Rails.logger.error("Failed to sync IGDB metadata: #{e.message}")
|
|
end
|
|
|
|
def map_igdb_genres_to_ids(genre_names)
|
|
return [] if genre_names.blank?
|
|
|
|
# Genre mapping (same as in IgdbMatchSuggestion)
|
|
genre_mappings = {
|
|
"Role-playing (RPG)" => "RPG",
|
|
"Fighting" => "Fighting",
|
|
"Shooter" => "Shooter",
|
|
"Platform" => "Platformer",
|
|
"Puzzle" => "Puzzle",
|
|
"Racing" => "Racing",
|
|
"Real Time Strategy (RTS)" => "Strategy",
|
|
"Simulator" => "Simulation",
|
|
"Sport" => "Sports",
|
|
"Strategy" => "Strategy",
|
|
"Adventure" => "Adventure",
|
|
"Indie" => "Indie",
|
|
"Arcade" => "Arcade",
|
|
"Hack and slash/Beat 'em up" => "Action"
|
|
}
|
|
|
|
genre_ids = []
|
|
genre_names.each do |igdb_genre_name|
|
|
# Try exact match
|
|
local_genre = Genre.find_by("LOWER(name) = ?", igdb_genre_name.downcase)
|
|
|
|
# Try mapped name
|
|
if local_genre.nil? && genre_mappings[igdb_genre_name]
|
|
mapped_name = genre_mappings[igdb_genre_name]
|
|
local_genre = Genre.find_by("LOWER(name) = ?", mapped_name.downcase)
|
|
end
|
|
|
|
genre_ids << local_genre.id if local_genre
|
|
end
|
|
|
|
genre_ids
|
|
end
|
|
|
|
def assign_igdb_genres_to_game(genre_names)
|
|
genre_ids = map_igdb_genres_to_ids(genre_names)
|
|
|
|
genre_ids.each do |genre_id|
|
|
genre = Genre.find(genre_id)
|
|
@game.genres << genre unless @game.genres.include?(genre)
|
|
end
|
|
end
|
|
|
|
def game_params
|
|
params.require(:game).permit(
|
|
:title, :platform_id, :format, :date_added, :completion_status,
|
|
:user_rating, :notes, :condition, :price_paid, :location,
|
|
:digital_store, :custom_entry, :igdb_id, genre_ids: [], collection_ids: []
|
|
)
|
|
end
|
|
end
|