diff --git a/Gemfile b/Gemfile index 377afa6..22c55de 100644 --- a/Gemfile +++ b/Gemfile @@ -38,3 +38,5 @@ end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] + +gem "active_model_serializers" diff --git a/Gemfile.lock b/Gemfile.lock index c8b8787..8f83422 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,6 +39,11 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) + active_model_serializers (0.10.13) + actionpack (>= 4.1, < 7.1) + activemodel (>= 4.1, < 7.1) + case_transform (>= 0.2) + jsonapi-renderer (>= 0.1.1.beta1, < 0.3) activejob (6.1.4.4) activesupport (= 6.1.4.4) globalid (>= 0.3.6) @@ -80,6 +85,8 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) + case_transform (0.2) + activesupport childprocess (4.1.0) concurrent-ruby (1.1.9) connection_pool (2.2.5) @@ -98,6 +105,7 @@ GEM jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) + jsonapi-renderer (0.2.2) launchy (2.5.0) addressable (~> 2.7) letter_opener (1.7.0) @@ -246,6 +254,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + active_model_serializers bootsnap (>= 1.4.4) byebug capybara (>= 3.26) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 064809b..2dffa3b 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -4,11 +4,14 @@ class HomeController < ApplicationController before_action :set_todo_item, only: [:edit_todo_item] def landing - @todos = Todo.all.order(:id) + @todos = Todo.all.order(:id).map { |record| TodoSerializer.new(record) } end def edit_todo_item - @todo_item.update(todo_item_params) + @todo_item.image.attach(todo_item_params[:image]) if todo_item_params[:image] + @todo_item.update(todo_item_params.except(:image)) + + render json: @todo_item end def reset_todo_items @@ -18,7 +21,7 @@ def reset_todo_items private def todo_item_params - params.permit(:id, :title, :checked) + params.require(:home).permit(:id, :title, :checked, :image) end def set_todo_item diff --git a/app/javascript/components/TodoList/index.tsx b/app/javascript/components/TodoList/index.tsx index 60f7fd3..3da09dc 100644 --- a/app/javascript/components/TodoList/index.tsx +++ b/app/javascript/components/TodoList/index.tsx @@ -7,6 +7,7 @@ type TodoItem = { id: number; title: string; checked: boolean; + image: string; }; type Props = { @@ -26,9 +27,34 @@ const TodoList: React.FC = ({ todoItems }) => { todoItemId: number ): void => { axios.post("/todo", { - id: todoItemId, - checked: e.target.checked, - }); + home: { + id: todoItemId, + checked: e.target.checked, + } + }).then((res) => { + let foundIndex = todoItems.findIndex(todo => todo.id == res.data.id); + todoItems[foundIndex] = res.data.id; + e.target.checked = res.data.checked; + }); + + }; + + const onImageUpload = (event, id) => { + const data = new FormData(); + data.append("home[image]", event.target.files[0]); + data.append("home[id]", `${id}`); + + axios.post('todo/', data, { + headers: { + 'Content-Type': 'multipart/form-data' + }}) + .then((res) => { + let foundIndex = todoItems.findIndex(x => x.id == res.data.id); + todoItems[foundIndex] = res.data.id; + + const image = document.getElementById(`img_${res.data.id}`) as HTMLImageElement | null; + image.src = res.data.image; + }); }; const resetButtonOnClick = (): void => { @@ -47,6 +73,11 @@ const TodoList: React.FC = ({ todoItems }) => { checked={todo.checked} onChange={(e) => checkBoxOnCheck(e, todo.id)} /> + + + onImageUpload(e, todo.id)} /> ))} Reset diff --git a/app/models/todo.rb b/app/models/todo.rb index e7adee6..378e0f8 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,2 +1,3 @@ class Todo < ApplicationRecord + has_one_attached :image end diff --git a/app/serializers/todo_serializer.rb b/app/serializers/todo_serializer.rb new file mode 100644 index 0000000..369aaf3 --- /dev/null +++ b/app/serializers/todo_serializer.rb @@ -0,0 +1,9 @@ +class TodoSerializer < ActiveModel::Serializer + include Rails.application.routes.url_helpers + + attributes :id, :title, :checked, :image + + def image + rails_blob_path(object.image, only_path: true) if object.image.attached? + end +end diff --git a/config/routes.rb b/config/routes.rb index 6c5c96c..f61a9b4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,5 +3,5 @@ Rails.application.routes.draw do root to: "home#landing" post "todo", to: "home#edit_todo_item" - post "reset", to: "home#reset_todo_item" + post "reset", to: "home#reset_todo_items" end diff --git a/db/migrate/20220908121707_create_active_storage_tables.active_storage.rb b/db/migrate/20220908121707_create_active_storage_tables.active_storage.rb new file mode 100644 index 0000000..8779826 --- /dev/null +++ b/db/migrate/20220908121707_create_active_storage_tables.active_storage.rb @@ -0,0 +1,36 @@ +# This migration comes from active_storage (originally 20170806125915) +class CreateActiveStorageTables < ActiveRecord::Migration[5.2] + def change + create_table :active_storage_blobs do |t| + t.string :key, null: false + t.string :filename, null: false + t.string :content_type + t.text :metadata + t.string :service_name, null: false + t.bigint :byte_size, null: false + t.string :checksum, null: false + t.datetime :created_at, null: false + + t.index [ :key ], unique: true + end + + create_table :active_storage_attachments do |t| + t.string :name, null: false + t.references :record, null: false, polymorphic: true, index: false + t.references :blob, null: false + + t.datetime :created_at, null: false + + t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + + create_table :active_storage_variant_records do |t| + t.belongs_to :blob, null: false, index: false + t.string :variation_digest, null: false + + t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end +end diff --git a/db/schema.rb b/db/schema.rb index c6bc617..940a4bc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,39 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_03_05_192904) do +ActiveRecord::Schema.define(version: 2022_09_08_121707) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "active_storage_attachments", force: :cascade do |t| + t.string "name", null: false + t.string "record_type", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.string "key", null: false + t.string "filename", null: false + t.string "content_type" + t.text "metadata" + t.string "service_name", null: false + t.bigint "byte_size", null: false + t.string "checksum", null: false + t.datetime "created_at", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + + create_table "active_storage_variant_records", force: :cascade do |t| + t.bigint "blob_id", null: false + t.string "variation_digest", null: false + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true + end + create_table "todos", force: :cascade do |t| t.string "title" t.boolean "checked", default: false @@ -22,4 +50,6 @@ t.datetime "updated_at", precision: 6, null: false end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" end