diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 288b9ab7..b62e5f6f 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -13,3 +13,58 @@ *= require_tree . *= require_self */ + +/* Miniature CSS reset */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: system-ui, sans-serif; +} + +#wrapper { + display: flex; + flex-direction: column; + margin: 0 auto; + max-width: 500px; + margin-top: 20px; + gap: 10px; + padding: 10px; +} + +/* Site-wide HTML element and class styles */ +a { + text-decoration: none; +} + +input, textarea { + padding: 3px; + font-size: 16px; + margin-bottom: 10px; + margin-top: 4px; + width: 100%; +} + +input[type="submit"] { + padding: 5px; +} + +textarea { + max-width: 100%; +} + +li { + list-style-type: none; + margin-bottom: 15px; +} + +.form-error { + background: rgba(255, 0, 0, 0.5); + padding: 10px; + margin-top: 10px; + margin-bottom: 10px; + border-radius: 4px; +} diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb new file mode 100644 index 00000000..6e672459 --- /dev/null +++ b/app/controllers/articles_controller.rb @@ -0,0 +1,65 @@ +class ArticlesController < ApplicationController + # GET /articles + def index + # Filter by the search query, if present + @articles = if params.key?(:query) + Article.search(params[:query]) + else + Article.all + end + end + + # GET /articles/:id + def show + @article = Article.find(params[:id]) + end + + # GET /articles/new + def new + @article = Article.new(date: Date.today) + end + + # POST /articles + def create + @article = Article.new(article_parameters) + + if @article.save + redirect_to @article + else + render :new, status: :unprocessable_entity + end + end + + # GET /articles/:id/edit + def edit + @article = Article.find(params[:id]) + end + + # POST /articles/:id + def update + @article = Article.find(params[:id]) + + if @article.update(article_parameters) + redirect_to @article + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /articles/:id + def destroy + @article = Article.find(params[:id]) + @article.destroy + + # Redirect the user to the list of all articles after deletion + redirect_to articles_path, status: :see_other + end + + # Ensure the hash passed to `create` or `edit`contains only the valid fields + # for the Article model. + private + + def article_parameters + params.require(:article).permit(:title, :content, :author, :date) + end +end diff --git a/app/models/article.rb b/app/models/article.rb new file mode 100644 index 00000000..6c8532e5 --- /dev/null +++ b/app/models/article.rb @@ -0,0 +1,16 @@ +class Article < ApplicationRecord + # Ensure articles have a non-empty title and non-empty content + validates :title, presence: true + validates :content, presence: true + + # Search articles by title and content + def self.search(query) + # We want to interpret characters like '%' and '_' in the search query + # literally, because users aren't familiar with sqlite's search syntax. + # So we use `sanitize_sql_like` before querying the database. See + # https://guides.rubyonrails.org/active_record_querying.html section 3.2.2 + # for more information on sanitizing LIKE queries. + where('title LIKE :sanitized_query OR content LIKE :sanitized_query', + { sanitized_query: "%#{sanitize_sql_like(query)}%" }) + end +end diff --git a/app/views/articles/_form.html.erb b/app/views/articles/_form.html.erb new file mode 100644 index 00000000..f0b604ae --- /dev/null +++ b/app/views/articles/_form.html.erb @@ -0,0 +1,30 @@ +<%= form_with model: article do |form| %> +
<%= message %>
+ <% end %> ++ <%= @articles.count %> + <%= 'article'.pluralize(@articles.count) %> found for query + ‘<%= params[:query] %>’ +
+<% end %> + +<%# Articles List %> +<%= article.content.truncate(140, separator: " ") %>
+ <% end %> +You can <%= link_to "create a new article", new_article_path, class: "link-button" %>.
diff --git a/app/views/articles/new.html.erb b/app/views/articles/new.html.erb new file mode 100644 index 00000000..fa7c39aa --- /dev/null +++ b/app/views/articles/new.html.erb @@ -0,0 +1,5 @@ +<%= render "articles/header", back_button: true do %> ++ <% if @article.author? %> + <%= @article.author %> • + <% end %> + + <% if @article.date? %> + <%= @article.date %> + <% end %> +
+<% end %> + +<%= @article.content %>
+ +You can <%= link_to "edit", edit_article_path(@article) %> or + <%= link_to "delete", article_path(@article), data: { + turbo_method: :delete, + turbo_confirm: "Are you sure you want to delete ’#{@article.title}? This can't be undone." + } %> this article. +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 812bfb90..14e919d6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,6 +11,8 @@ - <%= yield %> +