diff --git a/app/controllers/api/v1/briefings_controller.rb b/app/controllers/api/v1/briefings_controller.rb
index 70fe429..e12f106 100644
--- a/app/controllers/api/v1/briefings_controller.rb
+++ b/app/controllers/api/v1/briefings_controller.rb
@@ -1,8 +1,6 @@
module Api
module V1
- class BriefingsController < ApplicationController
- skip_before_action :verify_authenticity_token
-
+ class BriefingsController < BaseController
RECIPIENT = "austindanielfrench@gmail.com".freeze
def send_briefing
diff --git a/app/controllers/concerns/http_basic_authenticable.rb b/app/controllers/concerns/http_basic_authenticable.rb
deleted file mode 100644
index f573e4f..0000000
--- a/app/controllers/concerns/http_basic_authenticable.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module HttpBasicAuthenticable
- extend ActiveSupport::Concern
-
- included do
- before_action :authenticate_admin!, if: :admin_authentication_required?
- end
-
- private
-
- def authenticate_admin!
- authenticate_or_request_with_http_basic("Admin Area") do |username, password|
- expected_username = ENV.fetch("ADMIN_USER_NAME", "admin")
- expected_password = ENV.fetch("ADMIN_PASSWORD", "changeme123")
-
- ActiveSupport::SecurityUtils.secure_compare(username, expected_username) &&
- ActiveSupport::SecurityUtils.secure_compare(password, expected_password)
- end
- end
-
- def admin_authentication_required?
- true
- end
-end
diff --git a/app/helpers/invoices_helper.rb b/app/helpers/invoices_helper.rb
index ebb749d..061b28d 100644
--- a/app/helpers/invoices_helper.rb
+++ b/app/helpers/invoices_helper.rb
@@ -1,15 +1,5 @@
module InvoicesHelper
- def format_money(cents)
- return "$0.00" if cents.nil? || cents == 0
- dollars = cents.to_f / 100
- negative = dollars < 0
- dollars = dollars.abs
- formatted = format("%.2f", dollars)
- parts = formatted.split(".")
- parts[0] = parts[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
- result = "$#{parts.join('.')}"
- negative ? "-#{result}" : result
- end
+ include MoneyFormattable
def invoice_status_badge(status)
colors = {
diff --git a/app/javascript/components/ScrollbarStyles.jsx b/app/javascript/components/ScrollbarStyles.jsx
deleted file mode 100644
index 733c913..0000000
--- a/app/javascript/components/ScrollbarStyles.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React, { useEffect } from 'react';
-
-const ScrollbarStyles = () => {
- useEffect(() => {
- // Create and append a style element to ensure scrollbar styles are applied with high priority
- const styleEl = document.createElement('style');
- styleEl.textContent = `
- /* Custom Timeline Scrollbar (Applied via JS for higher priority) */
- ::-webkit-scrollbar {
- width: 8px !important;
- }
-
- ::-webkit-scrollbar-track {
- background-color: #f1f1f1 !important;
- background-image: linear-gradient(
- to bottom,
- transparent 0px,
- transparent 98px,
- #aaa 98px,
- #aaa 102px,
- transparent 102px
- ) !important;
- background-repeat: repeat-y !important;
- background-position: center !important;
- background-size: 2px auto !important;
- }
-
- ::-webkit-scrollbar-thumb {
- background: #888 !important;
- border-radius: 4px !important;
- }
-
- ::-webkit-scrollbar-thumb:hover {
- background: #555 !important;
- }
-
- /* Firefox */
- * {
- scrollbar-width: thin !important;
- scrollbar-color: #888 #f1f1f1 !important;
- }
- `;
- document.head.appendChild(styleEl);
-
- return () => {
- document.head.removeChild(styleEl);
- };
- }, []);
-
- return null; // This component doesn't render anything
-};
-
-export default ScrollbarStyles;
\ No newline at end of file
diff --git a/app/javascript/components/markdown/DebugPanel.jsx b/app/javascript/components/markdown/DebugPanel.jsx
deleted file mode 100644
index aa02761..0000000
--- a/app/javascript/components/markdown/DebugPanel.jsx
+++ /dev/null
@@ -1,26 +0,0 @@
-// File: app/javascript/components/markdown/DebugPanel.jsx
-import React from 'react';
-
-const DebugPanel = ({ slug, useElementId, content }) => {
- if (!content) return null;
-
- return (
-
-
Debug Info:
-
Slug: {slug || 'No slug provided'}
-
Using element ID: {useElementId || 'None'}
-
Content length: {content ? content.length : 0} characters
-
Content preview: {content ? content.substring(0, 50) + '...' : 'None'}
-
- );
-};
-
-export default DebugPanel;
\ No newline at end of file
diff --git a/app/models/concerns/money_formattable.rb b/app/models/concerns/money_formattable.rb
new file mode 100644
index 0000000..32815ba
--- /dev/null
+++ b/app/models/concerns/money_formattable.rb
@@ -0,0 +1,15 @@
+module MoneyFormattable
+ extend ActiveSupport::Concern
+
+ def format_money(cents)
+ return "$0.00" if cents.nil? || cents == 0
+ dollars = cents.to_f / 100
+ negative = dollars < 0
+ dollars = dollars.abs
+ formatted = format("%.2f", dollars)
+ parts = formatted.split(".")
+ parts[0] = parts[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
+ result = "$#{parts.join('.')}"
+ negative ? "-#{result}" : result
+ end
+end
diff --git a/app/models/invoice.rb b/app/models/invoice.rb
index 7854240..fee1df9 100644
--- a/app/models/invoice.rb
+++ b/app/models/invoice.rb
@@ -1,4 +1,6 @@
class Invoice < ApplicationRecord
+ include MoneyFormattable
+
belongs_to :client
has_many :line_items, class_name: "InvoiceLineItem", dependent: :destroy
@@ -65,15 +67,4 @@ def calculate_totals
self.tax_cents = (subtotal_cents * (tax_rate || 0) / 100).round
self.total_cents = subtotal_cents + tax_cents
end
-
- def format_money(cents)
- dollars = cents.to_f / 100
- "$#{number_with_delimiter(format('%.2f', dollars))}"
- end
-
- def number_with_delimiter(number)
- parts = number.to_s.split(".")
- parts[0] = parts[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
- parts.join(".")
- end
end
diff --git a/app/services/invoice_pdf_service.rb b/app/services/invoice_pdf_service.rb
index 9fb932c..e497fcd 100644
--- a/app/services/invoice_pdf_service.rb
+++ b/app/services/invoice_pdf_service.rb
@@ -1,4 +1,6 @@
class InvoicePdfService
+ include MoneyFormattable
+
ACCENT_COLOR = "1DB954"
def initialize(invoice)
@@ -198,13 +200,4 @@ def draw_footer(pdf)
end
pdf.fill_color "000000"
end
-
- def format_money(cents)
- return "$0.00" if cents.nil? || cents == 0
- dollars = cents.to_f / 100
- formatted = format("%.2f", dollars)
- parts = formatted.split(".")
- parts[0] = parts[0].gsub(/(\d)(?=(\d{3})+(?!\d))/, '\\1,')
- "$#{parts.join('.')}"
- end
end
diff --git a/app/views/pwa/manifest.json.erb b/app/views/pwa/manifest.json.erb
deleted file mode 100644
index 0beb359..0000000
--- a/app/views/pwa/manifest.json.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "name": "Austn",
- "icons": [
- {
- "src": "/icon.png",
- "type": "image/png",
- "sizes": "512x512"
- },
- {
- "src": "/icon.png",
- "type": "image/png",
- "sizes": "512x512",
- "purpose": "maskable"
- }
- ],
- "start_url": "/",
- "display": "standalone",
- "scope": "/",
- "description": "Austn.",
- "theme_color": "red",
- "background_color": "red"
-}
diff --git a/app/views/pwa/service-worker.js b/app/views/pwa/service-worker.js
deleted file mode 100644
index b3a13fb..0000000
--- a/app/views/pwa/service-worker.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// Add a service worker for processing Web Push notifications:
-//
-// self.addEventListener("push", async (event) => {
-// const { title, options } = await event.data.json()
-// event.waitUntil(self.registration.showNotification(title, options))
-// })
-//
-// self.addEventListener("notificationclick", function(event) {
-// event.notification.close()
-// event.waitUntil(
-// clients.matchAll({ type: "window" }).then((clientList) => {
-// for (let i = 0; i < clientList.length; i++) {
-// let client = clientList[i]
-// let clientPath = (new URL(client.url)).pathname
-//
-// if (clientPath == event.notification.data.path && "focus" in client) {
-// return client.focus()
-// }
-// }
-//
-// if (clients.openWindow) {
-// return clients.openWindow(event.notification.data.path)
-// }
-// })
-// )
-// })
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
deleted file mode 100644
index b3076b3..0000000
--- a/config/initializers/content_security_policy.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Define an application-wide content security policy.
-# See the Securing Rails Applications Guide for more information:
-# https://guides.rubyonrails.org/security.html#content-security-policy-header
-
-# Rails.application.configure do
-# config.content_security_policy do |policy|
-# policy.default_src :self, :https
-# policy.font_src :self, :https, :data
-# policy.img_src :self, :https, :data
-# policy.object_src :none
-# policy.script_src :self, :https
-# policy.style_src :self, :https
-# # Specify URI for violation reports
-# # policy.report_uri "/csp-violation-report-endpoint"
-# end
-#
-# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
-# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
-# config.content_security_policy_nonce_directives = %w(script-src style-src)
-#
-# # Report violations without enforcing the policy.
-# # config.content_security_policy_report_only = true
-# end
diff --git a/config/initializers/react_server_rendering.rb b/config/initializers/react_server_rendering.rb
deleted file mode 100644
index f9247bb..0000000
--- a/config/initializers/react_server_rendering.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-# To render React components in production, precompile the server rendering manifest:
-Rails.application.config.assets.precompile += [ "server_rendering.js" ]
diff --git a/config/routes.rb b/config/routes.rb
index 7b7f0d0..fbe41e7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -57,8 +57,6 @@
# Claude Corner
get "/claude", to: "claude_corner#index"
- # Movement demo
- get "/movement-demo", to: "movement_demo#index"
# Pitch checker
get "/pitch", to: "pitch#index"
diff --git a/lib/tasks/blog.rake b/lib/tasks/blog.rake
deleted file mode 100644
index 347f422..0000000
--- a/lib/tasks/blog.rake
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace :blog do
- desc "Import markdown files from Obsidian public notes directory"
- task import_obsidian_notes: :environment do
- ImportObsidianNotesJob.perform_now
- end
-end
diff --git a/lib/tasks/convert_images.rake b/lib/tasks/convert_images.rake
deleted file mode 100644
index 1789c28..0000000
--- a/lib/tasks/convert_images.rake
+++ /dev/null
@@ -1,153 +0,0 @@
-namespace :images do
- desc "Convert game textures to WebP format for better performance"
- task convert_to_webp: :environment do
- require "fileutils"
-
- # Check if cwebp command is available
- unless system("which cwebp > /dev/null 2>&1")
- puts "Error: cwebp utility not found. Please install it first:"
- puts " macOS: brew install webp"
- puts " Ubuntu/Debian: sudo apt-get install webp"
- puts " Windows: Download from https://developers.google.com/speed/webp/download"
- exit 1
- end
-
- # Define directories to process
- dirs_to_process = [
- Rails.root.join("public", "assets", "textures")
- ]
-
- total_converted = 0
- total_savings = 0
-
- dirs_to_process.each do |dir|
- next unless Dir.exist?(dir)
-
- # Get all image files recursively
- images = Dir.glob("#{dir}/**/*.{jpg,jpeg,png}")
-
- images.each do |image_path|
- original_size = File.size(image_path)
- output_path = "#{image_path.chomp(File.extname(image_path))}.webp"
-
- # Skip if WebP version already exists and is newer
- if File.exist?(output_path) && File.mtime(output_path) >= File.mtime(image_path)
- puts "Skipping #{image_path} (WebP already exists and is up-to-date)"
- next
- end
-
- # Determine quality based on file type
- quality = image_path.end_with?(".png") ? 90 : 85
-
- # Convert image to WebP
- success = system("cwebp -q #{quality} \"#{image_path}\" -o \"#{output_path}\"")
-
- if success
- webp_size = File.size(output_path)
- savings = ((original_size - webp_size) / original_size.to_f * 100).round(2)
- total_savings += original_size - webp_size
-
- puts "Converted #{image_path} to WebP, saved #{savings}% (#{original_size} → #{webp_size} bytes)"
- total_converted += 1
- else
- puts "Failed to convert #{image_path}"
- end
- end
- end
-
- if total_converted > 0
- total_savings_mb = (total_savings / 1024.0 / 1024.0).round(2)
- puts "\nConverted #{total_converted} images, total savings: #{total_savings_mb} MB"
- else
- puts "\nNo images were converted."
- end
- end
-
- desc "Resize and optimize all images"
- task optimize: :environment do
- require "fileutils"
-
- # Check for required tools
- %w[convert identify].each do |cmd|
- unless system("which #{cmd} > /dev/null 2>&1")
- puts "Error: #{cmd} command not found. Please install ImageMagick:"
- puts " macOS: brew install imagemagick"
- puts " Ubuntu/Debian: sudo apt-get install imagemagick"
- exit 1
- end
- end
-
- # Define size limits for different image types
- MAX_GAME_TEXTURE_SIZE = 1024 # Maximum texture dimension for game
- MAX_THUMBNAIL_SIZE = 480 # Maximum thumbnail dimension
- MAX_PROFILE_PIC_SIZE = 256 # Maximum profile picture dimension
-
- # Define directories to process
- dirs_to_process = [
- Rails.root.join("public", "assets", "textures")
- ]
-
- total_processed = 0
- total_savings = 0
-
- dirs_to_process.each do |dir|
- next unless Dir.exist?(dir)
-
- # Get all image files recursively
- images = Dir.glob("#{dir}/**/*.{jpg,jpeg,png}")
-
- images.each do |image_path|
- original_size = File.size(image_path)
-
- # Skip small files (less than 10KB)
- next if original_size < 10 * 1024
-
- # Get image dimensions
- dimensions = `identify -format "%wx%h" "#{image_path}"`.strip.split("x").map(&:to_i)
- width, height = dimensions
-
- # Determine max size based on image type/location
- max_size = MAX_GAME_TEXTURE_SIZE
-
- # Calculate new dimensions (only if image is larger than max size)
- if width > max_size || height > max_size
- if width > height
- new_width = max_size
- new_height = (height.to_f / width * max_size).to_i
- else
- new_height = max_size
- new_width = (width.to_f / height * max_size).to_i
- end
-
- # Create a temporary file path
- temp_path = "#{image_path}.tmp"
-
- # Resize and optimize the image
- success = system("convert \"#{image_path}\" -resize #{new_width}x#{new_height} -strip -quality 85 \"#{temp_path}\"")
-
- if success
- # Replace the original with the optimized version
- FileUtils.mv(temp_path, image_path)
-
- new_size = File.size(image_path)
- savings = ((original_size - new_size) / original_size.to_f * 100).round(2)
- total_savings += original_size - new_size
-
- puts "Resized #{image_path} from #{width}x#{height} to #{new_width}x#{new_height}, saved #{savings}% (#{original_size} → #{new_size} bytes)"
- total_processed += 1
- else
- puts "Failed to optimize #{image_path}"
- FileUtils.rm(temp_path) if File.exist?(temp_path)
- end
- end
- end
- end
-
- if total_processed > 0
- total_savings_mb = (total_savings / 1024.0 / 1024.0).round(2)
- puts "\nOptimized #{total_processed} images, total savings: #{total_savings_mb} MB"
- else
- puts "\nNo images were optimized."
- end
- end
-end
diff --git a/test/controllers/movement_demo_controller_test.rb b/test/controllers/movement_demo_controller_test.rb
deleted file mode 100644
index f7230c5..0000000
--- a/test/controllers/movement_demo_controller_test.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-require "test_helper"
-
-class MovementDemoControllerTest < ActionDispatch::IntegrationTest
- # test "the truth" do
- # assert true
- # end
-end