diff --git a/app/controllers/todos_controller.rb b/app/controllers/todos_controller.rb index 5c09d20..4b8b6e3 100644 --- a/app/controllers/todos_controller.rb +++ b/app/controllers/todos_controller.rb @@ -1,9 +1,9 @@ class TodosController < ApplicationController - before_action :set_todo, only: %i[ show edit update destroy ] + before_action :set_todo, only: %i[show edit update destroy] # GET /todos or /todos.json def index - @todos = Todo.all + @todos = Todo.in_order_of(:status, %w[incomplete complete]) end # GET /todos/1 or /todos/1.json @@ -25,11 +25,14 @@ class TodosController < ApplicationController respond_to do |format| if @todo.save - format.html { redirect_to todo_url(@todo), notice: "Todo was successfully created." } - format.json { render :show, status: :created, location: @todo } + format.turbo_stream + format.html { redirect_to todo_url(@todo), notice: 'Todo was successfully created.' } else + format.turbo_stream do + render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: 'form', + locals: { todo: @todo }) + end format.html { render :new, status: :unprocessable_entity } - format.json { render json: @todo.errors, status: :unprocessable_entity } end end end @@ -38,11 +41,13 @@ class TodosController < ApplicationController def update respond_to do |format| if @todo.update(todo_params) - format.html { redirect_to todo_url(@todo), notice: "Todo was successfully updated." } - format.json { render :show, status: :ok, location: @todo } + format.html { redirect_to todo_url(@todo), notice: 'Todo was successfully updated.' } else + format.turbo_stream do + render turbo_stream: turbo_stream.replace("#{helpers.dom_id(@todo)}_form", partial: 'form', + locals: { todo: @todo }) + end format.html { render :edit, status: :unprocessable_entity } - format.json { render json: @todo.errors, status: :unprocessable_entity } end end end @@ -52,19 +57,20 @@ class TodosController < ApplicationController @todo.destroy! respond_to do |format| - format.html { redirect_to todos_url, notice: "Todo was successfully destroyed." } - format.json { head :no_content } + format.turbo_stream { render turbo_stream: turbo_stream.remove("#{helpers.dom_id(@todo)}") } + format.html { redirect_to todos_url, notice: 'Todo was successfully destroyed.' } end end private - # Use callbacks to share common setup or constraints between actions. - def set_todo - @todo = Todo.find(params[:id]) - end - # Only allow a list of trusted parameters through. - def todo_params - params.require(:todo).permit(:title, :status) - end + # Use callbacks to share common setup or constraints between actions. + def set_todo + @todo = Todo.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def todo_params + params.require(:todo).permit(:title, :status) + end end diff --git a/app/models/todo.rb b/app/models/todo.rb index e7adee6..7d7722a 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,2 +1,7 @@ class Todo < ApplicationRecord + after_update_commit { broadcast_append_to 'todos' } + + validates :title, presence: true + + enum status: { incomplete: 0, complete: 1 } end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 8086fdc..a5b6c51 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,7 +1,7 @@ - Todoapp + Turbo Todos <%= csrf_meta_tags %> <%= csp_meta_tag %> @@ -12,7 +12,7 @@ -
+
<%= yield %>
diff --git a/app/views/todos/_form.html.erb b/app/views/todos/_form.html.erb index 324a15c..eb4714e 100644 --- a/app/views/todos/_form.html.erb +++ b/app/views/todos/_form.html.erb @@ -1,4 +1,4 @@ -<%= form_with(model: todo, class: "contents") do |form| %> +<%= form_with(model: todo, class: "contents", id: "#{dom_id(todo)}_form") do |form| %> <% if todo.errors.any? %>

<%= pluralize(todo.errors.count, "error") %> prohibited this todo from being saved:

@@ -11,17 +11,14 @@
<% end %> -
- <%= form.label :title %> - <%= form.text_field :title, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %> -
+
+ <%= form.label :title, class: "sr-only" %> +
+ <%= form.text_field :title, class: "block shadow-sm rounded-full border border-gray-200 outline-none px-6 py-4 w-full focus:border-sky-300 focus:outline-none focus:ring-4 focus:ring-sky-50 text-lg placeholder:text-gray-400", placeholder: "Add a new todo" %> -
- <%= form.label :status %> - <%= form.number_field :status, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %> -
- -
- <%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %> +
+ <%= form.submit class: "mt-px rounded-full py-4 px-5 bg-sky-500 text-white font-medium cursor-pointer border-2 border-sky-500 hover:bg-sky-600 hover:border-sky-600" %> +
+
<% end %> diff --git a/app/views/todos/_todo.html.erb b/app/views/todos/_todo.html.erb index d78ded5..0399266 100644 --- a/app/views/todos/_todo.html.erb +++ b/app/views/todos/_todo.html.erb @@ -1,12 +1,13 @@ -
-

- Title: - <%= todo.title %> -

- -

- Status: - <%= todo.status %> -

- -
+
  • " class="py-3"> + <%= turbo_frame_tag dom_id(todo) do %> +
    + <%= link_to todo.title, edit_todo_path(todo), class: "flex-1 #{"line-through opacity-50" if todo.complete?}" %> + <% if todo.complete? %> + <%= button_to "Mark incomplete", todo_path(todo, todo: { status: 'incomplete'}), method: :patch, class: "bg-gray-50 px-3 py-2 rounded inline-flex items-center justify-center text-gray-600" %> + <% else %> + <%= button_to "Mark complete", todo_path(todo, todo: { status: 'complete'}), method: :patch, class: "bg-green-50 px-3 py-2 rounded inline-flex items-center justify-center text-green-600" %> + <% end %> + <%= button_to "Delete", todo_path(todo), method: :delete, class: "bg-red-50 px-3 py-2 rounded inline-flex items-center justify-center text-red-600" %> +
    + <% end %> +
  • diff --git a/app/views/todos/create.turbo_stream.erb b/app/views/todos/create.turbo_stream.erb new file mode 100644 index 0000000..3245eed --- /dev/null +++ b/app/views/todos/create.turbo_stream.erb @@ -0,0 +1,7 @@ +<%= turbo_stream.prepend "todos" do %> + <%= render "todo", todo: @todo %> +<% end %> + +<%= turbo_stream.replace "#{dom_id(Todo.new)}_form" do %> + <%= render "form", todo: Todo.new %> +<% end %> diff --git a/app/views/todos/edit.html.erb b/app/views/todos/edit.html.erb index 91a1744..8d4131d 100644 --- a/app/views/todos/edit.html.erb +++ b/app/views/todos/edit.html.erb @@ -1,8 +1,3 @@ -
    -

    Editing todo

    - +<%= turbo_frame_tag dom_id(@todo) do %> <%= render "form", todo: @todo %> - - <%= link_to "Show this todo", @todo, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> - <%= link_to "Back to todos", todos_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> -
    +<% end %> diff --git a/app/views/todos/index.html.erb b/app/views/todos/index.html.erb index 3e0865a..16ca488 100644 --- a/app/views/todos/index.html.erb +++ b/app/views/todos/index.html.erb @@ -1,21 +1,16 @@ -
    +
    +
    +

    Todos

    +
    + <% if notice.present? %>

    <%= notice %>

    <% end %> - <% content_for :title, "Todos" %> + <%= render "form", todo: Todo.new %> -
    -

    Todos

    - <%= link_to "New todo", new_todo_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %> -
    - -
    - <% @todos.each do |todo| %> - <%= render todo %> -

    - <%= link_to "Show this todo", todo, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %> -

    - <% end %> -
    + <%= turbo_stream_from "todos" %> +
      + <%= render @todos %> +