aboutsummaryrefslogtreecommitdiff
path: root/app/views
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <msabate@suse.com>2024-03-13 18:03:43 +0100
committerMiquel Sabaté Solà <msabate@suse.com>2024-03-13 18:03:43 +0100
commita7791635aa98f012d35f97c3c5288494bbb28e38 (patch)
treebad987e6b4d10ad2f7cd02e40658d464907595ee /app/views
downloadoperum-a7791635aa98f012d35f97c3c5288494bbb28e38.tar.gz
operum-a7791635aa98f012d35f97c3c5288494bbb28e38.zip
Initial commit
Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
Diffstat (limited to 'app/views')
-rw-r--r--app/views/active_storage/blobs/_blob.html.erb14
-rw-r--r--app/views/comments/_comment.html.erb42
-rw-r--r--app/views/comments/_list.html.erb30
-rw-r--r--app/views/exports/new.html.erb14
-rw-r--r--app/views/exports/show.csv.erb1
-rw-r--r--app/views/exports/show.uoc.erb7
-rw-r--r--app/views/layouts/_errors.html.erb13
-rw-r--r--app/views/layouts/action_text/contents/_content.html.erb3
-rw-r--r--app/views/layouts/application.html.erb78
-rw-r--r--app/views/licenses/show.html.erb23
-rw-r--r--app/views/searches/_form.html.erb29
-rw-r--r--app/views/searches/_search.html.erb5
-rw-r--r--app/views/searches/edit.html.erb1
-rw-r--r--app/views/searches/index.html.erb1
-rw-r--r--app/views/searches/new.html.erb1
-rw-r--r--app/views/searches/show.html.erb1
-rw-r--r--app/views/sessions/new.html.erb21
-rw-r--r--app/views/shared_searches/index.html.erb13
-rw-r--r--app/views/shared_searches/show.html.erb28
-rw-r--r--app/views/tags/_form.html.erb12
-rw-r--r--app/views/tags/_tag.html.erb8
-rw-r--r--app/views/tags/_taggable.html.erb17
-rw-r--r--app/views/tags/index.html.erb13
-rw-r--r--app/views/tags/new.html.erb9
-rw-r--r--app/views/things/_form.html.erb102
-rw-r--r--app/views/things/_thing.html.erb7
-rw-r--r--app/views/things/edit.html.erb3
-rw-r--r--app/views/things/new.html.erb1
-rw-r--r--app/views/things/show.html.erb7
29 files changed, 504 insertions, 0 deletions
diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb
new file mode 100644
index 0000000..49ba357
--- /dev/null
+++ b/app/views/active_storage/blobs/_blob.html.erb
@@ -0,0 +1,14 @@
+<figure class="attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
+ <% if blob.representable? %>
+ <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %>
+ <% end %>
+
+ <figcaption class="attachment__caption">
+ <% if caption = blob.try(:caption) %>
+ <%= caption %>
+ <% else %>
+ <span class="attachment__name"><%= blob.filename %></span>
+ <span class="attachment__size"><%= number_to_human_size blob.byte_size %></span>
+ <% end %>
+ </figcaption>
+</figure>
diff --git a/app/views/comments/_comment.html.erb b/app/views/comments/_comment.html.erb
new file mode 100644
index 0000000..8c0975e
--- /dev/null
+++ b/app/views/comments/_comment.html.erb
@@ -0,0 +1,42 @@
+<div id="<%= dom_id(comment) %>" class="comment">
+ <div class="comment-header">
+ <div class="comment-header-title">
+ <% if defined?(idx) %>
+ <span title="Commented on <%= comment.created_at %>"><b><%= I18n.t('comments.title') %></b> #<%= idx + 1 %></span>
+ <% else %>
+ <span title="Commented on <%= comment.created_at %>"><b><%= I18n.t('comments.title') %></b> <%= I18n.t('general.in') %> <%= link_to comment.thing.title, thing_path(comment.thing) %></span>
+ <% end %>
+ </div>
+
+ <div class="comment-header-actions">
+ <% if defined?(from) %>
+ <a title="<%= I18n.t('comments.edit-title') %>" href="#" class="edit-comment" data-action="comment#toggle"><%= I18n.t('general.edit').capitalize %></a>
+ <%= link_to I18n.t('general.delete').capitalize, thing_comment_path(comment.thing, comment, from: from), data: { "turbo-method": :delete }, title: I18n.t('comments.delete-title'), class: 'delete-comment' %>
+ <% end %>
+ </div>
+ </div>
+
+ <div class="comment-body">
+ <%= comment.content %>
+ </div>
+
+ <% if comment.tag_references.any? %>
+ <div class="comment-footer">
+ <small><b><%= I18n.t('tags.title') %></b>: <%= comment.tags.order(:name).pluck(:name).join(", ") %></small>
+ </div>
+ <% end %>
+
+ <% if defined?(from) %>
+ <div class="comment-edit-body hidden">
+ <%= form_with model: [comment.thing, comment] do |form| %>
+ <%= form.hidden_field :from, value: from %>
+ <%= form.rich_text_area :content, autofocus: true %>
+
+ <%= render "tags/taggable", tag_references: comment.tag_references.pluck(:tag_id), form: form %>
+
+ <%= form.submit %>
+ <span><%= I18n.t('general.or') %> <a title="Cancel editing of comment" href="#" data-action="comment#toggle"><%= I18n.t('general.cancel') %></a></span>
+ <% end %>
+ </div>
+ <% end %>
+</div>
diff --git a/app/views/comments/_list.html.erb b/app/views/comments/_list.html.erb
new file mode 100644
index 0000000..2396140
--- /dev/null
+++ b/app/views/comments/_list.html.erb
@@ -0,0 +1,30 @@
+<div id="comment-list" data-controller="comment">
+ <h5><%= I18n.t('comments.title-plural') %></h5>
+
+ <div>
+ <% if thing.comments.any? %>
+ <% thing.comments.each_with_index do |comment, idx| %>
+ <%= render comment, from: from, idx: idx %>
+ <% end %>
+ <% else %>
+ <p><%= I18n.t('comments.none') %>.</p>
+ <% end %>
+ </div>
+
+ <div id="comment-new-section">
+ <a id="comment-new-button" class="button" href="#" data-action="comment#toggleNewComment"><%= I18n.t('comments.new') %></a>
+
+ <div id="comment-new-body" class="hidden">
+ <%= form_with model: [thing, Comment.new] do |form| %>
+ <%= form.rich_text_area :content, size: "20x5", autofocus: true %>
+
+ <%= form.hidden_field :from, value: from %>
+
+ <%= render "tags/taggable", tag_references: [], form: form %>
+
+ <%= form.submit %>
+ <span><%= I18n.t('general.or') %> <a title="<%= I18n.t('general.cancel') %>" href="#" data-action="comment#toggleNewComment"><%= I18n.t('general.cancel') %></a></span>
+ <% end %>
+ </div>
+ </div>
+</div>
diff --git a/app/views/exports/new.html.erb b/app/views/exports/new.html.erb
new file mode 100644
index 0000000..73481a7
--- /dev/null
+++ b/app/views/exports/new.html.erb
@@ -0,0 +1,14 @@
+<div class="notice"><b><%= I18n.t('searches.export.note') %></b>: <%= I18n.t('searches.export.note-msg') %>.</div>
+
+<div data-controller="export">
+ <div id="errors"></div>
+
+ <input type="hidden" data-export-target="id" value="<%= @search.id %>" />
+
+ <select id="export-select-format" data-action="export#change">
+ <option selected="selected" value="csv">CSV</option>
+ <option value="uoc">UOC</option>
+ </select>
+
+ <a id="export-format-button" class="button" href="#">Export</a>
+</div>
diff --git a/app/views/exports/show.csv.erb b/app/views/exports/show.csv.erb
new file mode 100644
index 0000000..43c7b82
--- /dev/null
+++ b/app/views/exports/show.csv.erb
@@ -0,0 +1 @@
+<%= CsvExporter.new(things: @results).export.html_safe %>
diff --git a/app/views/exports/show.uoc.erb b/app/views/exports/show.uoc.erb
new file mode 100644
index 0000000..b8f73b1
--- /dev/null
+++ b/app/views/exports/show.uoc.erb
@@ -0,0 +1,7 @@
+\chapter{<%= I18n.t('searches.export.chapter') %>}
+
+{\setlength{\parskip}{-0.3cm}
+
+<%= UocExporter.new(things: @results.order(:authors)).export %>
+
+}
diff --git a/app/views/layouts/_errors.html.erb b/app/views/layouts/_errors.html.erb
new file mode 100644
index 0000000..9e5ce38
--- /dev/null
+++ b/app/views/layouts/_errors.html.erb
@@ -0,0 +1,13 @@
+<% if model.errors.size.positive? %>
+ <div class="notice">
+ <% if model.errors.size == 1 %>
+ <span><%= model.errors.first.full_message %>.</span>
+ <% else %>
+ <ul>
+ <% model.errors.each do |error| %>
+ <li><%= error.full_message %>.</li>
+ <% end %>
+ </ul>
+ <% end %>
+ </div>
+<% end %>
diff --git a/app/views/layouts/action_text/contents/_content.html.erb b/app/views/layouts/action_text/contents/_content.html.erb
new file mode 100644
index 0000000..9e3c0d0
--- /dev/null
+++ b/app/views/layouts/action_text/contents/_content.html.erb
@@ -0,0 +1,3 @@
+<div class="trix-content">
+ <%= yield -%>
+</div>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
new file mode 100644
index 0000000..6042f43
--- /dev/null
+++ b/app/views/layouts/application.html.erb
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title><%= ENV.fetch('OPERUM_BASE_TITLE', 'Operum') %></title>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <%= csrf_meta_tags %>
+ <%= csp_meta_tag %>
+
+ <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+ <%= javascript_importmap_tags %>
+ </head>
+
+ <body>
+ <% if current_user && @searches %>
+ <header data-controller="header">
+ <nav>
+ <a class="<%= 'current' if current_user.last_search_id.nil? %>" href="/"><%= I18n.t('searches.home') %></a>
+ <% Search.find_each do |s| %>
+ <a class="<%= 'current' if current_user.last_search_id == s.id %>" href="<%= search_path(s) %>"><%= s.name %></a>
+ <% end %>
+ <a id="toggle-hidden-global-menu" href="#" data-action="header#toggle"><i id="header-chevron" class="gg-chevron-down"></i></a>
+ </nav>
+
+ <div id="hidden-nav" class="hidden">
+ <div class="flex-wrap">
+ <span><%= I18n.t('general.create').capitalize %>: </span>
+ <%= link_to I18n.t('searches.object'), new_search_path %>
+ <%= link_to I18n.t('things.object'), new_thing_path %>
+ </div>
+
+ <div class="flex-wrap">
+ <span><%= I18n.t('searches.title') %>: </span>
+ <% if @search&.id %>
+ <%= link_to I18n.t('general.edit'), edit_search_path(@search) %>
+ <%= link_to I18n.t('general.delete'), search_path(@search), data: { turbo_confirm: I18n.t('general.sure'), turbo_method: :delete } %>
+ <% end %>
+
+ <%= link_to I18n.t('searches.export.action'), new_search_exports_path(current_user.last_search_id ? current_user.last_search_id : '0') %>
+ </div>
+
+ <div class="flex-wrap">
+ <span><%= I18n.t('general.list').capitalize %>: </span>
+ <%= link_to I18n.t('tags.title').downcase, tags_path %>
+ <%= link_to I18n.t('searches.shared.title').downcase, shared_searches_path %>
+ </div>
+ </div>
+ </header>
+ <% end %>
+
+ <main>
+ <% if flash[:notice] %>
+ <div class="notice">
+ <span><%= flash[:notice] %>.</span>
+ </div>
+ <% end %>
+
+ <% if flash[:alert] %>
+ <div class="notice">
+ <span><%= flash[:alert] %>.</span>
+ </div>
+ <% end %>
+
+ <%= yield %>
+ </main>
+
+ <footer>
+ <p>
+ <%= I18n.t('layout.created-by') %> <a href="http://jo.mssola.com/">Miquel Sabaté Solà</a>
+ <br>
+ <%= I18n.t('layout.this-page-license') %> <a href="/license">AGPLv3</a> (<a href="https://github.com/mssola/operum"><%= I18n.t('layout.source-code') %></a>)
+ <% if current_user %>
+ <br>
+ <%= link_to I18n.t('sessions.sign-out'), sessions_path, data: { turbo_method: :delete } %>
+ <% end %>
+ </p>
+ </footer>
+ </body>
+</html>
diff --git a/app/views/licenses/show.html.erb b/app/views/licenses/show.html.erb
new file mode 100644
index 0000000..7687d54
--- /dev/null
+++ b/app/views/licenses/show.html.erb
@@ -0,0 +1,23 @@
+<h3><%= t('license.title') %> <a href="https://www.gnu.org/licenses/agpl-3.0.html">AGPLv3</a></h3>
+
+<p><%= t('license.at') %> <a href="https://github.com/mssola/operum"><%= t('license.here') %></a>.</p>
+
+<blockquote>
+<pre>
+Copyright (C) 2023-Ω Miquel Sabaté Solà &lt;mikisabate@gmail.com&gt;
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see &lt;https://www.gnu.org/licenses/&gt;.
+</pre>
+
+</blockquote>
diff --git a/app/views/searches/_form.html.erb b/app/views/searches/_form.html.erb
new file mode 100644
index 0000000..c940fe2
--- /dev/null
+++ b/app/views/searches/_form.html.erb
@@ -0,0 +1,29 @@
+<div data-controller="search">
+ <%= form_with(model: search) do |form| %>
+ <%= render "layouts/errors", model: search %>
+
+ <div id="search_form">
+ <div id="search-form-name" class="hidden">
+ <%= form.label :name %>
+ <%= form.text_field :name, required: true, autocomplete: 'off' %>
+ </div>
+
+ <div>
+ <%= form.text_field :body, "data-action": "keydown.enter->search#load", "data-search-target": "body", placeholder: 'Search body' %>
+ <a id="save-search" href="#" data-action="search#save" class="hidden"><%= I18n.t('searches.save') %></a>
+ </div>
+
+ <div id="search-form-shared" class="hidden">
+ <%= form.check_box :shared %>
+ <%= form.label :shared, I18n.t('searches.public') %>
+ </div>
+
+ <div id="search-form-submit" class="hidden">
+ <%= form.submit %>
+ </div>
+ </div>
+ <% end %>
+
+ <div id="results">
+ </div>
+</div>
diff --git a/app/views/searches/_search.html.erb b/app/views/searches/_search.html.erb
new file mode 100644
index 0000000..a717a22
--- /dev/null
+++ b/app/views/searches/_search.html.erb
@@ -0,0 +1,5 @@
+<div id="items">
+ <% items.values.flatten.each do |item| %>
+ <%= render item %>
+ <% end %>
+</div>
diff --git a/app/views/searches/edit.html.erb b/app/views/searches/edit.html.erb
new file mode 100644
index 0000000..2702224
--- /dev/null
+++ b/app/views/searches/edit.html.erb
@@ -0,0 +1 @@
+<%= render "form", search: @search %>
diff --git a/app/views/searches/index.html.erb b/app/views/searches/index.html.erb
new file mode 100644
index 0000000..b7d3854
--- /dev/null
+++ b/app/views/searches/index.html.erb
@@ -0,0 +1 @@
+<%= render "search", items: @search.results %>
diff --git a/app/views/searches/new.html.erb b/app/views/searches/new.html.erb
new file mode 100644
index 0000000..2702224
--- /dev/null
+++ b/app/views/searches/new.html.erb
@@ -0,0 +1 @@
+<%= render "form", search: @search %>
diff --git a/app/views/searches/show.html.erb b/app/views/searches/show.html.erb
new file mode 100644
index 0000000..b7d3854
--- /dev/null
+++ b/app/views/searches/show.html.erb
@@ -0,0 +1 @@
+<%= render "search", items: @search.results %>
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb
new file mode 100644
index 0000000..c6d7f4f
--- /dev/null
+++ b/app/views/sessions/new.html.erb
@@ -0,0 +1,21 @@
+<div class="center-contents">
+ <div></div>
+ <div>
+ <h2><%= I18n.t('sessions.title') %></h2>
+
+ <%= form_tag sessions_path do |form| %>
+ <div class='field'>
+ <%= label_tag :username, I18n.t('activerecord.attributes.user.username') %>
+ <%= text_field_tag :username, '', autofocus: true, class: 'width-100' %>
+ </div>
+ <div class='field'>
+ <%= label_tag :password, I18n.t('activerecord.attributes.user.password') %>
+ <%= password_field_tag :password, '', class: 'width-100' %>
+ </div>
+ <div class='actions'>
+ <%= submit_tag I18n.t('sessions.sign-in') %>
+ </div>
+ <% end %>
+ </div>
+ <div></div>
+</div>
diff --git a/app/views/shared_searches/index.html.erb b/app/views/shared_searches/index.html.erb
new file mode 100644
index 0000000..9a145f6
--- /dev/null
+++ b/app/views/shared_searches/index.html.erb
@@ -0,0 +1,13 @@
+<h3><%= I18n.t('searches.shared.title') %></h3>
+
+<% if @searches.empty? %>
+ <div><span><%= I18n.t('searches.shared.none') %>.</span></div>
+<% else %>
+ <div id="shared-searches-list">
+ <% @searches.each do |s| %>
+ <div>
+ <%= link_to s.name, search_shared_path(s) %>
+ </div>
+ <% end %>
+ </div>
+<% end %>
diff --git a/app/views/shared_searches/show.html.erb b/app/views/shared_searches/show.html.erb
new file mode 100644
index 0000000..1430529
--- /dev/null
+++ b/app/views/shared_searches/show.html.erb
@@ -0,0 +1,28 @@
+<table id="public-search-table">
+ <% @search.results.values.flatten.each do |res| %>
+ <tr>
+ <td>
+ <details>
+ <% if res.is_a? Thing %>
+ <summary><%= res.title %></summary>
+ <div class="details-flex">
+ <div><span><b>Authors</b>: <%= authors_or_editors(res) %></span></div>
+ <div><span><b>Publisher</b>: <%= string_maybe(res.publisher) %></span></div>
+ <div><span><b>Year</b>: <%= string_maybe(res.year) %></span></div>
+ <div><span><b>Kind</b>: <%= res.kind %></span></div>
+ <div><span><b>Note</b>: <%= string_maybe(res.note) %></span></div>
+ <% unless res.url.blank? %>
+ <div><span><b>URL</b>: <a href="<%= res.url %>" target="_blank"><%= res.url %></a></span></div>
+ <% end %>
+ </div>
+ <% else %>
+ <summary><b><%= I18n.t('comments.title') %></b> <%= I18n.t('general.in') %> «<i><%= res.thing.title %></i>»</summary>
+ <div class="details-flex">
+ <%= res.content %>
+ </div>
+ <% end %>
+ </details>
+ </td>
+ </tr>
+ <% end %>
+</table>
diff --git a/app/views/tags/_form.html.erb b/app/views/tags/_form.html.erb
new file mode 100644
index 0000000..75fc003
--- /dev/null
+++ b/app/views/tags/_form.html.erb
@@ -0,0 +1,12 @@
+<%= form_with(model: tag) do |form| %>
+ <%= render "layouts/errors", model: tag %>
+
+ <div>
+ <%= form.label :name, style: "display: block" %>
+ <%= form.text_field :name, autofocus: true, autocomplete: 'off', class: 'width-100' %>
+ </div>
+
+ <div>
+ <%= form.submit %>
+ </div>
+<% end %>
diff --git a/app/views/tags/_tag.html.erb b/app/views/tags/_tag.html.erb
new file mode 100644
index 0000000..65c6d4e
--- /dev/null
+++ b/app/views/tags/_tag.html.erb
@@ -0,0 +1,8 @@
+<div class="tag-container" id="<%= dom_id tag %>">
+ <div class="name">
+ <%= tag.name %>
+ </div>
+ <div class="action">
+ <%= link_to I18n.t('general.delete'), tag_path(tag), data: { "turbo-method": :delete } %>
+ </div>
+</div>
diff --git a/app/views/tags/_taggable.html.erb b/app/views/tags/_taggable.html.erb
new file mode 100644
index 0000000..84e236b
--- /dev/null
+++ b/app/views/tags/_taggable.html.erb
@@ -0,0 +1,17 @@
+<% if Tag.any? %>
+ <div>
+ <h5><%= I18n.t('tags.title') %> &#8212; <%= link_to I18n.t('tags.new-action'), new_tag_path %></h5>
+
+ <div class="flex-list">
+ <% Tag.find_each do |tag| %>
+ <div class="flex-element">
+ <label>
+ <%= form.check_box :tag_ids, { multiple: true, checked: tag_references&.include?(tag.id) }, tag.id, nil %>
+ <%= tag.name %>
+ </label>
+ </div>
+ <% end %>
+ </div>
+ </div>
+ <p></p>
+<% end %>
diff --git a/app/views/tags/index.html.erb b/app/views/tags/index.html.erb
new file mode 100644
index 0000000..329e069
--- /dev/null
+++ b/app/views/tags/index.html.erb
@@ -0,0 +1,13 @@
+<div class="center-contents">
+ <div></div>
+ <div id="tag-index">
+ <h2><%= I18n.t('tags.title') %> <span>&#8212; <%= link_to I18n.t('tags.new-action'), new_tag_path %></span></h2>
+
+ <div id="tags-list">
+ <% @tags.each do |tag| %>
+ <%= render tag %>
+ <% end %>
+ </div>
+ </div>
+ <div></div>
+</div>
diff --git a/app/views/tags/new.html.erb b/app/views/tags/new.html.erb
new file mode 100644
index 0000000..bd6c586
--- /dev/null
+++ b/app/views/tags/new.html.erb
@@ -0,0 +1,9 @@
+<div class="center-contents">
+ <div></div>
+ <div id="tag-new">
+ <h2><%= I18n.t('tags.new') %> <span>&#8212; <%= link_to I18n.t('general.back-to-list'), tags_path %></span></h2>
+
+ <%= render "form", tag: @tag %>
+ </div>
+ <div></div>
+</div>
diff --git a/app/views/things/_form.html.erb b/app/views/things/_form.html.erb
new file mode 100644
index 0000000..da90302
--- /dev/null
+++ b/app/views/things/_form.html.erb
@@ -0,0 +1,102 @@
+<%= form_with(model: thing) do |form| %>
+ <%= render "layouts/errors", model: thing %>
+
+ <div id="thing_form">
+ <div class="thing_large">
+ <%= form.label :title %>
+ <%= form.text_field :title, autofocus: true, required: true %>
+ </div>
+
+ <div class="thing_large">
+ <%= form.label :authors %>
+ <%= form.text_field :authors, required: true %>
+ <%= form.check_box :editors %>
+ <%= form.label :editors %>
+ </div>
+
+ <div class="thing_large">
+ <%= form.label :note %>
+ <%= form.text_field :note %>
+ </div>
+
+ <div>
+ <%= form.label :target %>
+ <%= form.text_field :target, required: true %>
+ </div>
+
+ <div>
+ <%= form.label :publisher %>
+ <%= form.text_field :publisher %>
+ </div>
+
+ <div>
+ <%= form.label :year %>
+ <%= form.number_field :year %>
+ </div>
+
+ <div>
+ <%= form.label :address %>
+ <%= form.text_field :address %>
+ </div>
+
+ <div>
+ <%= form.label :url %>
+ <%= form.text_field :url %>
+ </div>
+
+ <div>
+ <%= form.label :access %>
+ <%= form.date_field :access %>
+ </div>
+
+ <div>
+ <%= form.label :location %>
+ <%= form.text_field :location %>
+ </div>
+
+ <div>
+ <%= form.label :insideof %>
+ <%= form.text_field :insideof %>
+ </div>
+
+ <div>
+ <%= form.label :pages %>
+ <%= form.text_field :pages %>
+ </div>
+
+ <div>
+ <%= form.label :rate %>
+ <%= form.number_field :rate, min: 0, max: 10, required: true %>
+ </div>
+
+ <div>
+ <%= form.label :status %>
+ <%= form.select :status, options_for_select(Thing.statuses.map { |k, _| [I18n.t("things.status.#{k}"), k] }.sort, thing.status) %>
+ </div>
+
+ <div>
+ <%= form.label :kind %>
+ <%= form.select :kind, options_for_select(Thing.kinds.map { |k, _| [I18n.t("things.kind.#{k}"), k] }.sort, thing.kind) %>
+ </div>
+
+ <div>
+ <%= form.label :bought_at %>
+ <%= form.date_field :bought_at %>
+ </div>
+
+ <div>
+ <%= form.label :where_is_it %>
+ <%= form.text_field :where_is_it %>
+ </div>
+ </div>
+
+ <%= render "tags/taggable", tag_references: thing.tag_references.pluck(:tag_id), form: form %>
+
+ <div>
+ <%= form.submit %>
+ </div>
+<% end %>
+
+<% if thing.id %>
+ <%= button_to I18n.t('general.delete').capitalize, thing, id: 'thing-delete-button', form: { data: { turbo_confirm: I18n.t('general.sure') } }, method: :delete %>
+<% end %>
diff --git a/app/views/things/_thing.html.erb b/app/views/things/_thing.html.erb
new file mode 100644
index 0000000..5a31ccc
--- /dev/null
+++ b/app/views/things/_thing.html.erb
@@ -0,0 +1,7 @@
+<div id="<%= dom_id thing %>" class="thing_row">
+ <div class="thing_head">
+ <div class="dot dot_<%= thing.kind %>" title="<%= I18n.t("things.kind.#{thing.kind}") %>"></div>
+ </div>
+ <div class="thing_body"><%= link_to thing.title, edit_thing_path(thing) %></div>
+ <div class="thing_tail"><%= thing.rate %></div>
+</div>
diff --git a/app/views/things/edit.html.erb b/app/views/things/edit.html.erb
new file mode 100644
index 0000000..f30ec5c
--- /dev/null
+++ b/app/views/things/edit.html.erb
@@ -0,0 +1,3 @@
+<%= render "form", thing: @thing %>
+
+<%= render "comments/list", thing: @thing, from: 'things/edit' %>
diff --git a/app/views/things/new.html.erb b/app/views/things/new.html.erb
new file mode 100644
index 0000000..d28df14
--- /dev/null
+++ b/app/views/things/new.html.erb
@@ -0,0 +1 @@
+<%= render "form", thing: @thing %>
diff --git a/app/views/things/show.html.erb b/app/views/things/show.html.erb
new file mode 100644
index 0000000..c534e49
--- /dev/null
+++ b/app/views/things/show.html.erb
@@ -0,0 +1,7 @@
+<%= render "things/thing", thing: @thing %>
+
+<p>
+ <%= link_to I18n.t('things.create-another'), new_thing_path %>
+</p>
+
+<%= render "comments/list", thing: @thing, from: 'things/show' %>