diff --git a/.gitignore b/.gitignore
index c264e05..2491303 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,4 @@ node_modules
/app/assets/builds/*
!/app/assets/builds/.keep
node_modules/
+yarn.lock
diff --git a/App.css b/App.css
new file mode 100644
index 0000000..e08a42f
--- /dev/null
+++ b/App.css
@@ -0,0 +1,335 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0.5rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #cc0000aa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #336791aa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+/* Terminal and code styling */
+.terminal-output::-webkit-scrollbar {
+ width: 4px;
+}
+
+.terminal-output::-webkit-scrollbar-track {
+ background: #1a1a1a;
+}
+
+.terminal-output::-webkit-scrollbar-thumb {
+ background: #444;
+ border-radius: 2px;
+}
+
+.ascii-art {
+ font-family: monospace;
+ white-space: pre;
+ font-size: 0.7rem;
+ line-height: 1;
+}
+
+/* Matrix animation - kept for reference but not used in new design */
+.matrix-container {
+ display: none;
+}
+
+/* Analog noise overlay */
+.noise-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.1'/%3E%3C/svg%3E");
+ pointer-events: none;
+ opacity: 0.06;
+ z-index: 10;
+}
+
+/* Audio waveform divider */
+.waveform-divider {
+ height: 20px;
+ background: url("data:image/svg+xml,%3Csvg width='100%25' height='20' viewBox='0 0 1200 20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10 Q 25 5, 50 10 T 100 10 T 150 10 T 200 10 T 250 10 T 300 10 T 350 10 T 400 10 T 450 10 T 500 10 T 550 10 T 600 10 T 650 10 T 700 10 T 750 10 T 800 10 T 850 10 T 900 10 T 950 10 T 1000 10 T 1050 10 T 1100 10 T 1150 10 T 1200 10' stroke='%23555' fill='none' stroke-width='1'/%3E%3C/svg%3E");
+ margin: 1.5rem 0;
+ opacity: 0.5;
+}
+
+/* Ruby on Rails and Postgres inspired colors */
+.rails-red {
+ color: #CC0000 !important;
+}
+
+.postgres-blue {
+ color: #336791 !important;
+}
+
+.card-rails {
+ border-color: rgba(204, 0, 0, 0.3) !important;
+}
+
+.card-postgres {
+ border-color: rgba(51, 103, 145, 0.3) !important;
+}
+
+/* Mechanical key styling */
+.mech-key {
+ font-family: 'JetBrains Mono', monospace;
+ padding: 3px 8px;
+ background: #222;
+ border: 1px solid #444;
+ border-radius: 3px;
+ box-shadow: 0 2px 0 #111;
+ color: #eee;
+ display: inline-block;
+ margin: 0 2px;
+}
+
+/* Various animation styles */
+/* Analog VU meter animation */
+@keyframes vu-meter {
+ 0% { width: 20%; }
+ 20% { width: 60%; }
+ 40% { width: 40%; }
+ 60% { width: 80%; }
+ 80% { width: 30%; }
+ 100% { width: 50%; }
+}
+
+.vu-meter {
+ height: 4px;
+ background: linear-gradient(90deg, #39ff14, #f3d611, #f33611);
+ animation: vu-meter 4s ease-in-out infinite;
+ border-radius: 2px;
+ margin: 8px 0;
+}
+
+/* Loading bar animation */
+@keyframes loading-bar {
+ 0% { width: 0%; background-position: 0% 50%; }
+ 50% { width: 100%; background-position: 100% 50%; }
+ 100% { width: 0%; background-position: 0% 50%; }
+}
+
+.loading-bar {
+ height: 3px;
+ background: linear-gradient(90deg, #39ff14, #bc13fe, #336791, #CC0000);
+ background-size: 300% 300%;
+ animation: loading-bar 8s ease infinite;
+ border-radius: 2px;
+ margin: 8px 0;
+}
+
+/* Sine wave animation */
+@keyframes sine-wave {
+ 0% {
+ clip-path: path('M0,10 Q5,5 10,10 T20,10 T30,10 T40,10 T50,10 T60,10 T70,10 T80,10 T90,10 T100,10');
+ }
+ 25% {
+ clip-path: path('M0,10 Q5,15 10,10 T20,10 T30,10 T40,10 T50,10 T60,10 T70,10 T80,10 T90,10 T100,10');
+ }
+ 50% {
+ clip-path: path('M0,10 Q5,10 10,5 T20,10 T30,5 T40,10 T50,15 T60,10 T70,5 T80,10 T90,15 T100,10');
+ }
+ 75% {
+ clip-path: path('M0,10 Q5,5 10,15 T20,5 T30,15 T40,5 T50,15 T60,5 T70,15 T80,5 T90,15 T100,10');
+ }
+ 100% {
+ clip-path: path('M0,10 Q5,5 10,10 T20,10 T30,10 T40,10 T50,10 T60,10 T70,10 T80,10 T90,10 T100,10');
+ }
+}
+
+.sine-wave {
+ background: linear-gradient(90deg, #39ff14, #cc0000);
+ animation: sine-wave 4s ease-in-out infinite;
+ border-radius: 2px;
+ margin: 8px 0;
+}
+
+/* Pulse dot animation */
+@keyframes pulse-dot {
+ 0% { transform: scale(0.8); opacity: 0.5; }
+ 50% { transform: scale(1.2); opacity: 1; }
+ 100% { transform: scale(0.8); opacity: 0.5; }
+}
+
+.pulse-dot {
+ width: 6px;
+ height: 6px;
+ background: #39ff14;
+ border-radius: 50%;
+ margin: 8px auto;
+ animation: pulse-dot 2s ease-in-out infinite;
+}
+
+/* Glitchy text effect */
+@keyframes glitch-text {
+ 0% { text-shadow: -1px -1px 0 rgba(255,0,0,0.3), 1px 1px 0 rgba(0,255,255,0.3); }
+ 25% { text-shadow: 1px -1px 0 rgba(255,0,0,0.3), -1px 1px 0 rgba(0,255,255,0.3); }
+ 50% { text-shadow: -1px 1px 0 rgba(255,0,0,0.3), 1px -1px 0 rgba(0,255,255,0.3); }
+ 75% { text-shadow: 1px 1px 0 rgba(255,0,0,0.3), -1px -1px 0 rgba(0,255,255,0.3); }
+ 100% { text-shadow: -1px -1px 0 rgba(255,0,0,0.3), 1px 1px 0 rgba(0,255,255,0.3); }
+}
+
+.text-glitch:hover {
+ animation: glitch-text 0.3s ease-in-out infinite;
+}
+
+/* Server status pulse animation */
+@keyframes server-pulse {
+ 0%, 100% { opacity: 1; box-shadow: 0 0 5px currentColor; }
+ 50% { opacity: 0.6; box-shadow: 0 0 10px currentColor; }
+}
+
+/* Server status indicators */
+.server-status {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+ margin-right: 6px;
+ animation: server-pulse 2s ease-in-out infinite;
+}
+
+.server-online {
+ background-color: #0f9d58;
+}
+
+.server-issue {
+ background-color: #f4b400;
+}
+
+.server-offline {
+ background-color: #db4437;
+}
+
+/* Terminal status indicators that match Tailwind classes */
+.status-indicator {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ display: inline-block;
+ margin-right: 6px;
+ animation: server-pulse 2s ease-in-out infinite;
+}
+
+/* Terminal window macOS style */
+.terminal-header {
+ height: 24px;
+ background-color: #2a2a2a;
+ border-bottom: 1px solid #444;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+ display: flex;
+ align-items: center;
+ padding: 0 8px;
+}
+
+.terminal-dot {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ margin-right: 6px;
+}
+
+.terminal-dot-red {
+ background-color: #ff5f56;
+}
+
+.terminal-dot-yellow {
+ background-color: #ffbd2e;
+}
+
+.terminal-dot-green {
+ background-color: #27c93f;
+}
+
+.terminal-title {
+ color: #aaa;
+ font-size: 12px;
+ margin-left: 4px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+/* MacOS window styling for bento boxes */
+.macos-window {
+ border-radius: 6px;
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
+ overflow: hidden;
+ backdrop-filter: blur(8px);
+}
+
+.macos-header {
+ height: 26px;
+ background: linear-gradient(to bottom, #3a3a3a, #2a2a2a);
+ border-bottom: 1px solid #444;
+ display: flex;
+ align-items: center;
+ padding: 0 10px;
+}
+
+.macos-buttons {
+ display: flex;
+ gap: 6px;
+}
+
+.macos-button {
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+}
+
+.macos-button-close {
+ background-color: #ff5f56;
+ border: 1px solid #e0443e;
+}
+
+.macos-button-minimize {
+ background-color: #ffbd2e;
+ border: 1px solid #dea123;
+}
+
+.macos-button-expand {
+ background-color: #27c93f;
+ border: 1px solid #1aab29;
+}
+
+.macos-content {
+ padding: 10px;
+ background-color: rgba(30, 30, 30, 0.8);
+}
+
+/* Blink animation (caret) */
+@keyframes blink {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0; }
+}
+
+.animate-blink {
+ animation: blink 1s step-end infinite;
+}
\ No newline at end of file
diff --git a/App.tsx b/App.tsx
new file mode 100644
index 0000000..ab5ebf1
--- /dev/null
+++ b/App.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+import "./App.css";
+import "./index.css";
+import { BrowserRouter, Routes, Route } from "react-router-dom";
+import Navigation from "./components/Navigation";
+import Index from "./pages/Index";
+import Blog from "./pages/Blog";
+import Work from "./pages/Work";
+import Contact from "./pages/Contact";
+import Fun from "./pages/Fun";
+import Games from "./pages/Games";
+import NotFound from "./pages/NotFound";
+
+// Background texture overlay
+const BackgroundTexture = () => (
+
+);
+
+// App component with cleaner design
+const App = () => (
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+);
+
+export default App;
\ No newline at end of file
diff --git a/Procfile.dev b/Procfile.dev
index da151fe..1f45bf2 100644
--- a/Procfile.dev
+++ b/Procfile.dev
@@ -1,2 +1,2 @@
web: bin/rails server
-css: bin/rails tailwindcss:watch
+vite: npm run dev
diff --git a/app/assets/stylesheets/custom_scrollbar.css b/app/assets/stylesheets/custom_scrollbar.css
index a0bf30c..f02de52 100644
--- a/app/assets/stylesheets/custom_scrollbar.css
+++ b/app/assets/stylesheets/custom_scrollbar.css
@@ -1,18 +1,18 @@
-/* Chrome, Edge, and Safari */
+/* Chrome, Edge, and Safari - Terminal Theme */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 4px;
+ background: rgba(30, 30, 30, 0.8);
+ border-radius: 2px;
background-image: repeating-linear-gradient(
to bottom,
transparent,
transparent 48px,
- #888 48px,
- #888 52px,
+ #444 48px,
+ #444 52px,
transparent 52px
);
background-position: center;
@@ -21,25 +21,25 @@
}
::-webkit-scrollbar-thumb {
- background-color: #888;
- border-radius: 4px;
+ background: rgba(155, 135, 245, 0.7);
+ border-radius: 2px;
}
::-webkit-scrollbar-thumb:hover {
- background-color: #555;
+ background: rgba(155, 135, 245, 0.9);
}
/* Firefox */
* {
scrollbar-width: thin;
- scrollbar-color: #888 #f1f1f1;
+ scrollbar-color: rgba(155, 135, 245, 0.7) rgba(30, 30, 30, 0.8);
}
-/* Custom class for directly applying to specific elements */
+/* Custom class for directly applying to specific elements - Terminal Theme */
.custom-scrollbar {
/* Firefox */
scrollbar-width: thin;
- scrollbar-color: #888 #f1f1f1;
+ scrollbar-color: rgba(155, 135, 245, 0.7) rgba(30, 30, 30, 0.8);
}
.custom-scrollbar::-webkit-scrollbar {
@@ -48,14 +48,14 @@
}
.custom-scrollbar::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 4px;
+ background: rgba(30, 30, 30, 0.8);
+ border-radius: 2px;
background-image: repeating-linear-gradient(
to bottom,
transparent,
transparent 48px,
- #888 48px,
- #888 52px,
+ #444 48px,
+ #444 52px,
transparent 52px
);
background-position: center;
@@ -64,12 +64,12 @@
}
.custom-scrollbar::-webkit-scrollbar-thumb {
- background-color: #888;
- border-radius: 4px;
+ background: rgba(155, 135, 245, 0.7);
+ border-radius: 2px;
}
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
- background-color: #555;
+ background: rgba(155, 135, 245, 0.9);
}
/* Force scrollbar to be visible even when content doesn't overflow */
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
new file mode 100644
index 0000000..b50cf5f
--- /dev/null
+++ b/app/controllers/admin_controller.rb
@@ -0,0 +1,107 @@
+class AdminController < ApplicationController
+ layout 'admin'
+
+ def dashboard
+ @blog_posts_count = BlogPost.count
+ @game_posts_count = GamePost.count
+ @work_posts_count = WorkPost.count
+ end
+
+ def blog_posts
+ @blog_posts = BlogPost.all.order(created_at: :desc)
+ end
+
+ def game_posts
+ @game_posts = GamePost.all.order(created_at: :desc)
+ end
+
+ def work_posts
+ @work_posts = WorkPost.all.order(created_at: :desc)
+ end
+
+ def new_game_post
+ @game_post = GamePost.new
+ end
+
+ def create_game_post
+ @game_post = GamePost.new(game_post_params)
+
+ if @game_post.save
+ redirect_to admin_game_posts_path, notice: "Game post was successfully created."
+ else
+ render :new_game_post
+ end
+ end
+
+ def edit_game_post
+ @game_post = GamePost.find(params[:id])
+ end
+
+ def update_game_post
+ @game_post = GamePost.find(params[:id])
+
+ if @game_post.update(game_post_params)
+ redirect_to admin_game_posts_path, notice: "Game post was successfully updated."
+ else
+ render :edit_game_post
+ end
+ end
+
+ def destroy_game_post
+ @game_post = GamePost.find(params[:id])
+ @game_post.destroy
+
+ redirect_to admin_game_posts_path, notice: "Game post was successfully deleted."
+ end
+
+ def new_work_post
+ @work_post = WorkPost.new
+ end
+
+ def create_work_post
+ @work_post = WorkPost.new(work_post_params)
+
+ if @work_post.save
+ redirect_to admin_work_posts_path, notice: "Work post was successfully created."
+ else
+ render :new_work_post
+ end
+ end
+
+ def edit_work_post
+ @work_post = WorkPost.find(params[:id])
+ end
+
+ def update_work_post
+ @work_post = WorkPost.find(params[:id])
+
+ if @work_post.update(work_post_params)
+ redirect_to admin_work_posts_path, notice: "Work post was successfully updated."
+ else
+ render :edit_work_post
+ end
+ end
+
+ def destroy_work_post
+ @work_post = WorkPost.find(params[:id])
+ @work_post.destroy
+
+ redirect_to admin_work_posts_path, notice: "Work post was successfully deleted."
+ end
+
+ private
+
+ def authenticate_admin
+ # In a real application, you'd want to use a proper authentication system
+ # This is just a placeholder for demonstration purposes
+ true
+ end
+
+ def game_post_params
+ params.require(:game_post).permit(:title, :description, :image_url, :link, :featured)
+ end
+
+ def work_post_params
+ params.require(:work_post).permit(:title, :description, :image_url, :featured, tags: [])
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/blog_posts_controller.rb b/app/controllers/api/blog_posts_controller.rb
new file mode 100644
index 0000000..6225472
--- /dev/null
+++ b/app/controllers/api/blog_posts_controller.rb
@@ -0,0 +1,17 @@
+module Api
+ class BlogPostsController < ApplicationController
+ def index
+ @blog_posts = BlogPost.all.order(created_at: :desc)
+ render json: @blog_posts
+ end
+
+ def show
+ @blog_post = BlogPost.find_by(slug: params[:slug])
+ if @blog_post
+ render json: @blog_post
+ else
+ render json: { error: "Blog post not found" }, status: :not_found
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/game_posts_controller.rb b/app/controllers/api/game_posts_controller.rb
new file mode 100644
index 0000000..0d6622e
--- /dev/null
+++ b/app/controllers/api/game_posts_controller.rb
@@ -0,0 +1,17 @@
+module Api
+ class GamePostsController < ApplicationController
+ def index
+ @game_posts = GamePost.all.order(created_at: :desc)
+ render json: @game_posts
+ end
+
+ def show
+ @game_post = GamePost.find_by(slug: params[:slug])
+ if @game_post
+ render json: @game_post
+ else
+ render json: { error: "Game post not found" }, status: :not_found
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/work_posts_controller.rb b/app/controllers/api/work_posts_controller.rb
new file mode 100644
index 0000000..d77a877
--- /dev/null
+++ b/app/controllers/api/work_posts_controller.rb
@@ -0,0 +1,17 @@
+module Api
+ class WorkPostsController < ApplicationController
+ def index
+ @work_posts = WorkPost.all.order(created_at: :desc)
+ render json: @work_posts
+ end
+
+ def show
+ @work_post = WorkPost.find_by(slug: params[:slug])
+ if @work_post
+ render json: @work_post
+ else
+ render json: { error: "Work post not found" }, status: :not_found
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/frontend_controller.rb b/app/controllers/frontend_controller.rb
new file mode 100644
index 0000000..5e78bb5
--- /dev/null
+++ b/app/controllers/frontend_controller.rb
@@ -0,0 +1,12 @@
+class FrontendController < ApplicationController
+ # Disable layout for this controller since we're rendering a React app
+ layout false
+
+ # Turn off content security policy for development to allow Vite HMR
+ content_security_policy false if Rails.env.development?
+
+ def show
+ # Render the view that includes our React application
+ render :show
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb
new file mode 100644
index 0000000..14c1f79
--- /dev/null
+++ b/app/controllers/games_controller.rb
@@ -0,0 +1,18 @@
+class GamesController < ApplicationController
+ def index
+ @game_posts = GamePost.all.order(created_at: :desc)
+ respond_to do |format|
+ format.html
+ format.json { render json: @game_posts }
+ end
+ end
+
+ def show
+ @game_post = GamePost.find_by(slug: params[:slug])
+
+ respond_to do |format|
+ format.html
+ format.json { render json: @game_post }
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/works_controller.rb b/app/controllers/works_controller.rb
new file mode 100644
index 0000000..8bad3a5
--- /dev/null
+++ b/app/controllers/works_controller.rb
@@ -0,0 +1,18 @@
+class WorksController < ApplicationController
+ def index
+ @work_posts = WorkPost.all.order(created_at: :desc)
+ respond_to do |format|
+ format.html
+ format.json { render json: @work_posts }
+ end
+ end
+
+ def show
+ @work_post = WorkPost.find_by(slug: params[:slug])
+
+ respond_to do |format|
+ format.html
+ format.json { render json: @work_post }
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/helpers/frontend_helper.rb b/app/helpers/frontend_helper.rb
new file mode 100644
index 0000000..6035a51
--- /dev/null
+++ b/app/helpers/frontend_helper.rb
@@ -0,0 +1,2 @@
+module FrontendHelper
+end
diff --git a/app/javascript/channels/application_cable/connection.rb b/app/javascript/channels/application_cable/connection.rb
new file mode 100644
index 0000000..d32f603
--- /dev/null
+++ b/app/javascript/channels/application_cable/connection.rb
@@ -0,0 +1,4 @@
+module ApplicationCable
+ class Connection < ActionCable::Connection::Base
+ end
+end
\ No newline at end of file
diff --git a/app/models/game_post.rb b/app/models/game_post.rb
new file mode 100644
index 0000000..2fb73e6
--- /dev/null
+++ b/app/models/game_post.rb
@@ -0,0 +1,15 @@
+class GamePost < ApplicationRecord
+ validates :title, presence: true
+ validates :description, presence: true
+ validates :slug, presence: true, uniqueness: true
+ validates :image_url, presence: true
+ validates :link, presence: true
+
+ before_validation :generate_slug, on: :create
+
+ private
+
+ def generate_slug
+ self.slug ||= title.parameterize if title.present?
+ end
+end
\ No newline at end of file
diff --git a/app/models/work_post.rb b/app/models/work_post.rb
new file mode 100644
index 0000000..cf74332
--- /dev/null
+++ b/app/models/work_post.rb
@@ -0,0 +1,15 @@
+class WorkPost < ApplicationRecord
+ validates :title, presence: true
+ validates :description, presence: true
+ validates :slug, presence: true, uniqueness: true
+ validates :image_url, presence: true
+ validates :tags, presence: true
+
+ before_validation :generate_slug, on: :create
+
+ private
+
+ def generate_slug
+ self.slug ||= title.parameterize if title.present?
+ end
+end
\ No newline at end of file
diff --git a/app/views/admin/blog_posts.html.erb b/app/views/admin/blog_posts.html.erb
new file mode 100644
index 0000000..b3c62f5
--- /dev/null
+++ b/app/views/admin/blog_posts.html.erb
@@ -0,0 +1,36 @@
+
+
+
+ <% if @blog_posts.any? %>
+
+
+
+ Title
+ Slug
+ Created
+ Actions
+
+
+
+ <% @blog_posts.each do |blog_post| %>
+
+ <%= blog_post.title %>
+ <%= blog_post.slug %>
+ <%= blog_post.created_at.strftime("%b %d, %Y") %>
+
+ Edit
+ Delete
+
+
+ <% end %>
+
+
+ <% else %>
+
No blog posts found.
+ <% end %>
+
+
Note: Blog post management features are coming soon.
+
\ No newline at end of file
diff --git a/app/views/admin/dashboard.html.erb b/app/views/admin/dashboard.html.erb
new file mode 100644
index 0000000..8968f70
--- /dev/null
+++ b/app/views/admin/dashboard.html.erb
@@ -0,0 +1,28 @@
+
+
Dashboard
+
+
+
+
Blog Posts
+
<%= @blog_posts_count %>
+ <%= link_to "Manage", admin_blog_posts_path, class: "btn btn-primary mt-4" %>
+
+
+
+
Game Posts
+
<%= @game_posts_count %>
+ <%= link_to "Manage", admin_game_posts_path, class: "btn btn-primary mt-4" %>
+
+
+
+
Work Posts
+
<%= @work_posts_count %>
+ <%= link_to "Manage", admin_work_posts_path, class: "btn btn-primary mt-4" %>
+
+
+
+
+
+
Recent Activity
+
No recent activity to display.
+
\ No newline at end of file
diff --git a/app/views/admin/edit_game_post.html.erb b/app/views/admin/edit_game_post.html.erb
new file mode 100644
index 0000000..4f7521d
--- /dev/null
+++ b/app/views/admin/edit_game_post.html.erb
@@ -0,0 +1,46 @@
+
+
Edit Game Post
+
+ <%= form_with(model: @game_post, url: admin_update_game_post_path(@game_post), method: :patch, local: true) do |form| %>
+ <% if @game_post.errors.any? %>
+
+
<%= pluralize(@game_post.errors.count, "error") %> prohibited this game post from being saved:
+
+ <% @game_post.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :title, class: "form-label" %>
+ <%= form.text_field :title, class: "form-control" %>
+
+
+
+ <%= form.label :description, class: "form-label" %>
+ <%= form.text_area :description, rows: 5, class: "form-control" %>
+
+
+
+ <%= form.label :image_url, class: "form-label" %>
+ <%= form.text_field :image_url, class: "form-control" %>
+
+
+
+ <%= form.label :link, class: "form-label" %>
+ <%= form.text_field :link, class: "form-control" %>
+
+
+
+ <%= form.check_box :featured, class: "checkbox" %>
+ <%= form.label :featured, class: "form-label mb-0" %>
+
+
+
+ <%= form.submit "Update Game Post", class: "btn btn-primary mr-2" %>
+ <%= link_to "Cancel", admin_game_posts_path, class: "btn bg-gray-200 text-gray-800 hover:bg-gray-300" %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/edit_work_post.html.erb b/app/views/admin/edit_work_post.html.erb
new file mode 100644
index 0000000..55dcee6
--- /dev/null
+++ b/app/views/admin/edit_work_post.html.erb
@@ -0,0 +1,46 @@
+
+
Edit Work Post
+
+ <%= form_with(model: @work_post, url: admin_update_work_post_path(@work_post), method: :patch, local: true) do |form| %>
+ <% if @work_post.errors.any? %>
+
+
<%= pluralize(@work_post.errors.count, "error") %> prohibited this work post from being saved:
+
+ <% @work_post.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :title, class: "form-label" %>
+ <%= form.text_field :title, class: "form-control" %>
+
+
+
+ <%= form.label :description, class: "form-label" %>
+ <%= form.text_area :description, rows: 5, class: "form-control" %>
+
+
+
+ <%= form.label :image_url, class: "form-label" %>
+ <%= form.text_field :image_url, class: "form-control" %>
+
+
+
+ <%= form.label :tags, "Tags (comma separated)", class: "form-label" %>
+ <%= form.text_field :tags, value: @work_post.tags&.join(", "), class: "form-control" %>
+
+
+
+ <%= form.check_box :featured, class: "checkbox" %>
+ <%= form.label :featured, class: "form-label mb-0" %>
+
+
+
+ <%= form.submit "Update Work Post", class: "btn btn-primary mr-2" %>
+ <%= link_to "Cancel", admin_work_posts_path, class: "btn bg-gray-200 text-gray-800 hover:bg-gray-300" %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/game_posts.html.erb b/app/views/admin/game_posts.html.erb
new file mode 100644
index 0000000..d6fef45
--- /dev/null
+++ b/app/views/admin/game_posts.html.erb
@@ -0,0 +1,36 @@
+
+
+
Game Posts
+ <%= link_to "Add New Game", admin_new_game_post_path, class: "btn btn-primary" %>
+
+
+ <% if @game_posts.any? %>
+
+
+
+ Title
+ Description
+ Featured
+ Created
+ Actions
+
+
+
+ <% @game_posts.each do |game_post| %>
+
+ <%= game_post.title %>
+ <%= truncate(game_post.description, length: 50) %>
+ <%= game_post.featured ? "Yes" : "No" %>
+ <%= game_post.created_at.strftime("%b %d, %Y") %>
+
+ <%= link_to "Edit", admin_edit_game_post_path(game_post), class: "text-blue-600 hover:underline mr-2" %>
+ <%= button_to "Delete", admin_destroy_game_post_path(game_post), method: :delete, data: { confirm: "Are you sure?" }, class: "text-red-600 hover:underline bg-transparent border-0 p-0 cursor-pointer" %>
+
+
+ <% end %>
+
+
+ <% else %>
+
No game posts found. <%= link_to "Create your first game post", admin_new_game_post_path, class: "text-blue-600 hover:underline" %>.
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/new_game_post.html.erb b/app/views/admin/new_game_post.html.erb
new file mode 100644
index 0000000..8aae879
--- /dev/null
+++ b/app/views/admin/new_game_post.html.erb
@@ -0,0 +1,46 @@
+
+
New Game Post
+
+ <%= form_with(model: @game_post, url: admin_create_game_post_path, local: true) do |form| %>
+ <% if @game_post.errors.any? %>
+
+
<%= pluralize(@game_post.errors.count, "error") %> prohibited this game post from being saved:
+
+ <% @game_post.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :title, class: "form-label" %>
+ <%= form.text_field :title, class: "form-control" %>
+
+
+
+ <%= form.label :description, class: "form-label" %>
+ <%= form.text_area :description, rows: 5, class: "form-control" %>
+
+
+
+ <%= form.label :image_url, class: "form-label" %>
+ <%= form.text_field :image_url, class: "form-control" %>
+
+
+
+ <%= form.label :link, class: "form-label" %>
+ <%= form.text_field :link, class: "form-control" %>
+
+
+
+ <%= form.check_box :featured, class: "checkbox" %>
+ <%= form.label :featured, class: "form-label mb-0" %>
+
+
+
+ <%= form.submit "Create Game Post", class: "btn btn-primary mr-2" %>
+ <%= link_to "Cancel", admin_game_posts_path, class: "btn bg-gray-200 text-gray-800 hover:bg-gray-300" %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/new_work_post.html.erb b/app/views/admin/new_work_post.html.erb
new file mode 100644
index 0000000..c6473df
--- /dev/null
+++ b/app/views/admin/new_work_post.html.erb
@@ -0,0 +1,46 @@
+
+
New Work Post
+
+ <%= form_with(model: @work_post, url: admin_create_work_post_path, local: true) do |form| %>
+ <% if @work_post.errors.any? %>
+
+
<%= pluralize(@work_post.errors.count, "error") %> prohibited this work post from being saved:
+
+ <% @work_post.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :title, class: "form-label" %>
+ <%= form.text_field :title, class: "form-control" %>
+
+
+
+ <%= form.label :description, class: "form-label" %>
+ <%= form.text_area :description, rows: 5, class: "form-control" %>
+
+
+
+ <%= form.label :image_url, class: "form-label" %>
+ <%= form.text_field :image_url, class: "form-control" %>
+
+
+
+ <%= form.label :tags, "Tags (comma separated)", class: "form-label" %>
+ <%= form.text_field :tags, value: @work_post.tags&.join(", "), class: "form-control" %>
+
+
+
+ <%= form.check_box :featured, class: "checkbox" %>
+ <%= form.label :featured, class: "form-label mb-0" %>
+
+
+
+ <%= form.submit "Create Work Post", class: "btn btn-primary mr-2" %>
+ <%= link_to "Cancel", admin_work_posts_path, class: "btn bg-gray-200 text-gray-800 hover:bg-gray-300" %>
+
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/admin/work_posts.html.erb b/app/views/admin/work_posts.html.erb
new file mode 100644
index 0000000..b0d0962
--- /dev/null
+++ b/app/views/admin/work_posts.html.erb
@@ -0,0 +1,38 @@
+
+
+
Work Posts
+ <%= link_to "Add New Work", admin_new_work_post_path, class: "btn btn-primary" %>
+
+
+ <% if @work_posts.any? %>
+
+
+
+ Title
+ Description
+ Tags
+ Featured
+ Created
+ Actions
+
+
+
+ <% @work_posts.each do |work_post| %>
+
+ <%= work_post.title %>
+ <%= truncate(work_post.description, length: 50) %>
+ <%= work_post.tags.join(", ") %>
+ <%= work_post.featured ? "Yes" : "No" %>
+ <%= work_post.created_at.strftime("%b %d, %Y") %>
+
+ <%= link_to "Edit", admin_edit_work_post_path(work_post), class: "text-blue-600 hover:underline mr-2" %>
+ <%= button_to "Delete", admin_destroy_work_post_path(work_post), method: :delete, data: { confirm: "Are you sure?" }, class: "text-red-600 hover:underline bg-transparent border-0 p-0 cursor-pointer" %>
+
+
+ <% end %>
+
+
+ <% else %>
+
No work posts found. <%= link_to "Create your first work post", admin_new_work_post_path, class: "text-blue-600 hover:underline" %>.
+ <% end %>
+
\ No newline at end of file
diff --git a/app/views/frontend/show.html.erb b/app/views/frontend/show.html.erb
new file mode 100644
index 0000000..e98e9dc
--- /dev/null
+++ b/app/views/frontend/show.html.erb
@@ -0,0 +1,227 @@
+
+
+
+
+
+ Austin French - Backend Developer
+
+
+
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading Frontend...
+
If you're seeing this message for a long time, JavaScript might be disabled in your browser.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
new file mode 100644
index 0000000..1ba7e3f
--- /dev/null
+++ b/app/views/layouts/admin.html.erb
@@ -0,0 +1,147 @@
+
+
+
+ Portfolio Admin
+
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+ <%= javascript_importmap_tags %>
+
+
+
+
+
+
+
+ <% if notice %>
+
<%= notice %>
+ <% end %>
+
+ <% if alert %>
+
<%= alert %>
+ <% end %>
+
+ <%= yield %>
+
+
+
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index ec5f73b..003fd67 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -32,8 +32,11 @@
-
+
<%= yield %>
+