Created
March 2, 2025 05:10
-
-
Save anon987654321/a49a6cf1ca5b470d9000713fe79c16d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env zsh | |
set -e | |
# amber.sh - Sets up "Amber", an AI-enhanced social network for fashion. | |
# Version: 2.0.0 (2025-03-02) | |
# | |
# Features: | |
# • Wardrobe management with AI-powered recommendations | |
# • Style assistance for outfit combinations | |
# • Medium-style rich text editor for fashion posts | |
# • Social interactions with infinite scroll and live search | |
# • Stripe integration for marketplace functionality | |
# • Mapbox integration for location-based fashion discovery | |
APP_NAME="amber" | |
BASE_DIR="$HOME/dev/rails_apps" | |
source "$BASE_DIR/__shared.sh" | |
log "Starting Amber setup (AI-based fashion network)" | |
# -- INITIALIZE APPLICATION -- | |
initialize_setup "$APP_NAME" | |
setup_yarn | |
create_rails_app "$APP_NAME" | |
# -- INSTALL DEPENDENCIES -- | |
install_common_gems | |
setup_devise | |
setup_merit | |
setup_frontend | |
setup_active_storage | |
setup_stripe | |
setup_mediumstyle_editor | |
setup_mapbox | |
setup_common_scss | |
setup_redis | |
setup_pwa | |
# -- GENERATE MODELS -- | |
log "Generating base social models" | |
generate_social_models | |
log "Generating WardrobeItem model for clothing management" | |
bin/rails generate model WardrobeItem name:string color:string size:string description:text category:string \ | |
brand:string season:string status:string user:references || error_exit "WardrobeItem model creation failed" | |
# Enhance WardrobeItem model | |
cat <<EOF > app/models/wardrobe_item.rb | |
class WardrobeItem < ApplicationRecord | |
# Associations | |
belongs_to :user | |
has_many :outfit_items, dependent: :destroy | |
has_many :outfits, through: :outfit_items | |
has_one_attached :photo | |
# Validations | |
validates :name, presence: true | |
validates :category, presence: true | |
# Statuses | |
enum status: { | |
active: 'active', | |
archived: 'archived', | |
donated: 'donated', | |
sold: 'sold' | |
} | |
# Categories | |
CATEGORIES = [ | |
'tops', 'bottoms', 'dresses', 'outerwear', | |
'shoes', 'accessories', 'bags', 'jewelry' | |
] | |
# Seasons | |
SEASONS = ['spring', 'summer', 'fall', 'winter', 'all'] | |
# Scopes | |
scope :by_category, ->(category) { where(category: category) } | |
scope :by_season, ->(season) { where(season: season) } | |
scope :active, -> { where(status: 'active') } | |
# Search | |
include PgSearch::Model | |
pg_search_scope :search_by_attributes, | |
against: [:name, :description, :brand, :color], | |
using: { tsearch: { prefix: true } } | |
# Methods | |
def similar_items | |
WardrobeItem | |
.where(category: category) | |
.where.not(id: id) | |
.limit(5) | |
end | |
end | |
EOF | |
log "Generating Outfit model for style combinations" | |
bin/rails generate model Outfit name:string description:text occasion:string season:string user:references || error_exit "Outfit model creation failed" | |
cat <<EOF > app/models/outfit.rb | |
class Outfit < ApplicationRecord | |
# Associations | |
belongs_to :user | |
has_many :outfit_items, dependent: :destroy | |
has_many :wardrobe_items, through: :outfit_items | |
has_many :reactions, as: :reactable, dependent: :destroy | |
# Validations | |
validates :name, presence: true | |
# Occasions | |
OCCASIONS = [ | |
'casual', 'work', 'formal', 'date_night', | |
'workout', 'travel', 'special_event' | |
] | |
# Seasons | |
SEASONS = ['spring', 'summer', 'fall', 'winter', 'all'] | |
# Scopes | |
scope :by_occasion, ->(occasion) { where(occasion: occasion) } | |
scope :by_season, ->(season) { where(season: season) } | |
scope :recent, -> { order(created_at: :desc) } | |
scope :popular, -> { joins(:reactions).group(:id).order('COUNT(reactions.id) DESC') } | |
# Search | |
include PgSearch::Model | |
pg_search_scope :search_by_attributes, | |
against: [:name, :description, :occasion], | |
using: { tsearch: { prefix: true } } | |
end | |
EOF | |
log "Generating OutfitItem join model" | |
bin/rails generate model OutfitItem outfit:references wardrobe_item:references || error_exit "OutfitItem model creation failed" | |
# -- GENERATE CONTROLLERS -- | |
log "Generating WardrobeItems controller" | |
bin/rails generate controller WardrobeItems index show new create edit update destroy || error_exit "WardrobeItems controller generation failed" | |
log "Generating Outfits controller" | |
bin/rails generate controller Outfits index show new create edit update destroy || error_exit "Outfits controller generation failed" | |
log "Generating StyleRecommendations controller for AI suggestions" | |
bin/rails generate controller StyleRecommendations index show || error_exit "StyleRecommendations controller generation failed" | |
# -- SETUP REFLEXES -- | |
setup_infinite_scroll | |
setup_live_search | |
log "Generating WardrobeReflex for real-time wardrobe management" | |
bin/rails generate reflex WardrobeReflex || error_exit "WardrobeReflex generation failed" | |
cat <<'EOF' > app/reflexes/wardrobe_reflex.rb | |
class WardrobeReflex < ApplicationReflex | |
def filter | |
category = element.dataset["category"] | |
season = element.dataset["season"] | |
@wardrobe_items = current_user.wardrobe_items.active | |
@wardrobe_items = @wardrobe_items.by_category(category) if category.present? | |
@wardrobe_items = @wardrobe_items.by_season(season) if season.present? | |
morph "#wardrobe_items", ApplicationController.render( | |
partial: "wardrobe_items/items_list", | |
locals: { wardrobe_items: @wardrobe_items } | |
) | |
end | |
def search | |
query = element.value.to_s.strip | |
if query.present? && query.length >= 2 | |
@wardrobe_items = current_user.wardrobe_items.search_by_attributes(query) | |
morph "#wardrobe_items", ApplicationController.render( | |
partial: "wardrobe_items/items_list", | |
locals: { wardrobe_items: @wardrobe_items } | |
) | |
end | |
end | |
end | |
EOF | |
# -- SETUP ROUTES -- | |
log "Configuring routes" | |
cat <<'EOF' > config/routes.rb | |
Rails.application.routes.draw do | |
# Root path | |
root 'home#index' | |
# Authentication | |
devise_for :users | |
# Wardrobe management | |
resources :wardrobe_items | |
# Outfit management | |
resources :outfits | |
# Style recommendations | |
resources :style_recommendations, only: [:index, :show] | |
# Social features | |
resources :posts do | |
resources :reactions, only: [:create, :destroy] | |
end | |
# User profiles | |
resources :users, only: [:show] do | |
member do | |
get :wardrobe | |
get :outfits | |
get :posts | |
end | |
end | |
# Marketplace (enabled by Stripe) | |
namespace :marketplace do | |
resources :listings | |
resources :purchases, only: [:index, :show, :create] | |
end | |
# PWA offline route | |
get "/offline", to: "pages#offline" | |
# Stripe webhook | |
mount StripeEvent::Engine, at: '/stripe/webhooks' | |
end | |
EOF | |
# -- CREATE HOME CONTROLLER AND VIEWS -- | |
log "Generating Home controller" | |
bin/rails generate controller Home index || error_exit "Home controller generation failed" | |
cat <<'EOF' > app/views/home/index.html.erb | |
<div class="home-container"> | |
<header class="hero"> | |
<h1>Welcome to Amber</h1> | |
<p>Your AI-powered fashion assistant</p> | |
</header> | |
<section class="features"> | |
<div class="feature"> | |
<h3>Manage Your Wardrobe</h3> | |
<p>Catalog your clothing items and access them anytime, anywhere.</p> | |
<%= link_to 'My Wardrobe', wardrobe_items_path, class: 'btn btn-primary' if user_signed_in? %> | |
</div> | |
<div class="feature"> | |
<h3>Get Outfit Recommendations</h3> | |
<p>AI-powered suggestions based on your style, occasion, and weather.</p> | |
<%= link_to 'Style Suggestions', style_recommendations_path, class: 'btn btn-primary' if user_signed_in? %> | |
</div> | |
<div class="feature"> | |
<h3>Share Your Style</h3> | |
<p>Create posts about your outfits and fashion discoveries.</p> | |
<%= link_to 'Fashion Feed', posts_path, class: 'btn btn-primary' %> | |
</div> | |
</section> | |
<% if !user_signed_in? %> | |
<section class="cta"> | |
<h2>Start Your Fashion Journey</h2> | |
<div class="cta-buttons"> | |
<%= link_to 'Sign Up', new_user_registration_path, class: 'btn btn-primary' %> | |
<%= link_to 'Log In', new_user_session_path, class: 'btn btn-secondary' %> | |
</div> | |
</section> | |
<% end %> | |
</div> | |
EOF | |
# -- CREATE STYLE RECOMMENDATION SERVICE -- | |
log "Creating AI style recommendation service" | |
mkdir -p app/services | |
cat <<'EOF' > app/services/style_recommendation_service.rb | |
class StyleRecommendationService | |
attr_reader :user, :occasion, :season | |
def initialize(user, occasion: nil, season: nil) | |
@user = user | |
@occasion = occasion || 'casual' | |
@season = season || current_season | |
end | |
def recommend_outfit | |
wardrobe_items = user.wardrobe_items.active | |
# Create a balanced outfit based on categories | |
outfit_items = {} | |
# Start with tops | |
outfit_items[:top] = wardrobe_items.by_category('tops') | |
.by_season([season, 'all']) | |
.order('RANDOM()') | |
.first | |
# Add bottoms | |
outfit_items[:bottom] = wardrobe_items.by_category('bottoms') | |
.by_season([season, 'all']) | |
.order('RANDOM()') | |
.first | |
# Add shoes | |
outfit_items[:shoes] = wardrobe_items.by_category('shoes') | |
.by_season([season, 'all']) | |
.order('RANDOM()') | |
.first | |
# Add accessories (optional) | |
outfit_items[:accessory] = wardrobe_items.by_category('accessories') | |
.by_season([season, 'all']) | |
.order('RANDOM()') | |
.first | |
# Filter out nil items | |
outfit_items.compact | |
end | |
def recommend_items_to_buy | |
# Identify gaps in the wardrobe | |
categories = WardrobeItem::CATEGORIES | |
missing_categories = [] | |
categories.each do |category| | |
if user.wardrobe_items.by_category(category).count < 2 | |
missing_categories << category | |
end | |
end | |
# Return recommendations for missing items | |
missing_categories.map do |category| | |
{ | |
category: category, | |
recommendation: "Consider adding more #{category} to your wardrobe" | |
} | |
end | |
end | |
private | |
def current_season | |
month = Time.zone.now.month | |
case month | |
when 3..5 | |
'spring' | |
when 6..8 | |
'summer' | |
when 9..11 | |
'fall' | |
else | |
'winter' | |
end | |
end | |
end | |
EOF | |
# -- MIGRATE AND SEED -- | |
log "Running database migrations" | |
bin/rails db:create db:migrate || error_exit "Database migration failed" | |
log "Setting up seed data" | |
cat <<'EOF' > db/seeds/wardrobe_items.rb | |
# Sample wardrobe items for users | |
users = User.all | |
users.each do |user| | |
# Create tops | |
['T-shirt', 'Button-up Shirt', 'Blouse', 'Sweater'].each do |item| | |
WardrobeItem.create!( | |
name: item, | |
category: 'tops', | |
color: ['white', 'black', 'blue', 'green', 'red'].sample, | |
size: ['XS', 'S', 'M', 'L', 'XL'].sample, | |
description: "A nice #{item.downcase}", | |
brand: ['Zara', 'H&M', 'Nike', 'Adidas', 'Uniqlo'].sample, | |
season: ['spring', 'summer', 'fall', 'winter', 'all'].sample, | |
status: 'active', | |
user: user | |
) | |
end | |
# Create bottoms | |
['Jeans', 'Pants', 'Shorts', 'Skirt'].each do |item| | |
WardrobeItem.create!( | |
name: item, | |
category: 'bottoms', | |
color: ['blue', 'black', 'beige', 'grey'].sample, | |
size: ['XS', 'S', 'M', 'L', 'XL'].sample, | |
description: "A nice pair of #{item.downcase}", | |
brand: ['Levi\'s', 'Gap', 'J.Crew', 'Uniqlo'].sample, | |
season: ['spring', 'summer', 'fall', 'winter', 'all'].sample, | |
status: 'active', | |
user: user | |
) | |
end | |
# Create shoes | |
['Sneakers', 'Boots', 'Dress Shoes', 'Sandals'].each do |item| | |
WardrobeItem.create!( | |
name: item, | |
category: 'shoes', | |
color: ['black', 'white', 'brown', 'blue'].sample, | |
size: ((5..12).to_a.sample).to_s, | |
description: "A nice pair of #{item.downcase}", | |
brand: ['Nike', 'Adidas', 'Converse', 'Vans', 'Dr. Martens'].sample, | |
season: ['spring', 'summer', 'fall', 'winter', 'all'].sample, | |
status: 'active', | |
user: user | |
) | |
end | |
# Create accessories | |
['Hat', 'Scarf', 'Necklace', 'Watch'].each do |item| | |
WardrobeItem.create!( | |
name: item, | |
category: 'accessories', | |
color: ['black', 'gold', 'silver', 'red', 'blue'].sample, | |
size: 'One Size', | |
description: "A nice #{item.downcase}", | |
brand: ['Fossil', 'Gucci', 'Zara', 'H&M'].sample, | |
season: ['spring', 'summer', 'fall', 'winter', 'all'].sample, | |
status: 'active', | |
user: user | |
) | |
end | |
end | |
EOF | |
cat <<'EOF' > db/seeds/outfits.rb | |
# Create sample outfits for users | |
users = User.all | |
occasions = Outfit::OCCASIONS | |
seasons = Outfit::SEASONS | |
users.each do |user| | |
3.times do |i| | |
# Create an outfit | |
outfit = Outfit.create!( | |
name: "#{occasions.sample.titleize} Outfit #{i+1}", | |
description: "A great outfit for #{occasions.sample} occasions", | |
occasion: occasions.sample, | |
season: seasons.sample, | |
user: user | |
) | |
# Add wardrobe items to the outfit | |
tops = user.wardrobe_items.by_category('tops').limit(1) | |
bottoms = user.wardrobe_items.by_category('bottoms').limit(1) | |
shoes = user.wardrobe_items.by_category('shoes').limit(1) | |
accessories = user.wardrobe_items.by_category('accessories').limit(1) | |
[tops, bottoms, shoes, accessories].flatten.each do |item| | |
OutfitItem.create!( | |
outfit: outfit, | |
wardrobe_item: item | |
) if item | |
end | |
end | |
end | |
EOF | |
cat <<'EOF' >> db/seeds.rb | |
# Amber seed data | |
puts "Creating wardrobe items..." | |
require Rails.root.join('db', 'seeds', 'wardrobe_items') | |
puts "Creating outfits..." | |
require Rails.root.join('db', 'seeds', 'outfits') | |
EOF | |
# Run seeds | |
log "Populating database with seed data" | |
setup_social_seed_data | |
bin/rails db:seed || error_exit "Database seeding failed" | |
# -- FINAL STEPS -- | |
log "Setting up application layout" | |
cat <<'EOF' > app/views/layouts/application.html.erb | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title><%= content_for?(:title) ? yield(:title) : "Amber - AI Fashion Assistant" %></title> | |
<meta name="description" content="<%= content_for?(:description) ? yield(:description) : "Amber helps you manage your wardrobe, get style recommendations, and share your fashion journey." %>"> | |
<%= csrf_meta_tags %> | |
<%= csp_meta_tag %> | |
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> | |
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %> | |
<!-- PWA support --> | |
<%= render 'layouts/pwa' %> | |
<!-- SEO metadata --> | |
<meta property="og:title" content="<%= content_for?(:title) ? yield(:title) : "Amber - AI Fashion Assistant" %>"> | |
<meta property="og:description" content="<%= content_for?(:description) ? yield(:description) : "Amber helps you manage your wardrobe, get style recommendations, and share your fashion journey." %>"> | |
<meta property="og:type" content="website"> | |
<meta property="og:url" content="<%= request.original_url %>"> | |
</head> | |
<body> | |
<%= render 'shared/navigation' %> | |
<div class="container"> | |
<%= render 'shared/flash_messages' %> | |
<main> | |
<%= yield %> | |
</main> | |
</div> | |
<%= render 'shared/footer' %> | |
</body> | |
</html> | |
EOF | |
log "Creating navigation partial" | |
mkdir -p app/views/shared | |
cat <<'EOF' > app/views/shared/_navigation.html.erb | |
<nav class="main-nav"> | |
<div class="container"> | |
<div class="nav-brand"> | |
<%= link_to "Amber", root_path %> | |
</div> | |
<div class="nav-links"> | |
<% if user_signed_in? %> | |
<%= link_to "My Wardrobe", wardrobe_items_path %> | |
<%= link_to "My Outfits", outfits_path %> | |
<%= link_to "Style Recommendations", style_recommendations_path %> | |
<%= link_to "Fashion Feed", posts_path %> | |
<div class="dropdown"> | |
<button class="dropdown-toggle"><%= current_user.name %></button> | |
<div class="dropdown-menu"> | |
<%= link_to "Profile", user_path(current_user) %> | |
<%= link_to "Settings", edit_user_registration_path %> | |
<%= link_to "Sign Out", destroy_user_session_path, method: :delete %> | |
</div> | |
</div> | |
<% else %> | |
<%= link_to "Fashion Feed", posts_path %> | |
<%= link_to "Sign In", new_user_session_path, class: "btn btn-secondary" %> | |
<%= link_to "Sign Up", new_user_registration_path, class: "btn btn-primary" %> | |
<% end %> | |
</div> | |
<button class="mobile-menu-toggle" data-controller="mobile-menu"> | |
<span></span> | |
<span></span> | |
<span></span> | |
</button> | |
</div> | |
</nav> | |
EOF | |
log "Creating flash messages partial" | |
cat <<'EOF' > app/views/shared/_flash_messages.html.erb | |
<% flash.each do |key, message| %> | |
<div class="flash flash-<%= key %>"> | |
<%= message %> | |
<button class="close">×</button> | |
</div> | |
<% end %> | |
EOF | |
log "Creating footer partial" | |
cat <<'EOF' > app/views/shared/_footer.html.erb | |
<footer class="main-footer"> | |
<div class="container"> | |
<div class="footer-content"> | |
<div class="footer-brand"> | |
<h3>Amber</h3> | |
<p>Your AI-powered fashion assistant</p> | |
</div> | |
<div class="footer-links"> | |
<div class="footer-section"> | |
<h4>Features</h4> | |
<ul> | |
<li><%= link_to "Wardrobe Management", wardrobe_items_path %></li> | |
<li><%= link_to "Style Recommendations", style_recommendations_path %></li> | |
<li><%= link_to "Fashion Feed", posts_path %></li> | |
</ul> | |
</div> | |
<div class="footer-section"> | |
<h4>Help</h4> | |
<ul> | |
<li><a href="#">FAQ</a></li> | |
<li><a href="#">Contact Us</a></li> | |
<li><a href="#">Privacy Policy</a></li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<div class="footer-bottom"> | |
<p>© <%= Time.current.year %> Amber. All rights reserved.</p> | |
</div> | |
</div> | |
</footer> | |
EOF | |
# Commit changes to Git | |
commit_to_git "Amber is fully set up (AI-enhanced fashion network)" | |
log "Amber setup complete. Run 'bin/rails server' to start local development." | |
echo "Visit http://localhost:3000 to see your application." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment