【Rails】furimaアプリ　商品出品機能実装　#2

# 実装概要
- JavaScriptを使用して商品出品ページの販売価格を入力すると販売手数料（10%)と販売利益が自動で表示される機能の実装。
- データが保存され、エラーがある際はエラー文が表示される機能の実装。

### jsファイルの作成
app/javascriptディレクトリ配下にitems_new.jsファイルを作成。

### items_newファイルの読み込みができるように設定

items_new.jsファイルをアプリに「ピン留め（固定）」し、ページや機能の中で使えるようにするため下記コードを追記。
```ruby:importmap.rb
# 中略
pin "items_new", to: "items_new.js"
```
### items_new.jsの機能を実際に使うために読み込むコードを追記
```ruby:app/javascript/application.js
# 中略
import "items_new"
```

### items_new.jsファイルの編集
```ruby:app/jabascript/items_new.js
document.addEventListener('DOMContentLoaded', () => {
  const priceInput = document.getElementById("item-price");
  const addTaxDom = document.getElementById("add-tax-price");
  const profitDom = document.getElementById("profit");

  priceInput.addEventListener("input", () => {
    const inputValue = priceInput.value;

    if (inputValue >= 300 && inputValue <= 9999999) {
      const tax = Math.floor(inputValue * 0.1);
      const profit = inputValue - tax;

      addTaxDom.innerHTML = tax.toLocaleString();
      profitDom.innerHTML = profit.toLocaleString();
    } else {
      addTaxDom.innerHTML = '';
      profitDom.innerHTML = '';
    }
  });
});
```
JavaScriptの知識が浅く理解に苦労したためitems_new_jsで記述したコードの解説を記載していく
# コードの説明

## 1. `document.addEventListener('DOMContentLoaded', () => { ... });`
- **意味**: 
  - この部分は、HTML文書が完全に読み込まれた後、指定した関数を実行するためのコード。
- **詳細**:
  - `DOMContentLoaded`イベントは、ページのHTMLがすべて読み込まれたときに発火。このタイミングでJavaScriptのコードを実行することで、HTML要素に安全にアクセスできる。

## 2. `const priceInput = document.getElementById("item-price");`
- **意味**: 
  - `priceInput`という変数に、IDが`item-price`のHTML要素を格納。
- **詳細**:
  - `document.getElementById`は、指定されたIDを持つ要素を取得するメソッド。この場合、販売価格を入力するためのテキストボックスを取得している。

## 3. `const addTaxDom = document.getElementById("add-tax-price");`
- **意味**: 
  - `addTaxDom`変数に、IDが`add-tax-price`のHTML要素を格納。
- **詳細**:
  - ここでは、計算された販売手数料を表示するための要素を取得している。

## 4. `const profitDom = document.getElementById("profit");`
- **意味**: 
  - `profitDom`変数に、IDが`profit`のHTML要素を格納。
- **詳細**:
  - ここでは、計算された販売利益を表示するための要素を取得している。

## 5. `priceInput.addEventListener("input", () => { ... });`
- **意味**:
  -  `priceInput`要素に、ユーザーが値を入力するたびに発火するイベントリスナーを追加。
- **詳細**:
  - `"input"`イベントは、ユーザーがテキストボックスに文字を入力したときに発生。このイベントを使うことで、入力が行われるたびに計算を実行する。

## 6. `const inputValue = priceInput.value;`
- **意味**: 
  - 現在の入力値を取得して、`inputValue`という変数に格納。
- **詳細**:
  - `.value`プロパティを使うことで、テキストボックスに入力された値を取得できる。

## 7. `if (inputValue >= 300 && inputValue <= 9999999) { ... }`
- **意味**: 
  - 入力された値が300以上9999999以下であるかをチェックします。
- **詳細**:
  - この条件文は、入力値が販売価格として妥当かどうかを判断するために使われる。条件を満たさない場合は、販売手数料と利益を表示しないようにしている。

## 8. `const tax = Math.floor(inputValue * 0.1);`
- **意味**: 
  - 入力値の10%を計算し、`tax`という変数に格納。
- **詳細**:
  - `Math.floor()`は、計算結果を小数点以下切り捨てて整数にする関数。これにより、販売手数料が整数として表示される。

## 9. `const profit = inputValue - tax;`
- **意味**: 
  - 利益を計算し、`profit`という変数に格納。
- **詳細**:
  - 利益は、入力された販売価格から販売手数料を引いた値として計算される。

## 10. `addTaxDom.innerHTML = tax.toLocaleString();`
- **意味**: 
  - 販売手数料をHTML要素に表示。
- **詳細**:
  - `toLocaleString()`メソッドを使って、3桁ごとにカンマで区切った形式で表示。これにより、視覚的に見やすくなる。

## 11. `profitDom.innerHTML = profit.toLocaleString();`
- **意味**: 
  - 利益をHTML要素に表示。
- **詳細**:
  - 同様に、利益も3桁ごとにカンマで区切って表示。

## 12. `else { ... }`
- **意味**: 
  - 入力値が範囲外の場合の処理を定義。
- **詳細**:
  - このブロックでは、入力値が適切な範囲でない場合、販売手数料と利益の表示を空にする。

## 13. `addTaxDom.innerHTML = '';`
- **意味**: 
  - 販売手数料の表示を空にする。
- **詳細**:
  - 入力値が範囲外のとき、販売手数料の表示部分をクリアする。

## 14. `profitDom.innerHTML = '';`
- **意味**:
  - 利益の表示を空にする。
- **詳細**:
  - 同様に、利益の表示部分もクリアする。

商品出品ページの表示部分の実装が完了したので、次は商品データが保存されるようにする。
### ルーティングの設定
```ruby:config/routes.rb
# 中略
resources :items, only: [:index, :new, :create]
```

### バリデーションの設定
```ruby:app/models/item.rb
# 中略
validates :name, presence: true
  validates :description, presence: true
  validates :image, presence: true

  validates :category_id, numericality: { other_than: 1, only_integer: true, message: "can't be blank" }
  validates :status_id, numericality: { other_than: 1, only_integer: true, message: "can't be blank" }
  validates :shipping_fee_id, numericality: { other_than: 1, only_integer: true, message: "can't be blank" }
  validates :prefecture_id, numericality: { other_than: 1, only_integer: true, message: "can't be blank" }
  validates :delivery_time_id, numericality: { other_than: 1, only_integer: true, message: "can't be blank" }
  
  validates :price, presence: true, numericality: { greater_than_or_equal_to: 300, less_than_or_equal_to: 9999999 }
end
```

### createメソッド内のコードを記述
```ruby:app/controllers/items_controller.rb
def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to items_path
    else
      render :new, status: :unprocessable_entity
    end
  end

  private

  def item_params
    params.require(:item).permit(:name, :image, :description, :category_id, :status_id, :shipping_fee_id, :prefecture_id, :delivery_time_id, :price).merge(user_id: current_user.id)
  end
end

# 商品出品機能 #1でitem_params内のコード記述していたが併せて修正
```

### 未ログイン状態で商品出品ページに遷移しようとしてもログインページに遷移するよう設定
```ruby:app/controllers/items_controller.rb
before_action :authenticate_user!, only: [:new, :create]
```

必要な情報を入力するとデータが保存され、エラーがある際はエラー文が表示されることを確認。
次回はitemモデルの単体テストを実施していきます！