Skip to content

【admin】火器使用申請UIの作成#1950

Merged
hikahana merged 28 commits intogm3/developfrom
feat/hikahana/fire-equpment-admin-ui
Apr 18, 2026
Merged

【admin】火器使用申請UIの作成#1950
hikahana merged 28 commits intogm3/developfrom
feat/hikahana/fire-equpment-admin-ui

Conversation

@hikahana
Copy link
Copy Markdown
Contributor

@hikahana hikahana commented Jun 29, 2025

対応Issue

resolve #0

概要

  • 火気使用申請機能の新規実装

実装詳細

  • 火気使用申請機能
    • 火気使用申請の登録、一覧表示、編集、削除機能を追加
    • 年度ごとの絞り込み、CSVダウンロード機能を追加
    • ダッシュボードにサマリー情報を表示
    • サイドバーにメニュー項目を追加

画面スクリーンショット等

image
image
image

image
image

テスト項目

  • 火気使用申請
    • 新規申請が正しく登録できるか
    • 一覧画面にデータが正しく表示されるか
    • 年度での絞り込みが機能するか
    • CSVが正しくダウンロードされるか
    • 編集、削除が正しく行えるか
    • ダッシュボードの数値が正しいか
  • 既存機能
    • 各画面でのCSV/PDFダウンロードが正常に行えるか
    • 各画面の表示が崩れていないか

備考

Summary by CodeRabbit

  • New Features
    • Added a fire equipment application workflow: new menu link, list view, detail page with edit/delete, and creation modal.
    • Integrated fire equipment application status into the order monitoring page.
    • Year-based filtering for fire equipment applications and CSV export/download per year.

@hikahana hikahana self-assigned this Jun 29, 2025
@hikahana hikahana changed the title Feat/hikahana/fire equpment admin UI 火器使用申請UIの作成 Jun 30, 2025
@hikahana hikahana changed the title 火器使用申請UIの作成 【admin】火器使用申請UIの作成 Jul 1, 2025
@hikahana hikahana marked this pull request as ready for review July 6, 2025 10:43
Copy link
Copy Markdown
Collaborator

@izuizu0424 izuizu0424 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

動作確認OKだと思います!

管理者画面 画像
image
「使用場所」→「使用用途」でお願いします.
詳細ページは「使用方法」になっていたので,ここも「使用用途」統一でお願いします
image

今全部の情報をページで見れるようになっているんですが,言ってたようにここに申請状況の欄を足すと,使用用途と備考欄が狭くなってしまうかなと思うので,備考欄は詳細ページのみでよろしくお願いします.
詳しいデザインで指定しておらず,すみません

end
bom = "\uFEFF"
csv_data = CSV.generate(bom) do |csv|
column_name = %w(ID 団体名 品目 数量 燃料 使用場所 持ち帰り 備考)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

figmaだと火気の名称 , 火気の台数 , 使用用途 だった

Suggested change
column_name = %w(ID 団体名 品目 数量 燃料 使用場所 持ち帰り 備考)
column_name = %w(ID 団体名 火気の名称 火気の台数 燃料 使用用途 持ち帰り 備考)

