mirror of
https://github.com/indentlabs/notebook.git
synced 2025-10-26 11:19:22 +00:00
better category and field creation
This commit is contained in:
parent
5b58364c97
commit
9299699f4b
@ -9,5 +9,9 @@ $(document).ready ->
|
||||
window.scrollTo(0, 0);
|
||||
), 1
|
||||
|
||||
$('.new-attribute-field-link').click (e) ->
|
||||
e.preventDefault()
|
||||
$("#attribute-field-modal").openModal()
|
||||
|
||||
$('.share').click ->
|
||||
$('#share-modal').openModal()
|
||||
|
||||
@ -29,4 +29,4 @@ p.long-form {
|
||||
|
||||
.content-field {
|
||||
min-height: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,31 @@
|
||||
# Controller for the Attribute model
|
||||
class AttributeFieldsController < ContentController
|
||||
|
||||
def create
|
||||
initialize_object
|
||||
|
||||
if @content.save
|
||||
successful_response(:back, t(:create_success, model_name: humanized_model_name))
|
||||
else
|
||||
failed_response('new', :unprocessable_entity)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_object
|
||||
category = current_user.attribute_categories.where(label: content_params[:attribute_category]).first_or_initialize.tap do |c|
|
||||
c.entity_type = params[:entity_type]
|
||||
c.save!
|
||||
end
|
||||
|
||||
@content = AttributeField.new(label: content_params[:label]).tap do |f|
|
||||
f.attribute_category_id = category.id
|
||||
f.user_id = current_user.id
|
||||
f.field_type = 'textearea'
|
||||
end
|
||||
end
|
||||
|
||||
def content_params
|
||||
params.require(:attribute_field).permit(content_param_list)
|
||||
end
|
||||
@ -9,7 +33,7 @@ class AttributeFieldsController < ContentController
|
||||
def content_param_list
|
||||
[
|
||||
:universe_id, :user_id,
|
||||
:attribute_category_id,
|
||||
:attribute_category,
|
||||
:name, :field_type,
|
||||
:label, :description
|
||||
]
|
||||
|
||||
@ -8,7 +8,7 @@ module AttributesHelper
|
||||
end
|
||||
end
|
||||
|
||||
link = content_tag(:a, category.label, href: "##{category.name}_panel")
|
||||
link = content_tag(:a, category.label, href: "##{category.name.gsub("'", '')}_panel")
|
||||
content_tag(:li, link, class: "tab col s3 #{is_disabled}")
|
||||
end
|
||||
end
|
||||
|
||||
15
app/helpers/content_helper.rb
Normal file
15
app/helpers/content_helper.rb
Normal file
@ -0,0 +1,15 @@
|
||||
module ContentHelper
|
||||
def new_content_button(content)
|
||||
icon = content_tag(:i, content.icon, class: "material-icons #{content.color}-text")
|
||||
link = link_to("+ #{icon}".html_safe, new_polymorphic_path(content.build), class: "btn white #{content.color}-text")
|
||||
|
||||
content_tag(:small, link, class: 'right')
|
||||
end
|
||||
|
||||
def content_settings_button(content)
|
||||
icon = content_tag(:i, 'chrome_reader_mode', class: "material-icons #{content.color}-text")
|
||||
link = link_to("+ #{icon}".html_safe, new_attribute_category_for_entity_path(content.content_name), class: "btn white #{content.color}-text")
|
||||
|
||||
content_tag(:small, link, class: 'right')
|
||||
end
|
||||
end
|
||||
@ -1,19 +1,33 @@
|
||||
class AttributeCategory < ActiveRecord::Base
|
||||
validates :name, presence: true
|
||||
|
||||
belongs_to :user
|
||||
has_many :attribute_fields
|
||||
|
||||
include HasAttributes
|
||||
include Serendipitous::Concern
|
||||
|
||||
before_validation :ensure_name
|
||||
|
||||
def self.color
|
||||
'amber'
|
||||
end
|
||||
|
||||
def self.icon
|
||||
'chrome_reader_mode'
|
||||
'tab'
|
||||
end
|
||||
|
||||
def self.content_name
|
||||
'attribute_category'
|
||||
end
|
||||
|
||||
def icon
|
||||
self['icon'] || self.class.icon
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_name
|
||||
self.name ||= "#{label}-#{Time.now.to_i}".underscore.gsub(' ', '_')
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
class AttributeField < ActiveRecord::Base
|
||||
validates :name, presence: true
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :attribute_category
|
||||
has_many :attribute_values, class_name: 'Attribute'
|
||||
@ -8,6 +10,8 @@ class AttributeField < ActiveRecord::Base
|
||||
|
||||
attr_accessor :system
|
||||
|
||||
before_validation :ensure_name
|
||||
|
||||
scope :is_public, -> { eager_load(:universe).where('universes.privacy = ? OR attribute_fields.privacy = ?', 'public', 'public') }
|
||||
|
||||
def self.color
|
||||
@ -22,10 +26,6 @@ class AttributeField < ActiveRecord::Base
|
||||
'attribute'
|
||||
end
|
||||
|
||||
def name
|
||||
(self['name'] || "custom field #{Time.now.to_i}").downcase.gsub(' ','_')
|
||||
end
|
||||
|
||||
def humanize
|
||||
label
|
||||
end
|
||||
@ -37,4 +37,10 @@ class AttributeField < ActiveRecord::Base
|
||||
def system?
|
||||
!!self.system
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_name
|
||||
self.name ||= "#{label}-#{Time.now.to_i}".underscore.gsub(' ', '_')
|
||||
end
|
||||
end
|
||||
|
||||
@ -9,13 +9,13 @@ module HasAttributes
|
||||
|
||||
def self.attribute_categories(user = nil)
|
||||
categories = YAML.load_file(Rails.root.join('config', 'attributes', "#{content_name}.yml")).map do |category_name, details|
|
||||
category = AttributeCategory.new(entity_type: self.name, name: category_name.to_s, label: details[:label], icon: details[:icon])
|
||||
category = AttributeCategory.new(entity_type: self.content_name, name: category_name.to_s, label: details[:label], icon: details[:icon])
|
||||
category.attribute_fields << details[:attributes].map { |field| AttributeField.new(field.merge(system: true)) }
|
||||
category
|
||||
end
|
||||
|
||||
return categories if user.nil?
|
||||
[categories, user.attribute_categories.joins(:attribute_fields).where(['attribute_categories.entity_type = ?', content_name])].flatten.uniq
|
||||
[categories, user.attribute_categories.where(['attribute_categories.entity_type = ?', content_name]).includes(:attribute_fields)].flatten.uniq
|
||||
end
|
||||
|
||||
def update_custom_attributes
|
||||
|
||||
37
app/views/attribute_fields/_modal.html.erb
Normal file
37
app/views/attribute_fields/_modal.html.erb
Normal file
@ -0,0 +1,37 @@
|
||||
<div id="attribute-field-modal" class="modal">
|
||||
<%= form_for AttributeField.new do |f| %>
|
||||
<%= hidden_field_tag :entity_type, content.class.content_name %>
|
||||
<div class="modal-content">
|
||||
<h4>New attribute field</h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="input-field">
|
||||
<%= f.label :attribute_category, 'Category' %><br />
|
||||
<%= f.text_area :attribute_category, class: "materialize-textarea autocomplete" %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<%= f.text_area :label, class: "materialize-textarea", placeholder: "Field Name" %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<%= f.submit "Create field", class: "btn waves-effect waves-green" %>
|
||||
<a href="#!" class=" modal-action modal-close waves-effect waves-green btn-flat">close</a>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#attribute_field_attribute_category').autocomplete({
|
||||
data: {
|
||||
<% current_user.attribute_categories.where(entity_type: content.class.content_name).each do |category| %>
|
||||
"<%= category.label %>": null,
|
||||
<% end %>
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<ul class="hoverable tabs hide-on-small-only">
|
||||
<% content.class.attribute_categories(current_user).each do |category| %>
|
||||
<li class="tab col s12">
|
||||
<a href="#<%= category.name %>_panel">
|
||||
<a href="#<%= category.name.gsub("'", '') %>_panel">
|
||||
<i class="material-icons hide-on-med-and-down" style="font-size: 18px; position: relative; top: 3px;"><%= category.icon %></i>
|
||||
<%= category.label %>
|
||||
</a>
|
||||
@ -28,7 +28,7 @@
|
||||
<ul class="tabs hide-on-med-and-up">
|
||||
<% content.class.attribute_categories(current_user).each do |category| %>
|
||||
<li class="tab col s12">
|
||||
<a href="#<%= category.name %>_panel" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="<%= category.label %>">
|
||||
<a href="#<%= category.name.gsub("'", '') %>_panel" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="<%= category.label %>">
|
||||
<i class="material-icons" style="font-size: 18px; position: relative; top: 3px;"><%= category.icon %></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@ -31,7 +31,7 @@ set_meta_tags title: content.name, description: content.description
|
||||
</ul>
|
||||
|
||||
<% categories.each do |category| %>
|
||||
<div id="<%= category.name %>_panel" class="row">
|
||||
<div id="<%= category.name.gsub("'", '') %>_panel" class="row">
|
||||
<% category.attribute_fields.each do |attribute| %>
|
||||
<% next if attribute.name.start_with?("private") && @content.user != current_user %>
|
||||
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
<% if content.persisted? %>
|
||||
<a class='dropdown-button black-text' href='#' data-activates='options-menu' data-constrainwidth='false'>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</a>
|
||||
<% end %>
|
||||
<a class='dropdown-button black-text' href='#' data-activates='options-menu' data-constrainwidth='false'>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</a>
|
||||
|
||||
<ul id='options-menu' class='dropdown-content'>
|
||||
<!-- Broken on content edit pages
|
||||
<li><a href="#!" class="share">Share this <%= content.class.to_s.downcase %></a></li>
|
||||
<li class="divider"></li>
|
||||
-->
|
||||
<li>
|
||||
<%= link_to "Create new attribute", '#attr-modal', class: 'black-text new-attribute-field-link' %>
|
||||
</li>
|
||||
|
||||
<% if content.persisted? %>
|
||||
<!-- Broken on content edit pages
|
||||
<li><a href="#!" class="share">Share this <%= content.class.to_s.downcase %></a></li>
|
||||
<li class="divider"></li>
|
||||
-->
|
||||
|
||||
<li>
|
||||
<%= link_to "Delete this #{content.class.content_name.humanize.downcase} forever", content,
|
||||
:class => 'red-text',
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
<div id="<%= category.name %>_panel" class="row">
|
||||
<% category.attribute_fields.each do |field| %>
|
||||
|
||||
<% # Do some dynamic resizing of columns based on how many columns there are
|
||||
# TODO: move this into some service or something? Dunno, doesn't belong here.
|
||||
<div id="<%= category.name.gsub("'", '') %>_panel" class="row">
|
||||
<% # Do some dynamic resizing of columns based on how many columns there are
|
||||
# TODO: move this into some service or something? Dunno, doesn't belong here.
|
||||
s_width = 12
|
||||
m_width = 6
|
||||
l_width = 4
|
||||
if category.attribute_fields.length == 1
|
||||
# If there's only one field on this tab, go full-width on all screen sizes
|
||||
s_width = m_width = l_width = 12
|
||||
elsif category.attribute_fields.length == 2
|
||||
# If there's two fields on this tab, go half-width on medium- and large-screens
|
||||
s_width = 12
|
||||
m_width = 6
|
||||
l_width = 4
|
||||
if category.attribute_fields.length == 1
|
||||
# If there's only one field on this tab, go full-width on all screen sizes
|
||||
s_width = m_width = l_width = 12
|
||||
elsif category.attribute_fields.length == 2
|
||||
# If there's two fields on this tab, go half-width on medium- and large-screens
|
||||
s_width = 12
|
||||
m_width = l_width = 6
|
||||
elsif category.attribute_fields.length > 2
|
||||
# If there's at least 3 fields, use the defaults (detailed above)
|
||||
end
|
||||
%>
|
||||
m_width = l_width = 6
|
||||
elsif category.attribute_fields.length > 2
|
||||
# If there's at least 3 fields, use the defaults (detailed above)
|
||||
end
|
||||
%>
|
||||
|
||||
<% category.attribute_fields.each do |field| %>
|
||||
<div class="col <%= "s#{s_width} m#{m_width} l#{l_width}" %>">
|
||||
|
||||
<% value = nil %>
|
||||
@ -32,7 +31,7 @@
|
||||
<% through_class = content.class.reflect_on_association(field.name).options[:through].to_s %>
|
||||
<%= render 'content/form/relation_input', f: f, attribute: field.name, relation: through_class %>
|
||||
|
||||
<% elsif attribute == 'archetype' %>
|
||||
<% elsif field.name == 'archetype' %>
|
||||
<div class="input-field input-select">
|
||||
<%= f.label field.name, class: 'active' %><br />
|
||||
<%= f.select field.name, options_for_select(t('archetypes'), selected: f.object.archetype), include_blank: true %>
|
||||
@ -57,10 +56,7 @@
|
||||
</div>
|
||||
|
||||
<% elsif field.name == 'entity_type' %>
|
||||
<div class="input-field">
|
||||
<%= f.label field.name, field.label %><br />
|
||||
<%= f.select field.name, %w(Character Item Location Universe).map { |tf| [tf, tf.downcase] } %>
|
||||
</div>
|
||||
<%= f.hidden_field :entity_type, value: f.object.entity_type %>
|
||||
|
||||
<% elsif field.name == 'privacy' %>
|
||||
<div class="input-field">
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
<%
|
||||
class_name = f.object.class.name.downcase
|
||||
value = content.send(field.name.to_sym)
|
||||
content_name = f.object.class.content_name
|
||||
|
||||
field_id = "#{content_name}_#{field.name}"
|
||||
value = nil
|
||||
if f.object.respond_to?(field.name.to_sym)
|
||||
value = f.object.send(field.name.to_sym)
|
||||
else
|
||||
value = Attribute.where(user: current_user, attribute_field: field, entity: f.object).first
|
||||
end
|
||||
|
||||
# TODO: Enable autocomplete when we can actually hide the dropdown if someone tabs out of a field.
|
||||
# Not the easiest thing in the world to do, apparently.
|
||||
@ -9,9 +16,9 @@
|
||||
<div class="input-field content-field">
|
||||
<%= f.label field.name, field.label %>
|
||||
<%
|
||||
placeholder = I18n.translate "attributes.#{class_name}.#{field.name}",
|
||||
placeholder = I18n.translate "attributes.#{content_name}.#{field.name}",
|
||||
scope: :serendipitous_questions,
|
||||
name: f.object.send('name') || "this #{class_name}",
|
||||
name: f.object.send('name') || "this #{content_name}",
|
||||
attribute: "test",
|
||||
default: 'Write as little or as much as you want!'
|
||||
%>
|
||||
@ -19,15 +26,15 @@
|
||||
<% if field.system? %>
|
||||
<%= f.text_area field.name, value: value, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: placeholder %>
|
||||
<% else %>
|
||||
<%= hidden_field_tag "#{content.content_name}[custom_attributes][][name]", field.name %>
|
||||
<%= text_area_tag "#{content.content_name}[custom_attributes][][value]", value, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: placeholder %>
|
||||
<%= hidden_field_tag "#{content_name}[custom_attribute_values][][name]", field.name %>
|
||||
<%= text_area_tag "#{content_name}[custom_attribute_values][][value]", value && value.value, class: "materialize-textarea #{defined?(autocomplete) && false ? 'autocomplete' : ''}", placeholder: placeholder %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if defined?(autocomplete) && false %>
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#<%= "#{content.content_name}_#{field.name}" %>').autocomplete({
|
||||
$('#<%= "#{content_name}_#{field.name}" %>').autocomplete({
|
||||
data: {
|
||||
<% autocomplete.each do |autocomplete_option| %>
|
||||
"<%= autocomplete_option %>": null,
|
||||
|
||||
@ -2,5 +2,5 @@
|
||||
<%= content.name %>
|
||||
<% else %>
|
||||
<%- content_name = content.class.respond_to?(:content_name) ? content.class.content_name: content.class.to_s %>
|
||||
Create your new <%= content_name.downcase %>
|
||||
Create your new <%= content_name.humanize.downcase %>
|
||||
<% end %>
|
||||
|
||||
@ -9,17 +9,9 @@
|
||||
<% if @content.any? %>
|
||||
<h4>
|
||||
You've created <%= pluralize(@content.count, @content.content_name) %>
|
||||
<% if @content.count > 0 %>
|
||||
<small class="right">
|
||||
<%= link_to new_polymorphic_path(@content.build), class: "btn white #{content_type_class.color}-text" do %>
|
||||
+
|
||||
<i class="material-icons <%= content_type_class.color %>-text">
|
||||
<%= content_type_class.icon %>
|
||||
</i>
|
||||
<% end %>
|
||||
</small>
|
||||
<% end %>
|
||||
<%= new_content_button(@content) if @content.count > 0 %>
|
||||
</h4>
|
||||
|
||||
<%= render partial: 'content/list/list', locals: { content_list: @content } %>
|
||||
<%= link_to "Create another #{@content.content_name}", new_polymorphic_path(@content.build), :class => 'btn' %>
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -17,9 +17,6 @@
|
||||
<li><%= link_to 'Your characters', characters_path %></li>
|
||||
<li><%= link_to 'Your locations', locations_path %></li>
|
||||
<li><%= link_to 'Your items', items_path %></li>
|
||||
<li><%= link_to 'Your attribute categories', attribute_categories_path %></li>
|
||||
|
||||
<li><%= link_to 'Your attributes', attribute_fields_path %></li>
|
||||
<li class="divider"></li>
|
||||
<li><%= link_to 'Your author profile', current_user if current_user %></li>
|
||||
<li><%= link_to 'Account settings', edit_user_registration_path %></li>
|
||||
@ -80,16 +77,6 @@
|
||||
<i class="material-icons">beach_access</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<%= attribute_categories_url %>" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="Attribute Categories">
|
||||
<i class="material-icons">chrome_reader_mode</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<%= attribute_fields_url %>" class="tooltipped" data-position="bottom" data-delay="100" data-tooltip="Attributes">
|
||||
<i class="material-icons">assignment</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-button tooltipped" href="#!" data-activates="desktop-user-dropdown" data-position="bottom" data-delay="100" data-tooltip="You">
|
||||
<i class="material-icons">person</i>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -3,3 +3,4 @@
|
||||
<% end %>
|
||||
|
||||
<%= render partial: 'content/share', locals: { shared_content: @content} %>
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
<%= form_for @content do |form| %>
|
||||
<%= render partial: 'content/form', locals: { f: form, content: @content } %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= render 'attribute_fields/modal', content: @content %>
|
||||
|
||||
@ -2,11 +2,7 @@
|
||||
:label: General
|
||||
:icon: info
|
||||
:attributes:
|
||||
- :name: label
|
||||
:label: Name
|
||||
- :name: entity_type
|
||||
:label: Entity Type
|
||||
- :name: name
|
||||
:label: Name
|
||||
- :name: label
|
||||
:label: Label
|
||||
- :name: icon
|
||||
:label: Icon
|
||||
|
||||
Loading…
Reference in New Issue
Block a user