mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
Redesign collection creation form with modern TailwindCSS styling
- Replace mixed MaterializeCSS/TailwindCSS with comprehensive modern design - Add beautiful gradient hero section with collection branding and breadcrumbs - Organize form into logical card-based sections with color-coded headers - Implement enhanced form controls with custom styling: * Custom checkboxes with visual feedback for content types * Toggle switches for submission settings * Drag-and-drop file upload with visual states * Character counter for description field * Radio buttons with descriptive layouts - Add interactive JavaScript features: * Select All/None/Common Types for content selection * Progressive disclosure for submission options * Real-time character counting with color feedback * Drag-and-drop file upload enhancement - Improve UX with better visual hierarchy and spacing - Add comprehensive help text and guidance throughout - Maintain all existing functionality while enhancing usability - Ensure responsive design works across all screen sizes 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
23297339bb
commit
23a20629e1
@ -1,231 +1,575 @@
|
||||
<div class="lg:grid lg:grid-cols-12 lg:gap-x-5 max-w-7xl mx-auto">
|
||||
<aside class="py-6 px-2 sm:px-6 lg:py-0 lg:px-0 lg:col-span-3">
|
||||
<nav class="space-y-1">
|
||||
<!-- Current: "bg-gray-50 text-indigo-700 hover:text-indigo-700 hover:bg-white", Default: "text-gray-900 hover:text-gray-900 hover:bg-gray-50" -->
|
||||
<a href="#" class="bg-gray-50 <%= PageCollection.text_color %> hover:text-brown-700 hover:bg-white group rounded-md px-3 py-2 flex items-center text-sm font-medium" aria-current="page">
|
||||
<i class="material-icons float-left -ml-1 mr-3"><%= PageCollection.icon %></i>
|
||||
<span class="truncate">Look & feel</span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="text-gray-600 hover:text-gray-800 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<i class="material-icons float-left -ml-1 mr-3">inbox</i>
|
||||
<span class="truncate">Submissions</span>
|
||||
</a>
|
||||
|
||||
<!--
|
||||
<a href="#" class="text-gray-600 hover:text-gray-800 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<i class="material-icons float-left -ml-1 mr-3">lock</i>
|
||||
<span class="truncate">Privacy</span>
|
||||
</a>
|
||||
-->
|
||||
|
||||
<!--
|
||||
<a href="#" class="text-gray-900 hover:text-gray-900 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<svg class="text-gray-400 group-hover:text-gray-500 flex-shrink-0 -ml-1 mr-3 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
||||
</svg>
|
||||
<span class="truncate"> Plan & Billing </span>
|
||||
</a>
|
||||
-->
|
||||
<!--
|
||||
<a href="#" class="text-gray-900 hover:text-gray-900 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<svg class="flex-shrink-0 -ml-1 mr-3 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
||||
</svg>
|
||||
<span class="truncate"> Collaborators </span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="text-gray-900 hover:text-gray-900 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<i class="material-icons float-left -ml-1 mr-3"><%= User.icon %></i>
|
||||
<span class="truncate"> Followers </span>
|
||||
</a>
|
||||
|
||||
<a href="#" class="text-gray-900 hover:text-gray-900 hover:bg-gray-50 group rounded-md px-3 py-2 flex items-center text-sm font-medium">
|
||||
<svg class="flex-shrink-0 -ml-1 mr-3 h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 14v6m-3-3h6M6 10h2a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2zm10 0h2a2 2 0 002-2V6a2 2 0 00-2-2h-2a2 2 0 00-2 2v2a2 2 0 002 2zM6 20h2a2 2 0 002-2v-2a2 2 0 00-2-2H6a2 2 0 00-2 2v2a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<span class="truncate"> Integrations </span>
|
||||
</a>
|
||||
-->
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<%# todo: audit usage of form_with and local:true %>
|
||||
<%= form_with(model: page_collection, local: true, class: 'sm:px-6 lg:px-0 lg:col-span-9') do |f| %>
|
||||
<% if page_collection.errors.any? %>
|
||||
<div id="error_explanation">
|
||||
<h2 style="font-size: 1.5em"><%= pluralize(page_collection.errors.count, "error") %> prohibited this Collection from being saved:</h2>
|
||||
|
||||
<ul class="browser-default">
|
||||
<% page_collection.errors.full_messages.each do |message| %>
|
||||
<li class="red-text"><%= message %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class="shadow sm:rounded-md sm:overflow-hidden mb-8">
|
||||
<div class="bg-white py-6 px-4 space-y-6 sm:p-6">
|
||||
<%= form_with(model: page_collection, local: true, class: 'space-y-8') do |f| %>
|
||||
|
||||
<!-- Error Messages -->
|
||||
<% if page_collection.errors.any? %>
|
||||
<div class="bg-red-50 border border-red-200 rounded-lg p-4">
|
||||
<div class="flex items-start">
|
||||
<i class="material-icons text-red-400 mr-3 mt-0.5">error</i>
|
||||
<div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">Look & feel</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Control the look and feel of your collection.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-6">
|
||||
<!--
|
||||
<div class="col-span-3 sm:col-span-2">
|
||||
<label for="company-website" class="block text-sm font-medium text-gray-700">
|
||||
URL
|
||||
</label>
|
||||
<div class="mt-1 rounded-md shadow-sm flex">
|
||||
<span class="bg-gray-50 border border-r-0 border-gray-300 rounded-l-md px-3 inline-flex items-center text-gray-500 sm:text-sm">
|
||||
notebook.ai/
|
||||
</span>
|
||||
<input type="text" name="username" id="username" autocomplete="username" class="focus:ring-indigo-500 focus:border-indigo-500 flex-grow block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300">
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="col-span-3">
|
||||
<%= f.label :title, class: 'block text-sm font-medium text-gray-700' %>
|
||||
<%= f.text_field :title, placeholder: 'My Awesome Collection', class: 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-brown-500 focus:border-brown-500 sm:text-sm' %>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3">
|
||||
<%= f.label :subtitle, class: 'block text-sm font-medium text-gray-700' %>
|
||||
<%= f.text_field :subtitle, placeholder: 'My favorite pages', class: 'mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-brown-500 focus:border-brown-500 sm:text-sm' %>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3">
|
||||
<%= f.label :description, class: 'block text-sm font-medium text-gray-700' %>
|
||||
<div class="mt-1">
|
||||
<%= f.text_area :description, placeholder: "Write as little or as much as you'd like!", rows: 5, class: 'shadow-sm focus:ring-brown-500 focus:border-brown-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md' %>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500">Brief description for your collection. <code><strong></code> and <code><em></em></code> tags are supported.</p>
|
||||
</div>
|
||||
|
||||
<div class="col-span-3 -mb-6">
|
||||
<label class="block text-sm font-medium text-gray-700">Header image</label>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<% if @page_collection.persisted? && @page_collection.header_image %>
|
||||
<%= link_to @page_collection.header_image, target: '_blank' do %>
|
||||
<%= image_tag @page_collection.first_public_image, class: 'rounded' %>
|
||||
<% end %>
|
||||
<h3 class="text-sm font-medium text-red-800 mb-2">
|
||||
<%= pluralize(page_collection.errors.count, "error") %> prevented this collection from being saved:
|
||||
</h3>
|
||||
<ul class="text-sm text-red-700 space-y-1">
|
||||
<% page_collection.errors.full_messages.each do |message| %>
|
||||
<li class="flex items-start">
|
||||
<span class="inline-block w-1 h-1 bg-red-400 rounded-full mt-2 mr-2 flex-shrink-0"></span>
|
||||
<%= message %>
|
||||
</li>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="col-span-2">
|
||||
<div class="mt-1 border-2 border-gray-300 border-dashed rounded-md px-6 pt-5 pb-6 flex justify-center">
|
||||
<div class="space-y-1 text-center">
|
||||
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none" viewBox="0 0 48 48" aria-hidden="true">
|
||||
<path d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
<div class="flex text-sm text-gray-600">
|
||||
<%= f.label :header_image, class: 'relative cursor-pointer bg-white rounded-md font-medium text-brown-600 hover:text-brown-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-brown-500' do %>
|
||||
<span>Upload a file</span>
|
||||
<%= f.file_field :header_image, class: 'sr-only' %>
|
||||
<% end %>
|
||||
<p class="pl-1">to change your header</p>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500">PNG, JPG, GIF up to 10MB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<fieldset class="col-span-3">
|
||||
<label class="block text-sm font-medium text-gray-700">Allowed page types</label>
|
||||
<div class="mt-2 border-t border-b border-gray-200 divide-y divide-gray-200 grid grid-cols-3">
|
||||
<%# todo: show user's ACTIVATED page types here and put the others behind an "all types" expander %>
|
||||
<% Rails.application.config.content_types[:all].each do |content_type| %>
|
||||
<div class="relative flex items-start py-2 col-span-1 text-sm">
|
||||
<%= f.label "page_types[#{content_type.name}]", class: 'font-medium text-gray-700 select-none' do %>
|
||||
<%= f.check_box "page_types[#{content_type.name}]", checked: page_collection.page_types.include?(content_type.name), class: 'focus:ring-notebook-blue h-6 w-6 border-gray-300 rounded mr-3 -mt-3' %>
|
||||
|
||||
<i class="material-icons <%= content_type.text_color %> mr-2 relative top-0.5">
|
||||
<%= content_type.icon %>
|
||||
</i>
|
||||
<span class="flex-1 relative -top-1">
|
||||
<%= content_type.name.pluralize %>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</fieldset>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
||||
<%= f.submit 'Save', class: "#{PageCollection.color} border border-transparent rounded-md shadow-sm py-2 px-4 inline-flex justify-center text-sm font-medium text-white hover:bg-brown-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brown-500" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shadow sm:rounded-md sm:overflow-hidden">
|
||||
<div class="bg-white py-6 px-4 space-y-6 sm:p-6">
|
||||
<div>
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">Privacy & Submissions</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">
|
||||
Collections are better with contributors!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="gap-6">
|
||||
<fieldset class="my-6">
|
||||
<legend class="text-base font-medium text-gray-900">Visibility</legend>
|
||||
<p class="text-sm text-gray-500">This setting controls who can see this collection.</p>
|
||||
<div class="mt-4 space-y-4">
|
||||
<%= f.label :privacy, class: 'block' do %>
|
||||
<%= f.radio_button :privacy, 'public', class: 'focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300' %>
|
||||
<span class="inline-block ml-2 text-sm font-medium text-gray-700">Public</span><span class="text-sm"> - Visible to anyone browsing collections</span>
|
||||
<% end %>
|
||||
<%= f.label :privacy, class: 'block' do %>
|
||||
<%= f.radio_button :privacy, 'private', class: 'focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300' %>
|
||||
<span class="inline-block ml-2 text-sm font-medium text-gray-700">Private</span><span class="text-sm"> - Visible only to you</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend class="text-base font-medium text-gray-900">Page submissions</legend>
|
||||
<div class="my-4 space-y-4">
|
||||
<div class="flex items-start">
|
||||
<div class="text-sm items-center">
|
||||
<%= f.label :allow_submissions, class: 'font-medium text-gray-700' do %>
|
||||
<%= f.check_box :allow_submissions, class: 'mr-2 focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded' %>
|
||||
Allow submissions from other users
|
||||
<p class="text-gray-500 ml-7">Submissions are added to a review queue for you to approve or deny.</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ml-7">
|
||||
<label for="about" class="block text-sm font-medium text-gray-700">Submission guidelines</label>
|
||||
<div class="mt-1">
|
||||
<textarea id="about" name="about" rows="3" class="shadow-sm focus:ring-brown-500 focus:border-brown-500 mt-1 block w-full sm:text-sm border border-gray-300 rounded-md" placeholder="Write as little or as much as you'd like!"></textarea>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500">
|
||||
What kinds of pages are you looking for? Anything you write here will be shown to users who are about to submit a page.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex items-start">
|
||||
<div class="text-sm items-center">
|
||||
<%= f.label :auto_accept, class: 'font-medium text-gray-700' do %>
|
||||
<%= f.check_box :auto_accept, class: 'mr-2 focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded' %>
|
||||
Auto-accept all submissions
|
||||
<p class="text-gray-500 ml-7">Submissions will skip the review queue and automatically publish.</p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
||||
<%= f.submit 'Save', class: "#{PageCollection.color} border border-transparent rounded-md shadow-sm py-2 px-4 inline-flex justify-center text-sm font-medium text-white hover:bg-brown-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-brown-500" %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<!-- Basic Information Card -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gradient-to-r from-amber-50 to-orange-50">
|
||||
<div class="flex items-center">
|
||||
<div class="p-2 bg-amber-100 rounded-lg mr-3">
|
||||
<i class="material-icons text-amber-600">edit</i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">Basic Information</h2>
|
||||
<p class="text-sm text-gray-600">Set up the core details for your collection</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-6">
|
||||
<!-- Title -->
|
||||
<div>
|
||||
<%= f.label :title, class: 'block text-sm font-medium text-gray-900 mb-2' do %>
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">title</i>
|
||||
Collection Title *
|
||||
</span>
|
||||
<% end %>
|
||||
<%= f.text_field :title,
|
||||
placeholder: 'My Awesome Collection',
|
||||
class: 'block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-amber-500 focus:border-amber-500 transition-colors duration-200 text-sm',
|
||||
required: true %>
|
||||
<p class="mt-1 text-xs text-gray-500">Choose a descriptive name that captures what your collection is about</p>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<div>
|
||||
<%= f.label :subtitle, class: 'block text-sm font-medium text-gray-900 mb-2' do %>
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">subtitles</i>
|
||||
Subtitle
|
||||
</span>
|
||||
<% end %>
|
||||
<%= f.text_field :subtitle,
|
||||
placeholder: 'A curated selection of my favorite pages',
|
||||
class: 'block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-amber-500 focus:border-amber-500 transition-colors duration-200 text-sm' %>
|
||||
<p class="mt-1 text-xs text-gray-500">Optional tagline to give more context about your collection</p>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div>
|
||||
<%= f.label :description, class: 'block text-sm font-medium text-gray-900 mb-2' do %>
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">description</i>
|
||||
Description
|
||||
</span>
|
||||
<% end %>
|
||||
<%= f.text_area :description,
|
||||
placeholder: "Write as little or as much as you'd like! Describe what makes this collection special...",
|
||||
rows: 4,
|
||||
class: 'block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-amber-500 focus:border-amber-500 transition-colors duration-200 text-sm resize-none' %>
|
||||
<p class="mt-1 text-xs text-gray-500">
|
||||
<span class="flex items-center justify-between">
|
||||
<span>Brief description for your collection. <code class="bg-gray-100 px-1 rounded text-xs"><strong></code> and <code class="bg-gray-100 px-1 rounded text-xs"><em></code> tags are supported.</span>
|
||||
<span class="text-gray-400" id="char-count">0 / 500</span>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Visual Settings Card -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gradient-to-r from-blue-50 to-indigo-50">
|
||||
<div class="flex items-center">
|
||||
<div class="p-2 bg-blue-100 rounded-lg mr-3">
|
||||
<i class="material-icons text-blue-600">palette</i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">Visual Settings</h2>
|
||||
<p class="text-sm text-gray-600">Customize how your collection looks</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<!-- Header Image -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-900 mb-4">
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">image</i>
|
||||
Header Image
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<!-- Current Image Preview -->
|
||||
<% if @page_collection.persisted? && @page_collection.header_image %>
|
||||
<div class="md:col-span-1">
|
||||
<div class="aspect-w-16 aspect-h-9 rounded-lg overflow-hidden bg-gray-100">
|
||||
<%= link_to @page_collection.header_image, target: '_blank', class: "block w-full h-full" do %>
|
||||
<%= image_tag @page_collection.first_public_image, class: 'w-full h-full object-cover hover:scale-105 transition-transform duration-200' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-2 text-center">Current image</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Upload Area -->
|
||||
<div class="<%= @page_collection.persisted? && @page_collection.header_image ? 'md:col-span-2' : 'md:col-span-3' %>">
|
||||
<div class="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center hover:border-gray-400 transition-colors duration-200">
|
||||
<div class="space-y-4">
|
||||
<div class="mx-auto w-12 h-12 bg-gray-100 rounded-lg flex items-center justify-center">
|
||||
<i class="material-icons text-gray-400 text-2xl">cloud_upload</i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<%= f.label :header_image, class: 'cursor-pointer' do %>
|
||||
<span class="inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-lg shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-amber-500 transition-colors duration-200">
|
||||
<i class="material-icons text-sm mr-2">add_photo_alternate</i>
|
||||
Choose Image
|
||||
</span>
|
||||
<%= f.file_field :header_image, class: 'sr-only', accept: 'image/*' %>
|
||||
<% end %>
|
||||
<p class="text-sm text-gray-500 mt-2">or drag and drop</p>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-gray-500">
|
||||
<p>PNG, JPG, GIF up to 10MB</p>
|
||||
<p>Recommended: 1200×400px for best results</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content Settings Card -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gradient-to-r from-purple-50 to-pink-50">
|
||||
<div class="flex items-center">
|
||||
<div class="p-2 bg-purple-100 rounded-lg mr-3">
|
||||
<i class="material-icons text-purple-600">category</i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">Content Settings</h2>
|
||||
<p class="text-sm text-gray-600">Choose what types of content can be added to this collection</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<fieldset>
|
||||
<legend class="block text-sm font-medium text-gray-900 mb-4">
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">checklist</i>
|
||||
Allowed Page Types *
|
||||
</span>
|
||||
</legend>
|
||||
|
||||
<!-- Select All/None Controls -->
|
||||
<div class="flex items-center space-x-4 mb-4 p-3 bg-gray-50 rounded-lg">
|
||||
<button type="button" id="select-all-types" class="text-sm font-medium text-blue-600 hover:text-blue-700 transition-colors duration-200">
|
||||
Select All
|
||||
</button>
|
||||
<span class="text-gray-300">|</span>
|
||||
<button type="button" id="select-none-types" class="text-sm font-medium text-gray-600 hover:text-gray-700 transition-colors duration-200">
|
||||
Select None
|
||||
</button>
|
||||
<span class="text-gray-300">|</span>
|
||||
<button type="button" id="select-common-types" class="text-sm font-medium text-green-600 hover:text-green-700 transition-colors duration-200">
|
||||
Common Types
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Content Type Grid -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<% Rails.application.config.content_types[:all].each do |content_type| %>
|
||||
<div class="content-type-option">
|
||||
<%= f.label "page_types[#{content_type.name}]", class: 'flex items-center p-3 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 hover:border-gray-300 transition-all duration-200 group' do %>
|
||||
<%= f.check_box "page_types[#{content_type.name}]",
|
||||
checked: page_collection.page_types.include?(content_type.name),
|
||||
class: 'sr-only content-type-checkbox',
|
||||
data: {
|
||||
type: content_type.name.downcase,
|
||||
common: %w[character location item universe].include?(content_type.name.downcase)
|
||||
} %>
|
||||
|
||||
<!-- Custom Checkbox -->
|
||||
<div class="checkbox-custom w-5 h-5 border-2 border-gray-300 rounded flex items-center justify-center mr-3 flex-shrink-0 group-hover:border-gray-400 transition-colors duration-200">
|
||||
<i class="material-icons text-white text-sm opacity-0 check-icon">check</i>
|
||||
</div>
|
||||
|
||||
<!-- Content Type Info -->
|
||||
<div class="flex items-center flex-1 min-w-0">
|
||||
<div class="p-2 <%= content_type.color %> bg-opacity-10 rounded-lg mr-3 flex-shrink-0">
|
||||
<i class="material-icons <%= content_type.text_color %> text-sm">
|
||||
<%= content_type.icon %>
|
||||
</i>
|
||||
</div>
|
||||
<span class="text-sm font-medium text-gray-900 truncate">
|
||||
<%= content_type.name.pluralize %>
|
||||
</span>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<p class="mt-4 text-xs text-gray-500">
|
||||
<i class="material-icons text-xs mr-1">info</i>
|
||||
Select the types of content that can be submitted to this collection. You can change this later.
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Privacy & Submissions Card -->
|
||||
<div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden">
|
||||
<div class="px-6 py-4 border-b border-gray-200 bg-gradient-to-r from-green-50 to-emerald-50">
|
||||
<div class="flex items-center">
|
||||
<div class="p-2 bg-green-100 rounded-lg mr-3">
|
||||
<i class="material-icons text-green-600">security</i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-900">Privacy & Submissions</h2>
|
||||
<p class="text-sm text-gray-600">Control who can see and contribute to your collection</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6 space-y-8">
|
||||
<!-- Privacy Settings -->
|
||||
<fieldset>
|
||||
<legend class="block text-sm font-medium text-gray-900 mb-4">
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">visibility</i>
|
||||
Visibility *
|
||||
</span>
|
||||
</legend>
|
||||
|
||||
<div class="space-y-3">
|
||||
<%= f.label :privacy, class: 'flex items-start p-4 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 hover:border-gray-300 transition-all duration-200' do %>
|
||||
<%= f.radio_button :privacy, 'public', class: 'mt-1 mr-3 text-green-600 focus:ring-green-500' %>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center mb-1">
|
||||
<i class="material-icons text-green-600 text-sm mr-2">public</i>
|
||||
<span class="font-medium text-gray-900">Public</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600">Anyone can discover and view this collection</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= f.label :privacy, class: 'flex items-start p-4 border border-gray-200 rounded-lg cursor-pointer hover:bg-gray-50 hover:border-gray-300 transition-all duration-200' do %>
|
||||
<%= f.radio_button :privacy, 'private', class: 'mt-1 mr-3 text-gray-600 focus:ring-gray-500' %>
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center mb-1">
|
||||
<i class="material-icons text-gray-600 text-sm mr-2">lock</i>
|
||||
<span class="font-medium text-gray-900">Private</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600">Only you can see this collection</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- Submission Settings -->
|
||||
<fieldset>
|
||||
<legend class="block text-sm font-medium text-gray-900 mb-4">
|
||||
<span class="flex items-center">
|
||||
<i class="material-icons text-gray-400 text-sm mr-2">inbox</i>
|
||||
Community Contributions
|
||||
</span>
|
||||
</legend>
|
||||
|
||||
<!-- Allow Submissions Toggle -->
|
||||
<div class="border border-gray-200 rounded-lg p-4 space-y-4">
|
||||
<%= f.label :allow_submissions, class: 'flex items-start cursor-pointer' do %>
|
||||
<%= f.check_box :allow_submissions, class: 'sr-only submission-toggle' %>
|
||||
<div class="toggle-switch mr-3 mt-0.5"></div>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-gray-900 mb-1">Allow submissions from other users</div>
|
||||
<p class="text-sm text-gray-600">Let the community suggest content for your collection</p>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Submission Options (Initially Hidden) -->
|
||||
<div id="submission-options" class="hidden pl-10 space-y-4 border-l-2 border-gray-200 ml-2">
|
||||
<!-- Submission Guidelines -->
|
||||
<div>
|
||||
<label for="submission_guidelines" class="block text-sm font-medium text-gray-900 mb-2">
|
||||
Submission Guidelines
|
||||
</label>
|
||||
<%= f.text_area :submission_guidelines,
|
||||
id: 'submission_guidelines',
|
||||
placeholder: "What kinds of pages are you looking for? Any specific themes or requirements?",
|
||||
rows: 3,
|
||||
class: 'block w-full px-4 py-3 border border-gray-300 rounded-lg shadow-sm focus:ring-2 focus:ring-green-500 focus:border-green-500 transition-colors duration-200 text-sm resize-none' %>
|
||||
<p class="mt-1 text-xs text-gray-500">
|
||||
These guidelines will be shown to users when they submit content to your collection
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Auto Accept -->
|
||||
<div class="bg-amber-50 border border-amber-200 rounded-lg p-4">
|
||||
<%= f.label :auto_accept, class: 'flex items-start cursor-pointer' do %>
|
||||
<%= f.check_box :auto_accept, class: 'mt-1 mr-3 text-amber-600 focus:ring-amber-500' %>
|
||||
<div class="flex-1">
|
||||
<div class="font-medium text-gray-900 mb-1 flex items-center">
|
||||
Auto-accept all submissions
|
||||
<i class="material-icons text-amber-500 text-sm ml-2">bolt</i>
|
||||
</div>
|
||||
<p class="text-sm text-gray-600">
|
||||
Submissions will be automatically added without review.
|
||||
<strong class="text-amber-700">Use with caution!</strong>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Actions -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 p-6 bg-white rounded-xl shadow-sm border border-gray-200">
|
||||
<div class="text-sm text-gray-600">
|
||||
<i class="material-icons text-xs mr-1">info</i>
|
||||
You can edit all of these settings later from your collection's settings page.
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-3">
|
||||
<%= link_to page_collections_path, class: "px-4 py-2 border border-gray-300 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-50 focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-all duration-200" do %>
|
||||
Cancel
|
||||
<% end %>
|
||||
|
||||
<%= f.submit 'Create Collection', class: "px-6 py-2 bg-gradient-to-r from-amber-500 to-orange-500 hover:from-amber-600 hover:to-orange-600 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white focus:ring-2 focus:ring-offset-2 focus:ring-amber-500 transition-all duration-200 inline-flex items-center" do %>
|
||||
<i class="material-icons text-sm mr-2">add</i>
|
||||
Create Collection
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<!-- Custom Styles and JavaScript -->
|
||||
<style>
|
||||
/* Custom Checkbox Styles */
|
||||
.content-type-option input[type="checkbox"]:checked + label .checkbox-custom {
|
||||
@apply bg-blue-500 border-blue-500;
|
||||
}
|
||||
|
||||
.content-type-option input[type="checkbox"]:checked + label .check-icon {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
/* Toggle Switch Styles */
|
||||
.toggle-switch {
|
||||
width: 48px;
|
||||
height: 24px;
|
||||
background-color: #e5e7eb;
|
||||
border-radius: 12px;
|
||||
position: relative;
|
||||
transition: background-color 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-switch::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.submission-toggle:checked + .toggle-switch {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
.submission-toggle:checked + .toggle-switch::after {
|
||||
transform: translateX(24px);
|
||||
}
|
||||
|
||||
/* Radio Button Custom Styles */
|
||||
input[type="radio"]:checked {
|
||||
background-color: currentColor;
|
||||
}
|
||||
|
||||
/* Character Counter */
|
||||
.char-counter {
|
||||
transition: color 0.2s;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Content Type Selection Controls
|
||||
const selectAllBtn = document.getElementById('select-all-types');
|
||||
const selectNoneBtn = document.getElementById('select-none-types');
|
||||
const selectCommonBtn = document.getElementById('select-common-types');
|
||||
const checkboxes = document.querySelectorAll('.content-type-checkbox');
|
||||
|
||||
if (selectAllBtn) {
|
||||
selectAllBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = true;
|
||||
updateCheckboxUI(checkbox);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (selectNoneBtn) {
|
||||
selectNoneBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = false;
|
||||
updateCheckboxUI(checkbox);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (selectCommonBtn) {
|
||||
selectCommonBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = checkbox.dataset.common === 'true';
|
||||
updateCheckboxUI(checkbox);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Update checkbox UI when changed
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
updateCheckboxUI(this);
|
||||
});
|
||||
// Initialize UI
|
||||
updateCheckboxUI(checkbox);
|
||||
});
|
||||
|
||||
function updateCheckboxUI(checkbox) {
|
||||
const label = checkbox.closest('label');
|
||||
const customCheckbox = label.querySelector('.checkbox-custom');
|
||||
const checkIcon = label.querySelector('.check-icon');
|
||||
|
||||
if (checkbox.checked) {
|
||||
customCheckbox.classList.add('bg-blue-500', 'border-blue-500');
|
||||
customCheckbox.classList.remove('border-gray-300');
|
||||
checkIcon.classList.remove('opacity-0');
|
||||
checkIcon.classList.add('opacity-100');
|
||||
label.classList.add('ring-2', 'ring-blue-500', 'ring-opacity-20');
|
||||
} else {
|
||||
customCheckbox.classList.remove('bg-blue-500', 'border-blue-500');
|
||||
customCheckbox.classList.add('border-gray-300');
|
||||
checkIcon.classList.add('opacity-0');
|
||||
checkIcon.classList.remove('opacity-100');
|
||||
label.classList.remove('ring-2', 'ring-blue-500', 'ring-opacity-20');
|
||||
}
|
||||
}
|
||||
|
||||
// Submission Toggle
|
||||
const submissionToggle = document.querySelector('.submission-toggle');
|
||||
const submissionOptions = document.getElementById('submission-options');
|
||||
|
||||
if (submissionToggle && submissionOptions) {
|
||||
submissionToggle.addEventListener('change', function() {
|
||||
if (this.checked) {
|
||||
submissionOptions.classList.remove('hidden');
|
||||
} else {
|
||||
submissionOptions.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize state
|
||||
if (submissionToggle.checked) {
|
||||
submissionOptions.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
// Character Counter for Description
|
||||
const descriptionField = document.querySelector('textarea[name="page_collection[description]"]');
|
||||
const charCounter = document.getElementById('char-count');
|
||||
|
||||
if (descriptionField && charCounter) {
|
||||
function updateCharCount() {
|
||||
const count = descriptionField.value.length;
|
||||
charCounter.textContent = `${count} / 500`;
|
||||
|
||||
if (count > 400) {
|
||||
charCounter.classList.add('text-amber-600');
|
||||
} else if (count > 450) {
|
||||
charCounter.classList.add('text-red-600');
|
||||
charCounter.classList.remove('text-amber-600');
|
||||
} else {
|
||||
charCounter.classList.remove('text-amber-600', 'text-red-600');
|
||||
}
|
||||
}
|
||||
|
||||
descriptionField.addEventListener('input', updateCharCount);
|
||||
updateCharCount(); // Initialize
|
||||
}
|
||||
|
||||
// File Upload Enhancement
|
||||
const fileInput = document.querySelector('input[type="file"]');
|
||||
const uploadArea = fileInput?.closest('.border-dashed');
|
||||
|
||||
if (fileInput && uploadArea) {
|
||||
// Drag and Drop
|
||||
uploadArea.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
this.classList.add('border-blue-400', 'bg-blue-50');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('dragleave', function(e) {
|
||||
e.preventDefault();
|
||||
this.classList.remove('border-blue-400', 'bg-blue-50');
|
||||
});
|
||||
|
||||
uploadArea.addEventListener('drop', function(e) {
|
||||
e.preventDefault();
|
||||
this.classList.remove('border-blue-400', 'bg-blue-50');
|
||||
|
||||
const files = e.dataTransfer.files;
|
||||
if (files.length > 0) {
|
||||
fileInput.files = files;
|
||||
// Optionally show preview
|
||||
showImagePreview(files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', function() {
|
||||
if (this.files && this.files[0]) {
|
||||
showImagePreview(this.files[0]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showImagePreview(file) {
|
||||
if (file.type.startsWith('image/')) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
// You could add preview functionality here
|
||||
console.log('Image selected:', file.name);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@ -1,3 +1,37 @@
|
||||
<h1 style="font-size: 2em">Create a Collection</h1>
|
||||
|
||||
<%= render 'form', page_collection: @page_collection %>
|
||||
<!-- Main Container -->
|
||||
<div class="min-h-screen bg-gray-50">
|
||||
|
||||
<!-- Hero Section -->
|
||||
<div class="bg-gradient-to-br from-amber-500 via-orange-500 to-red-500 text-white">
|
||||
<div class="max-w-7xl mx-auto px-6 py-12">
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<nav class="mb-8">
|
||||
<div class="flex items-center space-x-2 text-white/80 text-sm">
|
||||
<%= link_to "Collections", page_collections_path, class: "hover:text-white transition-colors duration-200" %>
|
||||
<i class="material-icons text-xs">chevron_right</i>
|
||||
<span class="text-white font-medium">Create New Collection</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Header Content -->
|
||||
<div class="text-center">
|
||||
<div class="mb-6">
|
||||
<div class="inline-flex items-center justify-center w-20 h-20 bg-white/20 rounded-full backdrop-blur-sm mb-4">
|
||||
<i class="material-icons text-white text-4xl">collections</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1 class="text-4xl font-bold text-white mb-4">Create a Collection</h1>
|
||||
<p class="text-xl text-white/90 max-w-2xl mx-auto">
|
||||
Curate and organize your favorite content into beautiful, shareable collections that others can discover and contribute to.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Content -->
|
||||
<div class="max-w-4xl mx-auto px-6 py-8">
|
||||
<%= render 'form', page_collection: @page_collection %>
|
||||
</div>
|
||||
</div>
|
||||
Loading…
Reference in New Issue
Block a user