Comment on lines +506 to +515
column_name = %w(参加団体名 PR文 URL アナウンス有無)
column_name = %w(参加団体名 PR文 PR画像)
csv << column_name
@public_relations.each do |public_relations|
# データが存在しない場合はスキップする
if public_relations.nil?
@public_relations.each do |public_relation|
if public_relation.nil?
next
end
column_values = [
public_relations.group.name,
public_relations.blurb,
public_relations.picture_path,
public_relations.is_announcement_requested ? "有" : "無",
public_relation.group.name,
public_relation.pr_comment,
public_relation.pr_image,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

なぜかpublic_relationsも変更されてる
|public_relations||public_relation| にするのは良さげ

Comment thread api/app/controllers/api/v1/output_csv_controller.rb
@harata-t
Copy link
Copy Markdown
Collaborator

harata-t commented Jul 12, 2025

バックエンド:controller
管理者(Vue.js):index.vue
管理者(Vue.js):_id.vue
おそらくユーザー画面(Next.js)

で燃料enumの変換ロジックが重複するから、まとめられるならまとめたい感

@hikahana
Copy link
Copy Markdown
Contributor Author

hikahana commented Jul 18, 2025

@harata-t
ディレクトリ跨って重複するのはむずいなーって感じする。
APIのロジックに組み込むかフロント対応でよしなにするでも良さそうな感じするけど
個人的にはvueとnextjsでフロントが変わるから頑張ってapiの中でenum変換処理してレスポンスをstring形式で返した方が良いかも?

@hikahana
Copy link
Copy Markdown
Contributor Author

こんな感じです。

申請状況一覧
image

一覧ページ
image

詳細ページ
image

@hikahana hikahana requested review from harata-t and izuizu0424 July 20, 2025 07:16
Copy link
Copy Markdown
Collaborator

@harata-t harata-t left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Copy Markdown
Collaborator

@izuizu0424 izuizu0424 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

レビュー遅くなってすみません
csv出力なんですが,持って帰らない場合もあったと思うのでパラディンさんと要確認です
その他は大丈夫でした.

@hikahana
Copy link
Copy Markdown
Contributor Author

hikahana commented Aug 8, 2025

パラディンとよりも総務側でどのパターンのcsvがほしいかを確認すべきなのでは?

@harata-t
Copy link
Copy Markdown
Collaborator

harata-t commented Aug 8, 2025

バンバンslackで聞いちゃおう

@batcho0428
Copy link
Copy Markdown
Collaborator

@hikahana
csvに持って帰るか持って帰らないか入れる形でお願いします。

@batcho0428 batcho0428 requested a review from YosukeIida March 10, 2026 13:04
Copy link
Copy Markdown
Collaborator

@YosukeIida YosukeIida left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f4924550-3276-464e-a762-c435dbddb3ad

📥 Commits

Reviewing files that changed from the base of the PR and between bc52ad9 and 6f23c3e.

📒 Files selected for processing (1)
  • api/app/controllers/api/v1/order_status_check_api_controller.rb
✅ Files skipped from review due to trivial changes (1)
  • api/app/controllers/api/v1/order_status_check_api_controller.rb

📝 Walkthrough

Walkthrough

Adds a complete "fire equipment orders" feature: frontend menu and pages (list, detail, add/edit/delete), backend endpoints and CSV export, model association and translations, and new routes to support year-filtering, CSV download, and status integration.

Changes

Cohort / File(s) Summary
Frontend Navigation
admin_view/nuxt-project/components/Menu.vue
Inserted a new order_items entry for 火気使用申請 with icon and route to /fire_equipment_orders.
Frontend Pages – Fire Equipment Orders
admin_view/nuxt-project/pages/fire_equipment_orders/index.vue, admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue
Added listing page with year filtering, group lookup, CSV download, add modal; added detail page with view/edit/delete modals. Pay attention to API URLs, asyncData usage, and form state / snackbar timing.
Frontend – Order Status Check
admin_view/nuxt-project/pages/order_status_check/index.vue
Added “火気使用申請” column and adjusted unregistered/registered rendering logic based on group.fire_equipment_order_status and group.group_category.
Backend API – Group lookup & Status
api/app/controllers/api/v1/groups_api_controller.rb, api/app/controllers/api/v1/order_status_check_api_controller.rb
Added get_groups_for_fire_equipment_order (returns raw { data: @groups }, not existing response wrapper) and extended fit_group_index_for_admin_view to include fire_equipment_order derived from associated orders. Verify response shape consistency with frontend expectations.
Backend API – CSV & Controller changes
api/app/controllers/api/v1/output_csv_controller.rb, api/app/controllers/fire_equipment_orders_controller.rb
Added output_fire_equipment_orders_csv streaming CSV (BOM, header, boolean mapping) and renamed loop variable in another CSV method; updated fire_equipment_orders controller to filter by fes_year_id and to render enriched hashes including group: {id,name} and fuel_japanese. Review CSV encoding, filtering logic, and changed JSON shapes.
Backend Model
api/app/models/fire_equipment_order.rb
Added belongs_to :group, FUEL_TRANSLATIONS constant, fuel_japanese instance method, and fuel_options_for_select class method for localized labels.
Routes
api/config/routes.rb
Registered routes: get_groups_for_fire_equipment_order, get_fire_equipment_orders_csv/:fes_year_id, plus three new PDF endpoints (rental_items, power_orders, get_project_check_list_pdf/:group_id).

Sequence Diagram(s)

sequenceDiagram
    participant AdminUI as Admin UI
    participant NuxtServer as Nuxt Server
    participant API as Rails API
    participant DB as Database
    AdminUI->>NuxtServer: Navigate to /fire_equipment_orders
    NuxtServer->>API: GET /api/v1/.../fire_equipment_orders?fes_year_id=...
    API->>DB: Query FireEquipmentOrder (+ joins group)
    DB-->>API: Records
    API-->>NuxtServer: JSON (orders with group, fuel_japanese)
    NuxtServer-->>AdminUI: Render list
    AdminUI->>NuxtServer: Click "Download CSV"
    NuxtServer->>API: GET /api/v1/get_fire_equipment_orders_csv/{fes_year_id}
    API->>DB: Query FireEquipmentOrder (filtered by fes_year_id)
    DB-->>API: Records
    API-->>NuxtServer: CSV blob (BOM + header)
    NuxtServer-->>AdminUI: Trigger download
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped in quick, with forms to tend,
Added lists, edits, and CSVs to send.
Menus updated, routes in place,
Fuel names in Japanese — a tidy trace.
Cheers from a rabbit, bytes all in haste!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: implementation of the fire equipment order UI for the admin panel. It is concise, specific, and accurately reflects the primary objective of the PR.
Description check ✅ Passed The PR description follows the repository template, includes required sections (対応Issue, 概要, 実装詳細, スクリーンショット, テスト項目), provides implementation details, includes relevant UI screenshots, and documents test coverage with status.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/hikahana/fire-equpment-admin-ui

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue`:
- Around line 188-210: The edit() and destroy() functions need proper
async/await sequencing and error handling: wrap the body of edit() and destroy()
in try/catch, await the axios calls, await this.reload() before calling
this.closeEditModal() in edit(), and on error call this.openSnackBar with an
error message and optionally log the error; similarly in destroy() await the
$delete call and wrap the navigation in the try block, handling errors in the
catch. Target the edit, destroy, reload, closeEditModal methods and the
this.$axios.$put / this.$axios.$delete calls when applying the change.
- Around line 142-145: Replace fragile path string manipulation in asyncData:
stop using route.path.replace("/fire_equipment_orders/", "") and instead read
the dynamic segment from route.params.id; build the URL using that id (e.g.,
"/fire_equipment_orders/" + route.params.id) before calling $axios.$get so the
code is resilient to trailing slashes and path variations.
- Around line 25-27: The detail view crashes because it dereferences
fireEquipmentOrder.group.name without null checks; update the renderer in
pages/fire_equipment_orders/_id.vue to defensively handle a missing group by
checking fireEquipmentOrder.group before accessing its name and rendering an
empty string (or other safe fallback) when group is null—mirror the CSV-side
behavior that uses a fallback when order.group is absent to prevent render-time
exceptions.

In `@admin_view/nuxt-project/pages/fire_equipment_orders/index.vue`:
- Around line 221-262: Wrap the API calls in both submit() and downloadCSV()
with try/catch blocks: for submit(), surround the this.$axios.$post(...) and the
follow-up calls (this.refinement(...), this.closeAddModal(), resetting form
fields, this.openSnackBar(...)) so they only run on success, and in the catch
call this.openSnackBar with a clear failure message and console.error the caught
error; for downloadCSV(), surround the this.$axios.get(...) and the
blob/download logic so they only run on success, and in the catch call
this.openSnackBar with a failure message and console.error the error; ensure you
still revokeObjectURL in a finally block if the link was created. Reference:
submit(), downloadCSV(), this.$axios.$post, this.$axios.get, this.refinement,
this.closeAddModal, this.openSnackBar.

In `@admin_view/nuxt-project/pages/order_status_check/index.vue`:
- Around line 95-99: The cooking_process_order cell (<td :class="{ unregistered:
!group.cooking_process_order && group.group_category === 1 }"> with its three
<div> branches using group.cooking_process_order and group.group_category) is
rendered too early; remove this <td> from its current position and reinsert the
exact same block immediately after the venue_map cell so the row order aligns
with headers (place the cooking_process_order <td> right after the venue_map
cell block). Ensure you keep the class binding and the v-if / v-else-if / v-else
logic unchanged when moving it.
- Around line 124-126: The template is reading the wrong field name: replace
uses of group.fire_equipment_order_status with the API-returned field
group.fire_equipment_order so the UI reflects the real data; update the
conditional expressions in the Order Status Check page (the <div v-if="..." />,
<td :class="..."> and <div v-else-if="..." /> logic) to test
group.fire_equipment_order and keep the existing isUnregistered(group.group.id,
'fire_equipment_order') and category checks unchanged.

In `@api/app/controllers/api/v1/groups_api_controller.rb`:
- Around line 99-106: The custom action get_groups_for_fire_equipment_order
violates RESTful naming; either remove it and reuse the existing
get_refinement_groups (which already accepts fes_year_id and other filters) or
implement fes_year_id filtering in the standard index action and update
callers/routes accordingly. Specifically, migrate the filtering logic
(Group.where(fes_year_id: fes_year_id).order(:id) / Group.order(:id)) into
get_refinement_groups or GroupsController#index, ensure the JSON response shape
matches the original (render json: { data: `@groups` }), and delete the
get_groups_for_fire_equipment_order route/action so routes point to the
consolidated method. Also update any tests or clients to call the consolidated
endpoint.

In `@api/app/controllers/api/v1/order_status_check_api_controller.rb`:
- Around line 9-39: Remove the duplicate method definition
get_order_status_check_for_admin_view (the second occurrence) and its orphaned
end so the file parses; then ensure fit_group_index_for_admin_view is defined
once and correctly indented/paired with its end. Locate the duplicate
get_order_status_check_for_admin_view block and delete it, confirm only the
original method remains, and adjust surrounding indentation/blocks for
fit_group_index_for_admin_view and its inner map block to restore proper Ruby
syntax.

In `@api/app/controllers/api/v1/output_csv_controller.rb`:
- Around line 534-561: The CSV export in output_fire_equipment_orders_csv
iterates order.group for each row but the queries don't eager-load that
association, causing N+1 queries; fix by eager-loading group on the
`@fire_equipment_orders` query (add includes(:group) to the FireEquipmentOrder.all
branch and to the query currently written as
FireEquipmentOrder.joins(:group).where(...) or replace that with
FireEquipmentOrder.includes(:group).where(...)) so order.group is preloaded
during iteration.

In `@api/app/controllers/fire_equipment_orders_controller.rb`:
- Around line 9-20: The current index filters with
FireEquipmentOrder.joins(:group) but later calls order.as_json(include: { group:
... }) which will lazy-load `group` causing N+1; modify the branch that sets
`@fire_equipment_orders` so the records are eager-loaded (e.g., use
includes(:group) or combine joins(...).includes(:group) / preload(:group)) when
filtering by fes_year_id so `@fire_equipment_orders` always has groups preloaded
before the map that calls `as_json(... )` and `fuel_japanese`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8746f0a0-5766-4361-afcc-b4a456f4ca1a

📥 Commits

Reviewing files that changed from the base of the PR and between 7c01b1a and bc52ad9.

📒 Files selected for processing (10)
  • admin_view/nuxt-project/components/Menu.vue
  • admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue
  • admin_view/nuxt-project/pages/fire_equipment_orders/index.vue
  • admin_view/nuxt-project/pages/order_status_check/index.vue
  • api/app/controllers/api/v1/groups_api_controller.rb
  • api/app/controllers/api/v1/order_status_check_api_controller.rb
  • api/app/controllers/api/v1/output_csv_controller.rb
  • api/app/controllers/fire_equipment_orders_controller.rb
  • api/app/models/fire_equipment_order.rb
  • api/config/routes.rb

Comment on lines +25 to +27
<th>団体</th>
<td>{{ fireEquipmentOrder.group.name }}</td>
</tr>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

fd -t f "_id.vue" admin_view/

Repository: NUTFes/group-manager-2

Length of output: 1709


🏁 Script executed:

cat -n admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue | head -60

Repository: NUTFes/group-manager-2

Length of output: 2165


🏁 Script executed:

rg "fire.*equipment.*order" --type vue --type ts --type js -i -A 5 -B 5 | head -100

Repository: NUTFes/group-manager-2

Length of output: 93


🏁 Script executed:

rg "fire.*equipment.*order" -A 5 -B 5 -i 2>/dev/null | head -150

Repository: NUTFes/group-manager-2

Length of output: 8748


🏁 Script executed:

rg "output_fire_equipment_orders_csv" -A 20 -B 5

Repository: NUTFes/group-manager-2

Length of output: 4197


🏁 Script executed:

cat -n admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue | tail -100

Repository: NUTFes/group-manager-2

Length of output: 3339


🏁 Script executed:

rg "fireEquipmentOrder\.group" admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue

Repository: NUTFes/group-manager-2

Length of output: 122


🏁 Script executed:

rg "\{\{.*\?\..*\}\}" admin_view/nuxt-project/pages/ -A 2 -B 2 | head -80

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

rg "group\.name" admin_view/nuxt-project/pages/ -B 3 -A 1

Repository: NUTFes/group-manager-2

Length of output: 38068


🏁 Script executed:

rg "v-if.*group" admin_view/nuxt-project/pages/ -B 2 -A 2 | head -60

Repository: NUTFes/group-manager-2

Length of output: 5375


🏁 Script executed:

rg "eslint|prettier" admin_view/nuxt-project -l | head -10

Repository: NUTFes/group-manager-2

Length of output: 144


🏁 Script executed:

cat admin_view/nuxt-project/package.json | grep -A 10 -B 2 "vue\|nuxt\|eslint\|prettier"

Repository: NUTFes/group-manager-2

Length of output: 1009


group が null のとき詳細画面がクラッシュします。

Line 26 で fireEquipmentOrder.group.name を直接参照しており、groupnull のレコードで描画時例外になります。CSV出力側では order.group&.name || '' で null を考慮しているため、同様の防御が必要です。

修正例
-            <td>{{ fireEquipmentOrder.group.name }}</td>
+            <td>{{ fireEquipmentOrder.group ? fireEquipmentOrder.group.name : "" }}</td>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<th>団体</th>
<td>{{ fireEquipmentOrder.group.name }}</td>
</tr>
<th>団体</th>
<td>{{ fireEquipmentOrder.group ? fireEquipmentOrder.group.name : "" }}</td>
</tr>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue` around lines 25
- 27, The detail view crashes because it dereferences
fireEquipmentOrder.group.name without null checks; update the renderer in
pages/fire_equipment_orders/_id.vue to defensively handle a missing group by
checking fireEquipmentOrder.group before accessing its name and rendering an
empty string (or other safe fallback) when group is null—mirror the CSV-side
behavior that uses a fallback when order.group is absent to prevent render-time
exceptions.

Comment on lines +142 to +145
async asyncData({ $axios, route }) {
const routeId = route.path.replace("/fire_equipment_orders/", "");
const url = "/fire_equipment_orders/" + routeId;
const response = await $axios.$get(url);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, locate and examine the specific file
find . -path "*admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue" -type f

Repository: NUTFes/group-manager-2

Length of output: 127


🏁 Script executed:

# List files to understand the structure
fd "_id.vue" --type f

Repository: NUTFes/group-manager-2

Length of output: 1709


🏁 Script executed:

# Check the file structure around the repository root
git ls-files | grep -E "pages.*_id\.vue|fire_equipment_orders"

Repository: NUTFes/group-manager-2

Length of output: 1933


🏁 Script executed:

# Examine the file content around the specified lines
cat -n admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue | head -150 | tail -20

Repository: NUTFes/group-manager-2

Length of output: 695


🏁 Script executed:

# Check the full context around asyncData
cat -n admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue | sed -n '140,150p'

Repository: NUTFes/group-manager-2

Length of output: 452


🏁 Script executed:

# Check file size to determine how to read it
wc -l admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue

Repository: NUTFes/group-manager-2

Length of output: 129


🏁 Script executed:

# Check if there's ESLint/Prettier configuration
find . -name ".eslintrc*" -o -name ".prettierrc*" -o -name "eslint.config.*" | head -5

Repository: NUTFes/group-manager-2

Length of output: 114


🏁 Script executed:

# Check how other _id.vue files handle route parameter extraction
grep -r "route\.path\.replace\|route\.params\.id" admin_view/nuxt-project/pages --include="*.vue" | head -20

Repository: NUTFes/group-manager-2

Length of output: 2323


🏁 Script executed:

# Check for any lint configuration in the nuxt project
ls -la admin_view/nuxt-project/ | grep -E "eslint|prettier|lint"

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

# Check if there's ESLint configuration in the admin_view/nuxt-project or root
find . -path "*/admin_view/nuxt-project/.eslintrc*" -o -path "*/admin_view/.eslintrc*" -o -path "./.eslintrc*" 2>/dev/null

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

# Check the package.json in the nuxt project to see if ESLint is configured
cat admin_view/nuxt-project/package.json | grep -A 10 -B 2 "eslint\|prettier" 2>/dev/null || echo "No eslint/prettier config found in this package.json"

Repository: NUTFes/group-manager-2

Length of output: 620


🏁 Script executed:

# Verify Nuxt routing behavior - check Nuxt documentation context and other similar implementations
grep -r "asyncData.*route" admin_view/nuxt-project/pages --include="*.vue" -A 2 | grep -E "route\.(params|path)" | head -10

Repository: NUTFes/group-manager-2

Length of output: 1161


🏁 Script executed:

# Let's verify the Nuxt context - check if route.params is available in asyncData
cat admin_view/nuxt-project/pages/cooking_process_order/_id.vue | head -160 | tail -25

Repository: NUTFes/group-manager-2

Length of output: 704


🏁 Script executed:

# Find the asyncData section in cooking_process_order/_id.vue
grep -n "asyncData" admin_view/nuxt-project/pages/cooking_process_order/_id.vue -A 10

Repository: NUTFes/group-manager-2

Length of output: 481


🏁 Script executed:

# Check a file that uses route.params.id properly
grep -n "asyncData" admin_view/nuxt-project/pages/assign_rental_items/_id.vue -A 5

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

# Double-check that route.params.id is the correct approach in Nuxt by examining the file structure
cat admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue | sed -n '1,20p'

Repository: NUTFes/group-manager-2

Length of output: 581


Use route.params.id instead of route.path.replace() to extract the route parameter.

Line 143's string manipulation approach is fragile and can break with trailing slashes or path variations. In Nuxt's dynamic routes (e.g., _id.vue), route.params.id is the correct and safer way to access route parameters.

Proposed fix
-    const routeId = route.path.replace("/fire_equipment_orders/", "");
+    const routeId = route.params.id;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async asyncData({ $axios, route }) {
const routeId = route.path.replace("/fire_equipment_orders/", "");
const url = "/fire_equipment_orders/" + routeId;
const response = await $axios.$get(url);
async asyncData({ $axios, route }) {
const routeId = route.params.id;
const url = "/fire_equipment_orders/" + routeId;
const response = await $axios.$get(url);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue` around lines 142
- 145, Replace fragile path string manipulation in asyncData: stop using
route.path.replace("/fire_equipment_orders/", "") and instead read the dynamic
segment from route.params.id; build the URL using that id (e.g.,
"/fire_equipment_orders/" + route.params.id) before calling $axios.$get so the
code is resilient to trailing slashes and path variations.

Comment on lines +188 to +210
async edit() {
const url = "/fire_equipment_orders/" + this.routeId;
const params = {
fire_equipment_order: {
name: this.name,
quantity: this.quantity,
fuel: this.fuel,
usage: this.usage,
is_takeaway: this.isTakeaway,
remark: this.remark,
}
};
await this.$axios.$put(url, params).then(() => {
this.openSnackBar("申請を編集しました");
this.reload();
this.closeEditModal();
});
},
async destroy() {
const url = "/fire_equipment_orders/" + this.routeId;
await this.$axios.$delete(url);
this.$router.push("/fire_equipment_orders");
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find the Vue file
fd -t f "_id.vue" admin_view/nuxt-project

Repository: NUTFes/group-manager-2

Length of output: 1709


🏁 Script executed:

# Check file structure to understand the path better
git ls-files | grep -E "(fire_equipment|_id\.vue)" | head -20

Repository: NUTFes/group-manager-2

Length of output: 1107


🏁 Script executed:

# Read the specific file and lines around the mentioned code
if [ -f "admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue" ]; then
  echo "=== File found, reading lines 185-215 ==="
  sed -n '185,215p' "admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue" | cat -n
else
  echo "File not found at expected path"
fi

Repository: NUTFes/group-manager-2

Length of output: 1128


🏁 Script executed:

# Check if there's a reload method definition in the file
if [ -f "admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue" ]; then
  echo "=== Searching for reload method ==="
  rg -n "reload\s*\(" "admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue" -A 3
fi

Repository: NUTFes/group-manager-2

Length of output: 387


🏁 Script executed:

# Check ESLint and Prettier configuration
fd -t f "\.(eslintrc|prettierrc|eslint\.config)" "admin_view/nuxt-project/" 2>/dev/null | head -5

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

# Check package.json for lint/format scripts
if [ -f "admin_view/nuxt-project/package.json" ]; then
  echo "=== Checking for lint scripts ==="
  cat "admin_view/nuxt-project/package.json" | grep -A 20 '"scripts"'
fi

Repository: NUTFes/group-manager-2

Length of output: 681


Add error handling and await for async operations in edit/destroy methods

The edit() and destroy() methods lack error handling for API failures, causing silent failures when network requests fail. Additionally, reload() is called without await on line 202, creating a race condition where the edit modal closes before data is refreshed.

Proposed fix
-    async edit() {
+    async edit() {
       const url = "/fire_equipment_orders/" + this.routeId;
       const params = {
         fire_equipment_order: {
           name: this.name,
           quantity: this.quantity,
           fuel: this.fuel,
           usage: this.usage,
           is_takeaway: this.isTakeaway,
           remark: this.remark,
         }
       };
-      await this.$axios.$put(url, params).then(() => {
+      try {
+        await this.$axios.$put(url, params);
         this.openSnackBar("申請を編集しました");
-        this.reload();
+        await this.reload();
         this.closeEditModal();
-      });
+      } catch (_) {
+        this.openSnackBar("申請の編集に失敗しました");
+      }
     },
     async destroy() {
       const url = "/fire_equipment_orders/" + this.routeId;
-      await this.$axios.$delete(url);
-      this.$router.push("/fire_equipment_orders");
+      try {
+        await this.$axios.$delete(url);
+        this.$router.push("/fire_equipment_orders");
+      } catch (_) {
+        this.openSnackBar("申請の削除に失敗しました");
+      }
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/fire_equipment_orders/_id.vue` around lines 188
- 210, The edit() and destroy() functions need proper async/await sequencing and
error handling: wrap the body of edit() and destroy() in try/catch, await the
axios calls, await this.reload() before calling this.closeEditModal() in edit(),
and on error call this.openSnackBar with an error message and optionally log the
error; similarly in destroy() await the $delete call and wrap the navigation in
the try block, handling errors in the catch. Target the edit, destroy, reload,
closeEditModal methods and the this.$axios.$put / this.$axios.$delete calls when
applying the change.

Comment on lines +221 to +262
async submit() {
const url = `/fire_equipment_orders`;
const params = {
fire_equipment_order: {
group_id: this.groupId,
name: this.name,
quantity: this.quantity,
fuel: this.fuel,
usage: this.usage,
is_takeaway: this.isTakeaway,
remark: this.remark,
},
};
await this.$axios.$post(url, params);
await this.refinement(this.refYearID);
this.closeAddModal();
this.openSnackBar("申請を追加しました");
// フォームの内容をリセット
this.groupId = null;
this.name = "";
this.quantity = 1;
this.fuel = "";
this.usage = "";
this.isTakeaway = false;
this.remark = "";
},
async downloadCSV() {
const url =
this.$config.apiURL +
"/api/v1/get_fire_equipment_orders_csv/" +
this.refYearID;
const { data } = await this.$axios.get(url, { responseType: "blob" });
const blob = new Blob([data], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `火気使用申請一覧_${this.refYears}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
this.openSnackBar("火気使用申請のCSVをダウンロードしました");
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "index.vue" -path "*/fire_equipment_orders/*" -type f

Repository: NUTFes/group-manager-2

Length of output: 129


🏁 Script executed:

cat -n ./admin_view/nuxt-project/pages/fire_equipment_orders/index.vue | sed -n '215,270p'

Repository: NUTFes/group-manager-2

Length of output: 1985


🏁 Script executed:

# Check for ESLint and Prettier configuration
find . -maxdepth 2 -name ".eslintrc*" -o -name "prettier.config.*" -o -name ".prettierrc*" | head -10

Repository: NUTFes/group-manager-2

Length of output: 89


🏁 Script executed:

find ./admin_view/nuxt-project -maxdepth 2 -name ".eslintrc*" -o -name "package.json" | head -5

Repository: NUTFes/group-manager-2

Length of output: 104


🏁 Script executed:

cat ./admin_view/nuxt-project/package.json | head -50

Repository: NUTFes/group-manager-2

Length of output: 1009


🏁 Script executed:

find ./admin_view/nuxt-project -name ".eslintrc*" -o -name "eslint.config.*"

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

find ./admin_view -maxdepth 1 -name ".eslintrc*" -o -name "eslint.config.*"

Repository: NUTFes/group-manager-2

Length of output: 48


🏁 Script executed:

find . -maxdepth 1 -name ".eslintrc*" && echo "---" && wc -l ./admin_view/nuxt-project/pages/fire_equipment_orders/index.vue

Repository: NUTFes/group-manager-2

Length of output: 137


🏁 Script executed:

cat -n ./admin_view/nuxt-project/pages/fire_equipment_orders/index.vue

Repository: NUTFes/group-manager-2

Length of output: 9157


submit()downloadCSV() メソッドの API 呼び出しにエラーハンドリングを追加してください。

Line 234 と Line 252 の API 呼び出しが失敗した場合、ユーザーに失敗が通知されません。これらはユーザーが操作するボタンに紐付いているため、エラー時のフィードバックが必要です。

修正案
    async submit() {
      const url = `/fire_equipment_orders`;
      const params = {
        fire_equipment_order: {
          group_id: this.groupId,
          name: this.name,
          quantity: this.quantity,
          fuel: this.fuel,
          usage: this.usage,
          is_takeaway: this.isTakeaway,
          remark: this.remark,
        },
      };
+     try {
        await this.$axios.$post(url, params);
        await this.refinement(this.refYearID);
        this.closeAddModal();
        this.openSnackBar("申請を追加しました");
+     } catch (_) {
+       this.openSnackBar("申請の追加に失敗しました");
+       return;
+     }
      // フォームの内容をリセット
      this.groupId = null;
      this.name = "";
      this.quantity = 1;
      this.fuel = "";
      this.usage = "";
      this.isTakeaway = false;
      this.remark = "";
    },
    async downloadCSV() {
      const url =
        this.$config.apiURL +
        "/api/v1/get_fire_equipment_orders_csv/" +
        this.refYearID;
+     let data;
+     try {
-       const { data } = await this.$axios.get(url, { responseType: "blob" });
+       ({ data } = await this.$axios.get(url, { responseType: "blob" }));
+     } catch (_) {
+       this.openSnackBar("CSVのダウンロードに失敗しました");
+       return;
+     }
      const blob = new Blob([data], { type: "text/csv;charset=utf-8;" });
      const link = document.createElement("a");
      link.href = window.URL.createObjectURL(blob);
      link.download = `火気使用申請一覧_${this.refYears}.csv`;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      window.URL.revokeObjectURL(link.href);
      this.openSnackBar("火気使用申請のCSVをダウンロードしました");
    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async submit() {
const url = `/fire_equipment_orders`;
const params = {
fire_equipment_order: {
group_id: this.groupId,
name: this.name,
quantity: this.quantity,
fuel: this.fuel,
usage: this.usage,
is_takeaway: this.isTakeaway,
remark: this.remark,
},
};
await this.$axios.$post(url, params);
await this.refinement(this.refYearID);
this.closeAddModal();
this.openSnackBar("申請を追加しました");
// フォームの内容をリセット
this.groupId = null;
this.name = "";
this.quantity = 1;
this.fuel = "";
this.usage = "";
this.isTakeaway = false;
this.remark = "";
},
async downloadCSV() {
const url =
this.$config.apiURL +
"/api/v1/get_fire_equipment_orders_csv/" +
this.refYearID;
const { data } = await this.$axios.get(url, { responseType: "blob" });
const blob = new Blob([data], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `火気使用申請一覧_${this.refYears}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
this.openSnackBar("火気使用申請のCSVをダウンロードしました");
},
async submit() {
const url = `/fire_equipment_orders`;
const params = {
fire_equipment_order: {
group_id: this.groupId,
name: this.name,
quantity: this.quantity,
fuel: this.fuel,
usage: this.usage,
is_takeaway: this.isTakeaway,
remark: this.remark,
},
};
try {
await this.$axios.$post(url, params);
await this.refinement(this.refYearID);
this.closeAddModal();
this.openSnackBar("申請を追加しました");
} catch (_) {
this.openSnackBar("申請の追加に失敗しました");
return;
}
// フォームの内容をリセット
this.groupId = null;
this.name = "";
this.quantity = 1;
this.fuel = "";
this.usage = "";
this.isTakeaway = false;
this.remark = "";
},
async downloadCSV() {
const url =
this.$config.apiURL +
"/api/v1/get_fire_equipment_orders_csv/" +
this.refYearID;
let data;
try {
({ data } = await this.$axios.get(url, { responseType: "blob" }));
} catch (_) {
this.openSnackBar("CSVのダウンロードに失敗しました");
return;
}
const blob = new Blob([data], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `火気使用申請一覧_${this.refYears}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
this.openSnackBar("火気使用申請のCSVをダウンロードしました");
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/fire_equipment_orders/index.vue` around lines
221 - 262, Wrap the API calls in both submit() and downloadCSV() with try/catch
blocks: for submit(), surround the this.$axios.$post(...) and the follow-up
calls (this.refinement(...), this.closeAddModal(), resetting form fields,
this.openSnackBar(...)) so they only run on success, and in the catch call
this.openSnackBar with a clear failure message and console.error the caught
error; for downloadCSV(), surround the this.$axios.get(...) and the
blob/download logic so they only run on success, and in the catch call
this.openSnackBar with a failure message and console.error the error; ensure you
still revokeObjectURL in a finally block if the link was created. Reference:
submit(), downloadCSV(), this.$axios.$post, this.$axios.get, this.refinement,
this.closeAddModal, this.openSnackBar.

Comment on lines +95 to +99
<td :class="{ unregistered: !group.cooking_process_order && group.group_category === 1 }">
<div v-if="group.cooking_process_order">◯</div>
<div v-else-if="group.group_category !== 1">ー</div>
<div v-else>✖️</div>
</td>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This cell is shifted ahead of its header.

headers still places 調理工程 after 模擬店平面図, but the body now renders cooking_process_order before 販売品/購入品/PR/... . From this cell onward, each status is shown under the wrong column label.

↔️ Suggested move

Remove this <td> here and reinsert the same block after the venue_map cell (current Lines 119-123), so the body order matches the header order.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin_view/nuxt-project/pages/order_status_check/index.vue` around lines 95 -
99, The cooking_process_order cell (<td :class="{ unregistered:
!group.cooking_process_order && group.group_category === 1 }"> with its three
<div> branches using group.cooking_process_order and group.group_category) is
rendered too early; remove this <td> from its current position and reinsert the
exact same block immediately after the venue_map cell so the row order aligns
with headers (place the cooking_process_order <td> right after the venue_map
cell block). Ensure you keep the class binding and the v-if / v-else-if / v-else
logic unchanged when moving it.

Comment thread admin_view/nuxt-project/pages/order_status_check/index.vue
Comment on lines +99 to +106
def get_groups_for_fire_equipment_order
fes_year_id = params[:fes_year_id]
@groups = if fes_year_id.present? && fes_year_id.to_i != 0
Group.where(fes_year_id: fes_year_id).order(:id)
else
Group.order(:id)
end
render json: { data: @groups }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
nl -ba api/app/controllers/api/v1/groups_api_controller.rb | sed -n '60,115p'
rg -n "resources :groups|get_group_index_for_admin_view|get_groups_for_fire_equipment_order" api/config/routes.rb api/app/controllers -g '*.rb'

Repository: NUTFes/group-manager-2

Length of output: 599


🏁 Script executed:

# Check the full routes context around the fire equipment order endpoint
sed -n '240,260p' api/config/routes.rb

# Check for groups index action in the controller
rg -A 10 "def index" api/app/controllers/api/v1/groups_api_controller.rb

# Check the full controller to understand all actions
head -120 api/app/controllers/api/v1/groups_api_controller.rb | tail -30

Repository: NUTFes/group-manager-2

Length of output: 2471


🏁 Script executed:

# Check if there's an index action in groups_api_controller
rg -n "def (index|get_.*groups)" api/app/controllers/api/v1/groups_api_controller.rb

# Get the full context of the routes around resources :groups
sed -n '95,115p' api/config/routes.rb

Repository: NUTFes/group-manager-2

Length of output: 932


🏁 Script executed:

# Check if there's a groups_controller (not groups_api_controller)
fd -e rb "groups_controller.rb|group_controller.rb" api/app/controllers/

# Check the groups_api_controller file size and structure
wc -l api/app/controllers/api/v1/groups_api_controller.rb

# See if there are any index methods anywhere in the groups controllers
rg -l "def index" api/app/controllers/api/v1/ | head -5

Repository: NUTFes/group-manager-2

Length of output: 217


🏁 Script executed:

# Read the full groups_api_controller to see all actions
cat -n api/app/controllers/api/v1/groups_api_controller.rb

# Check the standard groups_controller to see if it has index with query param support
head -80 api/app/controllers/groups_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 6338


Custom action naming violates RESTful conventions; consider get_refinement_groups pattern if filtering by year is a domain concern, or add query-param filtering to the standard index.

The get_groups_for_fire_equipment_order action uses query-param filtering (RESTful), but the custom action naming pattern contradicts the Follow RESTful controller patterns in Rails guideline. The codebase already demonstrates domain-specific filtering with get_refinement_groups (lines 62–81), which accepts multiple filter params including fes_year_id. Either consolidate this into that existing action or extend GroupsController#index to support fes_year_id filtering if this is a public API concern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/app/controllers/api/v1/groups_api_controller.rb` around lines 99 - 106,
The custom action get_groups_for_fire_equipment_order violates RESTful naming;
either remove it and reuse the existing get_refinement_groups (which already
accepts fes_year_id and other filters) or implement fes_year_id filtering in the
standard index action and update callers/routes accordingly. Specifically,
migrate the filtering logic (Group.where(fes_year_id: fes_year_id).order(:id) /
Group.order(:id)) into get_refinement_groups or GroupsController#index, ensure
the JSON response shape matches the original (render json: { data: `@groups` }),
and delete the get_groups_for_fire_equipment_order route/action so routes point
to the consolidated method. Also update any tests or clients to call the
consolidated endpoint.

Comment thread api/app/controllers/api/v1/order_status_check_api_controller.rb Outdated
Comment on lines +534 to +561
def output_fire_equipment_orders_csv
if params[:fes_year_id].to_i == 0
@fire_equipment_orders = FireEquipmentOrder.all
filename_year = '全'
else
fes_year = FesYear.find(params[:fes_year_id])
@fire_equipment_orders = FireEquipmentOrder.joins(:group).where(groups: { fes_year_id: fes_year.id })
filename_year = fes_year.year_num
end
bom = "\uFEFF"
csv_data = CSV.generate(bom) do |csv|
column_name = %w[ID 団体名 火気の名称 火気の台数 燃料 使用用途 持ち帰り 備考]
csv << column_name
@fire_equipment_orders.each do |order|
next if order.nil?

group_name = order.group&.name || ''
column_values = [
order.id,
group_name,
order.name,
order.quantity,
order.fuel_japanese,
order.usage,
order.is_takeaway ? 'はい' : 'いいえ',
order.remark
]
csv << column_values
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Eager-load group before iterating the CSV rows.

Both branches dereference order.group for every record, but the new query does not preload that association. Exporting a large year will turn into one extra query per row.

♻️ Suggested fix
   def output_fire_equipment_orders_csv
     if params[:fes_year_id].to_i == 0
-      `@fire_equipment_orders` = FireEquipmentOrder.all
+      `@fire_equipment_orders` = FireEquipmentOrder.includes(:group)
       filename_year = '全'
     else
       fes_year = FesYear.find(params[:fes_year_id])
-      `@fire_equipment_orders` = FireEquipmentOrder.joins(:group).where(groups: { fes_year_id: fes_year.id })
+      `@fire_equipment_orders` = FireEquipmentOrder.includes(:group)
+                                                 .joins(:group)
+                                                 .where(groups: { fes_year_id: fes_year.id })
       filename_year = fes_year.year_num
     end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def output_fire_equipment_orders_csv
if params[:fes_year_id].to_i == 0
@fire_equipment_orders = FireEquipmentOrder.all
filename_year = '全'
else
fes_year = FesYear.find(params[:fes_year_id])
@fire_equipment_orders = FireEquipmentOrder.joins(:group).where(groups: { fes_year_id: fes_year.id })
filename_year = fes_year.year_num
end
bom = "\uFEFF"
csv_data = CSV.generate(bom) do |csv|
column_name = %w[ID 団体名 火気の名称 火気の台数 燃料 使用用途 持ち帰り 備考]
csv << column_name
@fire_equipment_orders.each do |order|
next if order.nil?
group_name = order.group&.name || ''
column_values = [
order.id,
group_name,
order.name,
order.quantity,
order.fuel_japanese,
order.usage,
order.is_takeaway ? 'はい' : 'いいえ',
order.remark
]
csv << column_values
def output_fire_equipment_orders_csv
if params[:fes_year_id].to_i == 0
`@fire_equipment_orders` = FireEquipmentOrder.includes(:group)
filename_year = '全'
else
fes_year = FesYear.find(params[:fes_year_id])
`@fire_equipment_orders` = FireEquipmentOrder.includes(:group)
.joins(:group)
.where(groups: { fes_year_id: fes_year.id })
filename_year = fes_year.year_num
end
bom = "\uFEFF"
csv_data = CSV.generate(bom) do |csv|
column_name = %w[ID 団体名 火気の名称 火気の台数 燃料 使用用途 持ち帰り 備考]
csv << column_name
`@fire_equipment_orders.each` do |order|
next if order.nil?
group_name = order.group&.name || ''
column_values = [
order.id,
group_name,
order.name,
order.quantity,
order.fuel_japanese,
order.usage,
order.is_takeaway ? 'はい' : 'いいえ',
order.remark
]
csv << column_values
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/app/controllers/api/v1/output_csv_controller.rb` around lines 534 - 561,
The CSV export in output_fire_equipment_orders_csv iterates order.group for each
row but the queries don't eager-load that association, causing N+1 queries; fix
by eager-loading group on the `@fire_equipment_orders` query (add includes(:group)
to the FireEquipmentOrder.all branch and to the query currently written as
FireEquipmentOrder.joins(:group).where(...) or replace that with
FireEquipmentOrder.includes(:group).where(...)) so order.group is preloaded
during iteration.

Comment on lines +9 to +20
fes_year_id = params[:fes_year_id]
@fire_equipment_orders = if fes_year_id.present? && fes_year_id.to_i != 0
FireEquipmentOrder.joins(:group).where(groups: { fes_year_id: fes_year_id })
else
FireEquipmentOrder.includes(:group).all
end
orders_with_fuel_japanese = @fire_equipment_orders.map do |order|
order.as_json(include: { group: { only: %i[id name] } }).merge(
fuel_japanese: order.fuel_japanese
)
end
render json: fmt(ok, orders_with_fuel_japanese)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The filtered index path still does N+1 on group.

joins(:group) filters correctly, but as_json(include: { group: ... }) will still lazy-load the association unless it is eager-loaded. The admin list will slow down noticeably as row counts grow.

♻️ Suggested fix
     `@fire_equipment_orders` = if fes_year_id.present? && fes_year_id.to_i != 0
-                               FireEquipmentOrder.joins(:group).where(groups: { fes_year_id: fes_year_id })
+                               FireEquipmentOrder.eager_load(:group).where(groups: { fes_year_id: fes_year_id })
                              else
                                FireEquipmentOrder.includes(:group).all
                              end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/app/controllers/fire_equipment_orders_controller.rb` around lines 9 - 20,
The current index filters with FireEquipmentOrder.joins(:group) but later calls
order.as_json(include: { group: ... }) which will lazy-load `group` causing N+1;
modify the branch that sets `@fire_equipment_orders` so the records are
eager-loaded (e.g., use includes(:group) or combine joins(...).includes(:group)
/ preload(:group)) when filtering by fes_year_id so `@fire_equipment_orders`
always has groups preloaded before the map that calls `as_json(... )` and
`fuel_japanese`.

@hikahana hikahana merged commit cdf296b into gm3/develop Apr 18, 2026
3 checks passed
@hikahana hikahana deleted the feat/hikahana/fire-equpment-admin-ui branch April 18, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants