Skip to content

Feat/inaba/2015 add health check front#2044

Open
batcho0428 wants to merge 30 commits intogm3/developfrom
feat/inaba/2015-add-health-check-front
Open

Feat/inaba/2015 add health check front#2044
batcho0428 wants to merge 30 commits intogm3/developfrom
feat/inaba/2015-add-health-check-front

Conversation

@batcho0428
Copy link
Copy Markdown
Collaborator

@batcho0428 batcho0428 commented May 8, 2026

対応Issue

resolve #2015

概要

  • 保健所提出項目確認画面の開発
  • 一覧ページの追加
  • 詳細ページの追加

実装詳細

  • 一覧ページにステータス表示とすべてが承認済みになった際に〇を表示するロジックを実装
  • 一覧ページに申請状況を同期するボタン(申請状況を確認して未申請→未確認のステータスを作成する)を実装。※ユーザー画面対応までの暫定対策用
  • ステータス更新ドロップダウンリストの追加
  • 各申請のデータ表示及び編集機能の実装
  • 編集モーダルを共通化するためにコンポーネントとして分離

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

image image

テスト項目

  • ステータスが表示されるか
  • ステータス更新ボタンで未提出→未確認へステータスが変更されるか
  • ドロップダウンリストで登録したステータスが反映されるか
  • 編集モーダルで編集できるか
  • メッセージ送信でメッセージがDBに保存されるか(実際のメール送信ロジックは後日追加)
  • フィルタが正しく動作するか

備考

Summary by CodeRabbit

  • New Features

    • Health center document review pages with per-application status controls, comments, navigation and sync action
    • New confirmation menu entry and simple horizontal divider UI
    • Dedicated edit modals for cooking process, employee, food product, purchase list, rental order, venue map (with file validation and upload progress)
  • Refactor

    • Replaced inline edit forms with modal components
    • Improved SubHeader back-navigation flow
  • Tests

    • Tests for submission status creation and updates

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Warning

Rate limit exceeded

@batcho0428 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 8 minutes and 45 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99ee4f8d-154b-40e0-ad2e-24eaf8e85b2e

📥 Commits

Reviewing files that changed from the base of the PR and between bd38165 and 488314b.

📒 Files selected for processing (4)
  • .github/workflows/user-code-quality.yml
  • admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue
  • admin_view/nuxt-project/pages/health_center_document_review/_id.vue
  • admin_view/nuxt-project/pages/health_center_document_review/index.vue
📝 Walkthrough

Walkthrough

Adds backend submission-status helpers and endpoints, new admin index/detail pages, six reusable Vue edit-modals, replaces inline edit forms with modals across pages, and includes model tests.

Changes

Health Center Document Review System

Layer / File(s) Summary
Data Models & Status Management
api/app/models/health_center_submission_status.rb, api/app/models/food_product.rb
Added DEFAULT_STATUS and helper class methods (ensure_for_group_and_application_type!, insert_default_for_group_and_application_type!, default_submissions_for) and an after_create callback on FoodProduct to insert a default HealthCenterSubmissionStatus.
API Endpoints & Controllers
api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb, api/config/routes.rb
Centralized status persistence with save_submission_status, added upsert_health_center_submission_status and sync_health_center_submission_statuses actions, refactored comment creation to resolve/init submission records, ensured submission payloads include all application types, adjusted equipment→rental mapping, and added the upsert route.
Reusable Edit Modal Components
admin_view/nuxt-project/components/edit-modals/*EditModal.vue
Six new modal components (CookingProcessOrder, Employee, FoodProduct, PurchaseList, RentalOrder, VenueMap) with prop-driven initialization, immediate watchers, validation, API submission, upload flow for VenueMap, and emitted saved/close/error events.
Document Review Detail Page
admin_view/nuxt-project/pages/health_center_document_review/_id.vue
New detail page that fetches group/submission and related lists, renders status selectors and entity lists, opens edit modals, posts comments, and updates local submission state.
Document List & Filtering
admin_view/nuxt-project/pages/health_center_document_review/index.vue
New index page with refinements (year/international/external), persisted search, group table with per-step status badges and completeness indicator, and a sync action to refresh statuses.
Page Integration & Navigation
admin_view/nuxt-project/pages/{cooking_process_order,employees,food_products,purchase_lists,rental_orders,venue_maps}/_id.vue
admin_view/nuxt-project/components/{Menu,SubHeader,HorizontalRule}.vue
Replaced inline page edit forms with dedicated modal components; added a “確認画面” menu entry; changed SubHeader back behavior to a method; added HorizontalRule.vue; minor import/order tweak.
Tests
api/test/models/{food_product,health_center_submission_status}_test.rb
Added tests validating automatic HealthCenterSubmissionStatus creation on FoodProduct creation and ensure_for_group_and_application_type! behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant DetailPage
  participant EditModal
  participant API
  Browser->>DetailPage: navigate to /health_center_document_review/:id
  DetailPage->>API: GET group & submissions & related lists
  Browser->>DetailPage: open EditModal for item
  EditModal->>API: PUT/POST edit
  API-->>EditModal: success
  EditModal-->>DetailPage: emit saved
  DetailPage->>API: POST upsert_health_center_submission_status
  API-->>DetailPage: updated submission
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • KokiWakatsuki
  • hikahana
  • harata-t

Poem

🐰 I hopped through routes and Vue-lit streams,

I stitched statuses, modals, and scenes.
Pages hum, uploads take flight,
Comments land, badges gleam at night.
A tiny rabbit cheers: all set — good night!

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main addition: a health-check front-end feature for issue #2015, matching the core changeset.
Description check ✅ Passed The description provides linked issue, overview, implementation details, test items, and includes screenshots, adequately covering the PR requirements.
Linked Issues check ✅ Passed Code changes implement all stated objectives from #2015: front-end health-center submission UI with organization-level aggregation, list page with status display/sync, detail page with data editing.
Out of Scope Changes check ✅ Passed Changes focus on front-end UI (new components, pages) and supporting API/model updates for health-center submission workflow. One minor unrelated fix in order_status_check_api_controller appears incidental and does not significantly impede scope.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/inaba/2015-add-health-check-front

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
admin_view/nuxt-project/pages/rental_orders/_id.vue (1)

142-162: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

console.log in dead edit() method — ESLint no-console violation

The edit() method is no longer invoked from anywhere in the template after the modal refactor; it is dead code. The console.log(url) on line 152 will trigger ESLint's no-console rule and cause CI to fail. The entire method (and its URL-building logic, which is now duplicated inside RentalOrderEditModal) should be removed.

As per coding guidelines, "ESLint と Prettier の警告は必ず解消し、CI で落ちない状態にしてからプルリクエストを作成する".

🛠️ Proposed fix
-   async edit() {
-     const url =
-       "/rental_orders/" +
-       this.routeId +
-       "?group_id=" +
-       this.rentalOrder.rental_order.group_id +
-       "&rental_item_id=" +
-       this.rentalItemID +
-       "&num=" +
-       this.num;
-     console.log(url);
-
-     await this.$axios.$put(url).then((response) => {
-       this.openSnackBar("物品申請を編集しました");
-       this.groupID = null;
-       this.rentalItemID = null;
-       this.num = null;
-       this.reload(response.data.id);
-       this.closeEditModal();
-     });
-   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/rental_orders/_id.vue` around lines 142 - 162,
The edit() method in the component is dead and contains a console.log(url) that
violates ESLint no-console; remove the entire async edit() function (including
the URL-building and console.log) from the component and rely on the existing
logic inside RentalOrderEditModal instead, and also remove any unused local
state or resets that only existed for this method (e.g., groupID, rentalItemID,
num resets) if they are now unused; ensure no remaining references to edit()
remain in the component.
🧹 Nitpick comments (5)
admin_view/nuxt-project/pages/food_products/_id.vue (1)

67-72: ⚡ Quick win

Remove stale parent-side edit state now that edit is delegated to child modal.

After switching to EditModalsFoodProductEditModal, the parent’s inline-edit fields/mutations are redundant (name/isCooking/first/second/isCookingList, value-copying in openEditModal, and old edit() path). Keeping both flows increases drift risk.

Refactor direction
   data() {
     return {
       isOpenEditModal: false,
       isOpenDeleteModal: false,
-      isCookingList: [
-        { id: 1, text: "調理あり", value: true },
-        { id: 2, text: "調理なし", value: false },
-      ],
-      name: "",
-      isCooking: null,
-      first: null,
-      second: null,
       isOpenSnackBar: false,
     };
   },
@@
   methods: {
     openEditModal() {
-      this.name = this.foodProduct.food_product.name;
-      this.isCooking = this.foodProduct.food_product.is_cooking;
-      this.first = this.foodProduct.food_product.first_day_num;
-      this.second = this.foodProduct.food_product.second_day_num;
       this.isOpenEditModal = true;
     },
@@
-    async edit() {
-      // old inline modal update flow (remove)
-    },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/food_products/_id.vue` around lines 67 - 72,
The parent still maintains redundant edit state and mutation logic that was
replaced by the child EditModalsFoodProductEditModal; remove the stale reactive
fields (name, isCooking, first, second, isCookingList) plus any value-copying in
openEditModal and the old edit() mutation path so the parent only opens the
modal and passes :food-product, listens for `@close` and `@saved` (closeEditModal
and reload). Ensure closeEditModal and reload remain wired, delete unused
imports/refs and tests tied to the removed parent-side edit logic, and run the
app to confirm no remaining references to the removed symbols.
admin_view/nuxt-project/pages/venue_maps/_id.vue (1)

70-75: ⚡ Quick win

Good modal extraction; now remove legacy upload logic from this page

After delegating edit/upload to EditModalsVenueMapEditModal, the old page-level upload state/methods should be removed to avoid two diverging code paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/venue_maps/_id.vue` around lines 70 - 75,
Remove the leftover page-level upload state and handlers now that
EditModalsVenueMapEditModal handles uploads: delete any reactive vars like
isUploading, uploadProgress, selectedFile (or fileInput), and isOpenEditModal
only if it's solely for the old upload flow; remove methods such as
handleUpload, onFileChange, uploadVenueMap (or upload*), plus any
watchers/computed tied to those vars and the template file input/button for
uploads; keep the modal props/events (venue_map, `@close`="closeEditModal",
`@saved`="reload") and ensure closeEditModal and reload remain only if used by the
modal, removing unused imports and references to the removed symbols.
admin_view/nuxt-project/pages/cooking_process_order/_id.vue (1)

140-143: ⚡ Quick win

Dead code left over from modal extraction

Now that editing is fully delegated to <EditModalsCookingProcessOrderEditModal>, the following code is unreachable and should be removed:

Area Lines Why dead
data() properties 140–143 food_product_id, pre_open_kitchen, during_open_kitchen, tent are only read by edit() and set in openEditModal() — neither path is live any more
openEditModal() body 161–176 All the setup logic (setting the four data props) is vestigial; only this.isOpenEditModal = true (line 169/175) matters now
edit() method 201–249 No longer called from anywhere in the template; the inline <EditModal> slot that wired it was removed
.radio-group style 268–274 No elements with this class remain in the template after the inline form was removed

Keeping the dead edit() method is especially risky: a future contributor may try to fix a bug there without realising it is never invoked.

♻️ Proposed cleanup diff
   data() {
     return {
       cooking_process_order: {},
       isOpenEditModal: false,
       isOpenDeleteModal: false,
       isOpenSnackBar: false,
       snackMessage: null,
-      food_product_id: null,
-      pre_open_kitchen: null,
-      during_open_kitchen: null,
-      tent: "",
     };
   },
   openEditModal() {
-    if (this.cooking_process_order.cooking_process_order) {
-      this.food_product_id = this.cooking_process_order.food_product.id;
-      this.pre_open_kitchen =
-        this.cooking_process_order.cooking_process_order.pre_open_kitchen;
-      this.during_open_kitchen =
-        this.cooking_process_order.cooking_process_order.during_open_kitchen;
-      this.tent =
-        this.cooking_process_order.cooking_process_order.tent;
-      this.isOpenEditModal = true;
-    } else {
-      this.food_product_id = this.cooking_process_order.food_product.id;
-      this.pre_open_kitchen = null;
-      this.during_open_kitchen = null;
-      this.tent = null;
-      this.isOpenEditModal = true;
-    }
+    this.isOpenEditModal = true;
   },
-    async edit() {
-      if (
-        this.pre_open_kitchen === null ||
-        this.during_open_kitchen === null ||
-        this.tent === ""
-      ) {
-        this.openSnackBar("調理工程申請を全て入力してください");
-        return;
-      }
-      // ... (entire method removed)
-    },
 <style scoped>
 td {
   width: 70%;
 }
 th {
   width: 15%;
 }
-
-.radio-group {
-  display: flex;
-  align-items: center;
-  justify-content: left;
-  flex-flow: row nowrap;
-  width: 500px;
-}
 </style>

Also applies to: 160-177, 201-249, 268-274

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/cooking_process_order/_id.vue` around lines 140
- 143, Remove the dead editing code left after extracting
EditModalsCookingProcessOrderEditModal: delete the unused data properties
food_product_id, pre_open_kitchen, during_open_kitchen, and tent from data(),
strip the vestigial setup inside openEditModal() so it only sets
this.isOpenEditModal = true, remove the entire edit() method (unused), and
remove the .radio-group CSS block from the component styles; use the
function/class names openEditModal and edit and the data property names to
locate the code to remove.
admin_view/nuxt-project/pages/rental_orders/_id.vue (1)

52-57: ⚡ Quick win

Redundant API fetch and orphaned data properties left over from the refactor

Now that EditModalsRentalOrderEditModal owns its own rentable-items fetch and local form state, openEditModal() (lines 110–117) still issues a superfluous GET /api/v1/get_all_rentable_items on every modal open and sets this.rentableItemList (undeclared in data()), this.rentalItemID, and this.num. The rentalItemID and num entries in data() (lines 86–87) are also now dead.

🛠️ Proposed cleanup
  data() {
    return {
      isOpenEditModal: false,
      isOpenDeleteModal: false,
-     rentalItemID: null,
-     num: null,
      isOpenSnackBar: false,
    };
  },
- async openEditModal() {
-   this.rentalItemID = this.rentalOrder.rental_order.rental_item_id;
-   this.num = this.rentalOrder.rental_order.num;
-   const rentableItemsUrl = "/api/v1/get_all_rentable_items";
-   const resRentableItems = await this.$axios.$get(rentableItemsUrl);
-   this.rentableItemList = resRentableItems.data;
-   this.isOpenEditModal = true;
- },
+ openEditModal() {
+   this.isOpenEditModal = true;
+ },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/rental_orders/_id.vue` around lines 52 - 57,
openEditModal() still triggers a redundant GET /api/v1/get_all_rentable_items
and assigns this.rentableItemList, this.rentalItemID and this.num even though
EditModalsRentalOrderEditModal now handles its own rentable-items fetch and
local state; remove the GET request and the assignments to
rentableItemList/rentalItemID/num from the openEditModal() implementation and
delete the now-unused data() properties rentableItemID and num (and any
reference to rentableItemList) so the parent component only opens the modal and
passes rentalOrder to EditModalsRentalOrderEditModal.
api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb (1)

4-4: ⚡ Quick win

Regenerate OpenAPI documentation after adding this new endpoint.

A new POST upsert_health_center_submission_status action was wired in the routes. As per coding guidelines, "Regenerate OpenAPI documentation with make openapi when updating API specifications."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`
at line 4, A new API action upsert_health_center_submission_status was added but
the OpenAPI spec wasn't updated; regenerate the API documentation by running the
project's OpenAPI generation task (e.g., `make openapi`) so the new POST
upsert_health_center_submission_status endpoint appears in the spec and
committed docs, then review the generated docs for the new route,
request/response schemas, and add any missing documentation where the controller
(HealthCenterSubmissionStatusesApiController) defines parameters or response
shapes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@admin_view/nuxt-project/components/edit-modals/CookingProcessOrderEditModal.vue`:
- Around line 70-93: The edit() method currently calls this.$axios.$put / $post
without error handling, so wrap the network call(s) in a try/catch: call
getCookingProcessOrder() and build cookingProcessOrderData as before, then
inside a try block perform either this.$axios.$put(editUrl, {
cooking_process_order: cookingProcessOrderData }) or
this.$axios.$post('/cooking_process_orders', { cooking_process_order:
cookingProcessOrderData }); in the catch block emit the existing error event
(this.$emit("error", errMessage)) with a useful message or err.toString() and
return (do not emit saved/close on failure); keep this.$emit("saved",
this.food_product_id) and this.$emit("close") only after the axios call
succeeds.

In `@admin_view/nuxt-project/components/edit-modals/EmployeeEditModal.vue`:
- Around line 78-88: The URL is being built by concatenating raw fields
(employee.id, this.groupId, this.name, this.studentId, this.stoolTestID) which
can break if values contain &, = or ?; update the code that constructs url in
EmployeeEditModal.vue to build a proper query string using encoded parameters
(e.g., URLSearchParams or encodeURIComponent for each value) so group_id, name,
student_id and stool_test_id are URL-encoded before appending to "/employees/" +
employee.id; ensure the final url uses the encoded query string rather than raw
concatenation.

In `@admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue`:
- Around line 70-82: The composed URL in FoodProductEditModal.vue (variable url
built from foodProduct.id, foodProduct.group_id, this.name, this.isCooking,
this.first, this.second) appends raw form values into the query string; wrap all
query values with proper encoding (e.g., use encodeURIComponent for this.name,
this.first, this.second, this.isCooking and foodProduct.group_id) or build the
query via URLSearchParams to ensure characters like &/=/? are escaped before
assigning url. Update the code that constructs url to encode each query value
(or replace the string concat with URLSearchParams) so the request payload is
not corrupted by unescaped characters.

In `@admin_view/nuxt-project/components/edit-modals/PurchaseListEditModal.vue`:
- Around line 107-123: The URL is built by raw string concatenation (const url)
using fields like purchaseList.id, purchaseList.food_product_id, this.shopID,
this.purchase_date, this.items, this.isFresh, this.url, and this.remark which
can contain characters (e.g., &, ?) that break the query; replace the
concatenation with an encoded query using URLSearchParams (or move parameters
into a request body) so each value is percent-encoded before sending—construct
the params from keys (food_product_id, shop_id, purchase_date, items, is_fresh,
url, remark), append them to the endpoint or send as body, and update the code
paths that build the URL (the const url and the same pattern at lines 125-128)
to use this safe encoding approach.

In `@admin_view/nuxt-project/components/edit-modals/RentalOrderEditModal.vue`:
- Around line 72-88: The edit() method (and similarly fetchRentableItems())
performs an axios PUT/GET without error handling, causing unhandled promise
rejections and no user feedback on failure; wrap the axios call in a try/catch
(or add a .catch()) inside edit() and fetchRentableItems(), handle errors by
setting a local error state or emitting an error event (e.g.,
this.$emit("error", err) or this.error = err.message) and show user feedback
(toast/validation message), and ensure the success branch still emits "saved"
and "close" only on success—refer to the edit() function and
fetchRentableItems() to locate the spots to add the try/catch and error
handling.
- Around line 48-49: The assignment using falsy-coercion is turning a valid 0
into null for the property num; replace the use of `rentalOrder.num || null`
with a nullish check (e.g. use the nullish coalescing operator or an explicit
undefined check) so 0 is preserved (for example, change the expression that sets
`num` in RentalOrderEditModal.vue to use `rentalOrder.num ?? null` or `typeof
rentalOrder.num === 'undefined' ? null : rentalOrder.num`); ensure similar logic
only if needed for `rentalItemID`.

In `@admin_view/nuxt-project/components/edit-modals/VenueMapEditModal.vue`:
- Around line 6-7: The groupName input is currently bound (v-model="groupName")
but not included in the API payload so edits are never saved; either mark the
field read-only to reflect that it’s display-only (e.g., change the input to
readonly/disabled and remove v-model) or include groupName in the save
handler/payload used by the component's save method so updates are persisted;
update both the input at the top (v-model="groupName") and the corresponding
save logic (the component's save/submit method) and also apply the same change
for the other instance referenced around lines 125-129 to keep UX consistent.
- Around line 22-23: The button disabling state (isPush.disabled) is being
toggled but not bound to the UI and submit state isn't reset on failure; wire
the CommonButton :disabled prop to the reactive submit flag (use isPush.disabled
instead of !isFile or combine both) for the edit/save buttons (CommonButton
element and related buttons shown around lines with buttonState), and update
save/upload handlers (functions like edit, saveVenueMap, uploadMap or whichever
methods toggle isPush) to set isPush.disabled = true at start and ensure
isPush.disabled = false in every failure and finally path (catch/finally) so
failed uploads/saves restore the UI and prevent duplicate submits. Ensure any UI
state variables (buttonState/loading indicators) are also restored in those
error paths.
- Around line 94-95: The code uses ref(this.$storage, file.name) which can cause
filename collisions; change to build a unique storage path (e.g., include a
venue/group id and a timestamp or generated UUID and/or a dedicated folder) and
use that path when creating storageRef and uploadTask; locate the storageRef and
uploadTask creation (symbols: storageRef, uploadTask, this.$storage, file.name)
and replace file.name with the constructed uniquePath so each upload is stored
under a namespaced/unique key (e.g.,
`${groupId}/venues/${venueId}/${timestamp}_${file.name}` or a UUID-based
filename).

In `@admin_view/nuxt-project/components/SubHeader.vue`:
- Around line 59-65: The onSubHeaderBack method uses this.$router.push (in the
branch checking this.parentPageLink and the fallback calling
this.resolveIndexPath) but doesn't attach a .catch(), causing unhandled promise
rejections; update both this.$router.push(...) calls in onSubHeaderBack to
append a .catch() that swallows or properly handles benign navigation errors
(e.g., empty handler or check for NavigationDuplicated) so navigation failures
do not surface as unhandled rejections.

In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue`:
- Around line 788-817: Wrap the network call in onSubmitComment in a try/catch
around the this.$axios.$post to prevent silent failures; if the POST to
HEALTH_CENTER_COMMENT_CREATE_ENDPOINT throws, catch the error, do not clear
this.commentBody, do not mutate submission or push a comment, and surface the
failure to the user (e.g. call a UI notifier like this.$toast.error or set an
error state such as this.commentError) and optionally log the error for
debugging; ensure the success-path behavior (setting submission.id and appending
commentRes.data) only runs inside the try after a successful response.
- Around line 765-787: The onStatusChange handler does not handle API failures:
wrap the await this.$axios.$post(HEALTH_CENTER_STATUS_UPDATE_ENDPOINT, payload)
call in a try/catch inside the onStatusChange method, and on error revert the
UI-backed value by resetting the submission status via
getSubmission(applicationType) (or reloading the submission object) so the
native <select> reflects the original status; also surface the failure (e.g.,
rethrow or call a provided error/toast handler) so the user gets feedback.
Ensure you still update savedSubmission.id/status only on successful response.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue`:
- Around line 388-416: The calls to fetch unregistered groups in
fetchFilteredData() and searchGroups() use await
this.$axios.$get("/un_registered_groups") without error handling, causing
unhandled rejections; wrap each API call in a try/catch around the await
this.$axios.$get("/un_registered_groups") in both fetchFilteredData and
searchGroups, set a safe fallback for this.unregisteredGroups (e.g., empty
array) in the catch, and optionally log the error (e.g., via console.error or a
logger) so the UI update flow (groups = this.filterGroups(...), localStorage
handling, window.scrollTo) continues even if the request fails.
- Around line 229-279: The mounted hook adds a global scroll listener but never
removes it, causing duplicate handlers on navigation; add a lifecycle hook to
remove it (e.g., beforeDestroy for Vue 2 or beforeUnmount/unmounted for Vue 3)
that calls window.removeEventListener("scroll", this.saveScrollPosition) so the
listener registered in mounted() is properly cleaned up; reference the mounted()
registration and the saveScrollPosition method when adding the corresponding
removal hook.
- Around line 350-385: The updateFilters method is using item_id as an array
index (e.g., name_list[item_id - 1]) which breaks when IDs are sparse or
historic; instead, look up the selected item by matching its id/value field in
the corresponding list (yearList, groupCategories, internationalList,
externalList) — e.g., find entry where entry.id === item_id (or entry.value ===
item_id depending on the list) and use that entry's display field (year_num,
name, value) to set refYears/refGroupCategories/refInternational/refExternal;
keep the existing special-case for item_id == 0 (ALL), and add a safe fallback
when no entry is found to avoid accessing undefined.
- Around line 493-505: The completion check in isHealthCenterDocumentComplete
currently always includes group.equipment and fails when a field is "申請不要"
(displayed as "ー"); update isHealthCenterDocumentComplete to first map the
statuses (including group.equipment) through this.normalizeStatus and then
filter out any statuses that represent "not required" (e.g., normalized value
like "not_required" or original "ー") before performing .every(status => status
=== "approved"); use normalizeStatus and the existing statuses array so required
fields are validated but "申請不要" entries are excluded from the completion
decision.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`:
- Around line 126-128: The controller currently calls
HealthCenterSubmissionStatus.find with
params[:health_center_submission_status_id] inside resolve_submission_status,
which will raise ActiveRecord::RecordNotFound and produce an HTML 404; change
this to a safe lookup and return the fallback JSON path instead: either use
HealthCenterSubmissionStatus.find_by(id:
params[:health_center_submission_status_id]) or rescue
ActiveRecord::RecordNotFound around the find call and return default_status (or
nil) so the controller continues to respond with the same structured JSON error
path as other branches.

In `@api/app/models/food_product.rb`:
- Line 8: The after_create_commit callback in FoodProduct uses
after_create_commit :ensure_health_center_submission_status which swallows
exceptions; change it to use after_create
:ensure_health_center_submission_status so the status creation runs inside the
transaction and any failures in ensure_health_center_submission_status will roll
back FoodProduct creation and surface errors; update related tests to expect
transactional behavior and ensure ensure_health_center_submission_status still
performs any idempotency or side-effect handling needed when moved into the
transaction.

In `@api/test/models/food_product_test.rb`:
- Around line 17-18: The test expectation is incorrect:
FoodProduct#ensure_health_center_submission_status calls
insert_default_for_group_and_application_type! which sets
submission_status.status to :unsubmitted via the find_or_create_by! block, so
update the assertion in food_product_test (the status lookup using
HealthCenterSubmissionStatus.find_by with group: groups(:two), application_type:
:food_product) to expect 'unsubmitted' instead of 'unapproved' to match the
persisted value.

---

Outside diff comments:
In `@admin_view/nuxt-project/pages/rental_orders/_id.vue`:
- Around line 142-162: The edit() method in the component is dead and contains a
console.log(url) that violates ESLint no-console; remove the entire async edit()
function (including the URL-building and console.log) from the component and
rely on the existing logic inside RentalOrderEditModal instead, and also remove
any unused local state or resets that only existed for this method (e.g.,
groupID, rentalItemID, num resets) if they are now unused; ensure no remaining
references to edit() remain in the component.

---

Nitpick comments:
In `@admin_view/nuxt-project/pages/cooking_process_order/_id.vue`:
- Around line 140-143: Remove the dead editing code left after extracting
EditModalsCookingProcessOrderEditModal: delete the unused data properties
food_product_id, pre_open_kitchen, during_open_kitchen, and tent from data(),
strip the vestigial setup inside openEditModal() so it only sets
this.isOpenEditModal = true, remove the entire edit() method (unused), and
remove the .radio-group CSS block from the component styles; use the
function/class names openEditModal and edit and the data property names to
locate the code to remove.

In `@admin_view/nuxt-project/pages/food_products/_id.vue`:
- Around line 67-72: The parent still maintains redundant edit state and
mutation logic that was replaced by the child EditModalsFoodProductEditModal;
remove the stale reactive fields (name, isCooking, first, second, isCookingList)
plus any value-copying in openEditModal and the old edit() mutation path so the
parent only opens the modal and passes :food-product, listens for `@close` and
`@saved` (closeEditModal and reload). Ensure closeEditModal and reload remain
wired, delete unused imports/refs and tests tied to the removed parent-side edit
logic, and run the app to confirm no remaining references to the removed
symbols.

In `@admin_view/nuxt-project/pages/rental_orders/_id.vue`:
- Around line 52-57: openEditModal() still triggers a redundant GET
/api/v1/get_all_rentable_items and assigns this.rentableItemList,
this.rentalItemID and this.num even though EditModalsRentalOrderEditModal now
handles its own rentable-items fetch and local state; remove the GET request and
the assignments to rentableItemList/rentalItemID/num from the openEditModal()
implementation and delete the now-unused data() properties rentableItemID and
num (and any reference to rentableItemList) so the parent component only opens
the modal and passes rentalOrder to EditModalsRentalOrderEditModal.

In `@admin_view/nuxt-project/pages/venue_maps/_id.vue`:
- Around line 70-75: Remove the leftover page-level upload state and handlers
now that EditModalsVenueMapEditModal handles uploads: delete any reactive vars
like isUploading, uploadProgress, selectedFile (or fileInput), and
isOpenEditModal only if it's solely for the old upload flow; remove methods such
as handleUpload, onFileChange, uploadVenueMap (or upload*), plus any
watchers/computed tied to those vars and the template file input/button for
uploads; keep the modal props/events (venue_map, `@close`="closeEditModal",
`@saved`="reload") and ensure closeEditModal and reload remain only if used by the
modal, removing unused imports and references to the removed symbols.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`:
- Line 4: A new API action upsert_health_center_submission_status was added but
the OpenAPI spec wasn't updated; regenerate the API documentation by running the
project's OpenAPI generation task (e.g., `make openapi`) so the new POST
upsert_health_center_submission_status endpoint appears in the spec and
committed docs, then review the generated docs for the new route,
request/response schemas, and add any missing documentation where the controller
(HealthCenterSubmissionStatusesApiController) defines parameters or response
shapes.
🪄 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: a27185c2-20ce-48fa-8250-eec83b714823

📥 Commits

Reviewing files that changed from the base of the PR and between 7205f52 and 909caa4.

📒 Files selected for processing (24)
  • admin_view/nuxt-project/components/HorizontalRule.vue
  • admin_view/nuxt-project/components/Menu.vue
  • admin_view/nuxt-project/components/SubHeader.vue
  • admin_view/nuxt-project/components/edit-modals/CookingProcessOrderEditModal.vue
  • admin_view/nuxt-project/components/edit-modals/EmployeeEditModal.vue
  • admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue
  • admin_view/nuxt-project/components/edit-modals/PurchaseListEditModal.vue
  • admin_view/nuxt-project/components/edit-modals/RentalOrderEditModal.vue
  • admin_view/nuxt-project/components/edit-modals/VenueMapEditModal.vue
  • admin_view/nuxt-project/pages/cooking_process_order/_id.vue
  • admin_view/nuxt-project/pages/employees/_id.vue
  • admin_view/nuxt-project/pages/food_products/_id.vue
  • admin_view/nuxt-project/pages/health_center_document_review/_id.vue
  • admin_view/nuxt-project/pages/health_center_document_review/index.vue
  • admin_view/nuxt-project/pages/purchase_lists/_id.vue
  • admin_view/nuxt-project/pages/rental_orders/_id.vue
  • admin_view/nuxt-project/pages/venue_maps/_id.vue
  • api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb
  • api/app/controllers/api/v1/order_status_check_api_controller.rb
  • api/app/models/food_product.rb
  • api/app/models/health_center_submission_status.rb
  • api/config/routes.rb
  • api/test/models/food_product_test.rb
  • api/test/models/health_center_submission_status_test.rb

Comment thread admin_view/nuxt-project/components/edit-modals/EmployeeEditModal.vue Outdated
Comment thread admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue Outdated
Comment thread admin_view/nuxt-project/components/edit-modals/PurchaseListEditModal.vue Outdated
Comment thread admin_view/nuxt-project/components/edit-modals/RentalOrderEditModal.vue Outdated
Comment thread api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb Outdated
Comment thread api/app/models/food_product.rb Outdated
Comment thread api/test/models/food_product_test.rb Outdated
detail: fetch_detail_for(group, submission_status.application_type)
id: submission_status&.id,
application_type: application_type,
status: submission_status&.status || HealthCenterSubmissionStatus::DEFAULT_STATUS,
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.

レコードなしの場合,詳細画面と一覧画面で表示が異なるのでは?

指摘部分(詳細画面):「unapproved」を返すから,「未確認」と表示される.
以下の部分(一覧画面):「nil」を返すから,「未提出」と表示される.

food_product: statuses['food_product']&.status,
purchase_list: statuses['purchase_list']&.status,
cooking_process_order: statuses['cooking_process_order']&.status,
employee: statuses['employee']&.status,
venue_map: statuses['venue_map']&.status,
equipment: statuses['equipment']&.status

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

修正しました
全て空白時には未提出表示にして、ステータス更新で未確認ステータスを反映させる挙動にしました。

return group.group?.fes_year_id || group.fes_year?.id;
},
normalizeStatus(value) {
if (value === true || value === "submitted") return "approved";
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.

health_center_submission_status.rb

enum status: {
    unapproved: 0,
    waiting_resubmission: 1,
    approved: 2,
    unsubmitted: 3
  }

上記と「null」の5種類しか返ってこないから,この分岐は意図しないバグの原因になるかも

@KokiWakatsuki
Copy link
Copy Markdown
Collaborator

coderabbitaiの指摘をある程度修正してから,レビューさせてもらえると嬉しいです!

coderabbitaiの指摘とレビュワーの指摘を,一度にどちらも修正すると,大幅変更になって,結果的に2度の大規模レビューをすることになってしまうので,先に修正してもらいたい!
AIが指摘するエラーはAIで簡単に修正できると思うので,そこまで労力はかからないだろうという判断です.

もし,先に修正することで,開発のスピードが低下して間に合わなくなるということであれば,とりあえず45thでは今のままで進めましょう!

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: 3

♻️ Duplicate comments (7)
admin_view/nuxt-project/pages/health_center_document_review/index.vue (4)

535-547: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Exclude “申請しない” equipment from the completion check.

This method always requires group.equipment === approved, so a group that explicitly opted out of rental items still shows as incomplete. Skip equipment when isUnregistered(group.group.id, "rental_item_order") is true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue` around
lines 535 - 547, The isHealthCenterDocumentComplete method currently requires
group.equipment to be approved; change it so that if
isUnregistered(group.group.id, "rental_item_order") returns true you do not
include group.equipment in the statuses array. Concretely, inside
isHealthCenterDocumentComplete (which uses normalizeStatus), build statuses
conditionally: push cooking_process_order, food_product, purchase_list,
employee, venue_map, and only push equipment when
!isUnregistered(group.group.id, "rental_item_order"); then keep the existing
return statuses.every(status => this.normalizeStatus(status) === "approved").

361-396: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t treat IDs as array indexes in updateFilters.

name_list[item_id - 1] only works for dense, 1-based IDs. As soon as IDs are sparse or historical, the selected label can become wrong or undefined. Look up the selected entry by id instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue` around
lines 361 - 396, In updateFilters, you’re treating item_id as an array index
(e.g. name_list[item_id - 1]) which breaks when IDs are sparse; instead locate
the selected object by matching its id (e.g. use Array.prototype.find on
yearList, groupCategories, internationalList, externalList) and then read the
label/value from that object; keep setting the corresponding ref*ID (refYearID,
refCategoryID, refInternationalID, refExternalID) and use the found object's
properties for refYears/refGroupCategories/refInternational/refExternal, falling
back to the "ALL" strings when item_id is 0 or no match is found.

399-426: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle /un_registered_groups failures in both refresh paths.

Both fetchFilteredData() and searchGroups() await this request without try/catch, so a network/API failure aborts the filter/search flow with an unhandled rejection. Please keep the existing list usable and fall back safely when this fetch fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue` around
lines 399 - 426, Wrap the this.$axios.$get("/un_registered_groups") call in both
fetchFilteredData and searchGroups with a try/catch; on success assign
this.unregisteredGroups = unregisteredRes.data, and on failure catch the error,
log it (e.g. console.error or existing logger) and do not overwrite the current
this.unregisteredGroups (or set it to [] only if it was previously undefined) so
the UI keeps using the existing list and no unhandled rejection is thrown.

288-289: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the global scroll listener on destroy.

This page registers a window-level listener in mounted() but never unregisters it, so revisiting the screen stacks duplicate handlers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue` around
lines 288 - 289, The window-level scroll listener added in mounted() is never
removed; add a corresponding lifecycle hook (beforeDestroy for Vue 2 or
unmounted for Vue 3) that calls window.removeEventListener("scroll",
this.saveScrollPosition) to unregister the handler; locate the mounted() block
where window.addEventListener("scroll", this.saveScrollPosition) is added and
add the matching removeEventListener in the component's teardown hook so
revisits don't stack duplicate handlers.
admin_view/nuxt-project/pages/health_center_document_review/_id.vue (2)

814-843: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle comment-save failures explicitly.

If this POST fails, the page gives no feedback and the reviewer can easily assume the message was saved. Please catch the request error, keep commentBody intact, and avoid mutating local comment state on the failure path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue` around
lines 814 - 843, The onSubmitComment function currently posts and then mutates
local comment state and clears commentBody without handling failures; wrap the
axios POST in a try/catch (inside onSubmitComment), only apply the mutations
that set submission.id, push to targetSubmission.comments, and clear
this.commentBody if the POST succeeds, and in the catch preserve
this.commentBody and do not modify submission/targetSubmission; also surface an
error to the user (e.g., call your app's notification/error handler) so failures
are visible. Ensure you reference onSubmitComment, commentRes, submission,
targetSubmission, and this.commentBody when implementing the changes.

791-813: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Revert the dropdown when the status save fails.

The native <select> updates immediately, so if the POST rejects the UI can keep showing the new status even though nothing was persisted. Wrap this in try/catch, restore the previous value, and surface the failure to the reviewer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue` around
lines 791 - 813, The status POST in onStatusChange updates the native <select>
immediately causing the UI to show a new status even if the save fails; wrap the
request in a try/catch inside onStatusChange, capture the previous status from
getSubmission(applicationType) before changing, perform the axios POST to
HEALTH_CENTER_STATUS_UPDATE_ENDPOINT in the try block, and on catch restore the
previous status on the submission object and surface the error (e.g., rethrow or
call the existing error-notification method) so the dropdown reflects the
persisted value when the save fails.
api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb (1)

173-176: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use a safe lookup here to preserve the JSON error contract.

If health_center_submission_status_id is stale, Line 175 raises ActiveRecord::RecordNotFound and Rails returns its default 404 path instead of this controller’s structured JSON response. find_by/nil handling would keep this endpoint consistent with the rest of the action flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`
around lines 173 - 176, The resolve_submission_status method currently calls
HealthCenterSubmissionStatus.find(...) which will raise
ActiveRecord::RecordNotFound and bypass this controller’s JSON error contract;
change the lookup to a safe finder (e.g.,
HealthCenterSubmissionStatus.find_by(id:
params[:health_center_submission_status_id])) and handle the nil case the same
way other branches do so the method returns nil or a structured error instead of
letting Rails render its default 404. Update the branch that checks
params[:health_center_submission_status_id] in resolve_submission_status to use
find_by and ensure subsequent code handles a nil return consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue`:
- Around line 182-185: The template's empty-state conditional is using the
generic hasEquipmentApplication flag, causing unrelated sections to show
"物品申請は行わない"; update the conditional in
pages/health_center_document_review/_id.vue so each section uses its
section-specific applicability flag (e.g., hasRentalItemApplication,
hasSaleItemApplication, or the corresponding prop/computed used by the
販売品申請/従業員申請 blocks) instead of hasEquipmentApplication, and if a
section-specific flag is unavailable fall back to showing "未登録" rather than
"物品申請は行わない"; apply the same fix to the similar block around lines 303-306.
- Around line 693-705: The summary count getters currently compare raw
submission.status values inconsistently; update unapprovedSubmissionCount,
reSubmissionCount, unconfirmedCount, and unsubmittedCount to call
normalizeStatus(submission.status) before comparing so null/undefined or variant
strings are normalized for all four buckets (use normalizeStatus in each filter
callback and then .length).

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`:
- Around line 177-183: The enum assignment in resolve_submission_status can
raise ArgumentError if params[:status] is invalid; before setting
submission_status.status (inside the
HealthCenterSubmissionStatus.find_or_initialize_by.tap block), guard the
assignment by checking params[:status].present? &&
HealthCenterSubmissionStatus.statuses.key?(params[:status]) so you only assign
valid enum values; this same validation approach should cover calls from
create_health_center_submission_status_comment and let save_submission_status
continue to handle invalid-status responses (422) instead of crashing.

---

Duplicate comments:
In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue`:
- Around line 814-843: The onSubmitComment function currently posts and then
mutates local comment state and clears commentBody without handling failures;
wrap the axios POST in a try/catch (inside onSubmitComment), only apply the
mutations that set submission.id, push to targetSubmission.comments, and clear
this.commentBody if the POST succeeds, and in the catch preserve
this.commentBody and do not modify submission/targetSubmission; also surface an
error to the user (e.g., call your app's notification/error handler) so failures
are visible. Ensure you reference onSubmitComment, commentRes, submission,
targetSubmission, and this.commentBody when implementing the changes.
- Around line 791-813: The status POST in onStatusChange updates the native
<select> immediately causing the UI to show a new status even if the save fails;
wrap the request in a try/catch inside onStatusChange, capture the previous
status from getSubmission(applicationType) before changing, perform the axios
POST to HEALTH_CENTER_STATUS_UPDATE_ENDPOINT in the try block, and on catch
restore the previous status on the submission object and surface the error
(e.g., rethrow or call the existing error-notification method) so the dropdown
reflects the persisted value when the save fails.

In `@admin_view/nuxt-project/pages/health_center_document_review/index.vue`:
- Around line 535-547: The isHealthCenterDocumentComplete method currently
requires group.equipment to be approved; change it so that if
isUnregistered(group.group.id, "rental_item_order") returns true you do not
include group.equipment in the statuses array. Concretely, inside
isHealthCenterDocumentComplete (which uses normalizeStatus), build statuses
conditionally: push cooking_process_order, food_product, purchase_list,
employee, venue_map, and only push equipment when
!isUnregistered(group.group.id, "rental_item_order"); then keep the existing
return statuses.every(status => this.normalizeStatus(status) === "approved").
- Around line 361-396: In updateFilters, you’re treating item_id as an array
index (e.g. name_list[item_id - 1]) which breaks when IDs are sparse; instead
locate the selected object by matching its id (e.g. use Array.prototype.find on
yearList, groupCategories, internationalList, externalList) and then read the
label/value from that object; keep setting the corresponding ref*ID (refYearID,
refCategoryID, refInternationalID, refExternalID) and use the found object's
properties for refYears/refGroupCategories/refInternational/refExternal, falling
back to the "ALL" strings when item_id is 0 or no match is found.
- Around line 399-426: Wrap the this.$axios.$get("/un_registered_groups") call
in both fetchFilteredData and searchGroups with a try/catch; on success assign
this.unregisteredGroups = unregisteredRes.data, and on failure catch the error,
log it (e.g. console.error or existing logger) and do not overwrite the current
this.unregisteredGroups (or set it to [] only if it was previously undefined) so
the UI keeps using the existing list and no unhandled rejection is thrown.
- Around line 288-289: The window-level scroll listener added in mounted() is
never removed; add a corresponding lifecycle hook (beforeDestroy for Vue 2 or
unmounted for Vue 3) that calls window.removeEventListener("scroll",
this.saveScrollPosition) to unregister the handler; locate the mounted() block
where window.addEventListener("scroll", this.saveScrollPosition) is added and
add the matching removeEventListener in the component's teardown hook so
revisits don't stack duplicate handlers.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`:
- Around line 173-176: The resolve_submission_status method currently calls
HealthCenterSubmissionStatus.find(...) which will raise
ActiveRecord::RecordNotFound and bypass this controller’s JSON error contract;
change the lookup to a safe finder (e.g.,
HealthCenterSubmissionStatus.find_by(id:
params[:health_center_submission_status_id])) and handle the nil case the same
way other branches do so the method returns nil or a structured error instead of
letting Rails render its default 404. Update the branch that checks
params[:health_center_submission_status_id] in resolve_submission_status to use
find_by and ensure subsequent code handles a nil return consistently.
🪄 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: 98a4030b-f201-424e-b83d-84ac473491cb

📥 Commits

Reviewing files that changed from the base of the PR and between 909caa4 and 06f50f1.

📒 Files selected for processing (5)
  • admin_view/nuxt-project/pages/health_center_document_review/_id.vue
  • admin_view/nuxt-project/pages/health_center_document_review/index.vue
  • api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb
  • api/app/models/health_center_submission_status.rb
  • api/config/routes.rb
🚧 Files skipped from review as they are similar to previous changes (2)
  • api/config/routes.rb
  • api/app/models/health_center_submission_status.rb

Comment on lines +182 to +185
<template v-else>
<p v-if="hasEquipmentApplication">未登録</p>
<p v-else>物品申請は行わない</p>
</template>
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 | ⚡ Quick win

These empty states are driven by the equipment flag, so they show the wrong message.

Both branches currently use hasEquipmentApplication, which means the 販売品申請 / 従業員申請 sections can render 物品申請は行わない based on rental-item state. This should use section-specific applicability, or just fall back to 未登録 here.

Also applies to: 303-306

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue` around
lines 182 - 185, The template's empty-state conditional is using the generic
hasEquipmentApplication flag, causing unrelated sections to show "物品申請は行わない";
update the conditional in pages/health_center_document_review/_id.vue so each
section uses its section-specific applicability flag (e.g.,
hasRentalItemApplication, hasSaleItemApplication, or the corresponding
prop/computed used by the 販売品申請/従業員申請 blocks) instead of
hasEquipmentApplication, and if a section-specific flag is unavailable fall back
to showing "未登録" rather than "物品申請は行わない"; apply the same fix to the similar
block around lines 303-306.

Comment on lines +693 to +705
unapprovedSubmissionCount() {
return this.submissions.filter((submission) => submission.status !== "approved")
.length;
},
reSubmissionCount() {
return this.submissions.filter((submission) => submission.status === "waiting_resubmission").length;
},
unconfirmedCount() {
return this.submissions.filter((submission) => submission.status === "unapproved").length;
},
unsubmittedCount() {
return this.submissions.filter((submission) => submission.status === "unsubmitted").length;
},
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 | ⚡ Quick win

Normalize statuses before counting the summary buckets.

unapprovedSubmissionCount treats missing statuses as “not approved”, but unsubmittedCount only counts the literal "unsubmitted". When a submission row is still null, the header can say 未承認 N件 while the 再提出/未確認/未提出 breakdown undercounts the same items. Use normalizeStatus(submission.status) in all four computed counts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@admin_view/nuxt-project/pages/health_center_document_review/_id.vue` around
lines 693 - 705, The summary count getters currently compare raw
submission.status values inconsistently; update unapprovedSubmissionCount,
reSubmissionCount, unconfirmedCount, and unsubmittedCount to call
normalizeStatus(submission.status) before comparing so null/undefined or variant
strings are normalized for all four buckets (use normalizeStatus in each filter
callback and then .length).

Comment on lines +177 to +183
status = params[:status].presence || default_status || HealthCenterSubmissionStatus::DEFAULT_STATUS
HealthCenterSubmissionStatus.find_or_initialize_by(
group_id: params[:group_id],
application_type: params[:application_type]
).tap do |submission_status|
submission_status.status = status if submission_status.new_record? || params[:status].present?
end
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 | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In Rails 6.1 ActiveRecord enum, what happens when assigning an invalid value to an enum attribute? Does model.status = "invalid"raiseArgumentError immediately?

💡 Result:

In Rails 6.1 ActiveRecord enum, assigning an invalid value to an enum attribute like model.status = "invalid" raises ArgumentError immediately.[1][2][3][4][5] This is the default behavior because the EnumType#assert_valid_value method checks if the value is valid and raises ArgumentError if @_raise_on_invalid_values is true (which it is by default).[3][6] The Rails API documentation explicitly states: "By default, an ArgumentError will be raised when assigning an invalid value" with an example showing conversation.status = :unknown raising ArgumentError.[1] This behavior has been consistent since Rails 4.1 and remains in 6.1, with workarounds like custom setters or the validate: true option (introduced later in Rails 7.1) discussed in issues but not changing the default.[4][7][8]

Citations:


🏁 Script executed:

# Find the controller file and examine the code
fd -type f -name "health_center_submission_statuses_api_controller.rb" | head -20

Repository: NUTFes/group-manager-2

Length of output: 237


🏁 Script executed:

# Also search for the HealthCenterSubmissionStatus model to understand the enum definition
fd -type f -name "*health_center_submission_status*" -type f | grep -E "\.(rb|json)$" | head -20

Repository: NUTFes/group-manager-2

Length of output: 237


🏁 Script executed:

# Look for enum definitions in the model
rg "enum.*status" api/ -A 3 -B 3 --type rb

Repository: NUTFes/group-manager-2

Length of output: 93


🏁 Script executed:

# Find the controller file
fd health_center_submission_statuses_api_controller

Repository: NUTFes/group-manager-2

Length of output: 145


🏁 Script executed:

# Find the model file
fd health_center_submission_status

Repository: NUTFes/group-manager-2

Length of output: 453


🏁 Script executed:

# Search for enum definitions
rg "enum.*status" api/ -A 3 -B 3

Repository: NUTFes/group-manager-2

Length of output: 520


🏁 Script executed:

# Read the full model file to understand the enum and defaults
cat -n api/app/models/health_center_submission_status.rb

Repository: NUTFes/group-manager-2

Length of output: 2110


🏁 Script executed:

# Read the controller file around lines 177-183
sed -n '170,190p' api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 899


🏁 Script executed:

# Check if there's any validation before this method is called
rg "resolve_submission_status|save_submission_status|upsert_health_center_submission_status" api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb -B 2 -A 2

Repository: NUTFes/group-manager-2

Length of output: 1971


🏁 Script executed:

# Find the comment-create path mentioned in the review
rg "create_health_center_submission_status_comment" api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb -A 10

Repository: NUTFes/group-manager-2

Length of output: 1019


🏁 Script executed:

# Check the entire flow of upsert_health_center_submission_status and save_submission_status
sed -n '110,170p' api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb

Repository: NUTFes/group-manager-2

Length of output: 2057


🏁 Script executed:

# Check if there are any tests that might reveal expected behavior
cat -n api/test/models/health_center_submission_status_test.rb

Repository: NUTFes/group-manager-2

Length of output: 1546


🏁 Script executed:

# Look at how the API documentation describes the status parameter
cat api/oas_docs/src/paths/api/v1/health_center_submission_statuses_api.yml

Repository: NUTFes/group-manager-2

Length of output: 4622


Guard params[:status] with enum validation in resolve_submission_status.

Rails 6.1 raises ArgumentError immediately when assigning an invalid value to an enum attribute. At line 182, if params[:status] is present but invalid, the assignment submission_status.status = status will throw before execution reaches save_submission_status. This means an invalid status bypasses the "Invalid status" error handler and returns a 500 error instead of the intended 422 response.

The same issue affects create_health_center_submission_status_comment, which also calls resolve_submission_status without validation. Validate the status before assigning it using HealthCenterSubmissionStatus.statuses.key?() to match the existing validation pattern in save_submission_status.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb`
around lines 177 - 183, The enum assignment in resolve_submission_status can
raise ArgumentError if params[:status] is invalid; before setting
submission_status.status (inside the
HealthCenterSubmissionStatus.find_or_initialize_by.tap block), guard the
assignment by checking params[:status].present? &&
HealthCenterSubmissionStatus.statuses.key?(params[:status]) so you only assign
valid enum values; this same validation approach should cover calls from
create_health_center_submission_status_comment and let save_submission_status
continue to handle invalid-status responses (422) instead of crashing.

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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue`:
- Line 80: The call this.$emit("saved", response.data.id) assumes
response.data.id exists and can throw if the response shape differs; update the
save handler in FoodProductEditModal.vue to defensively access the id (e.g. use
optional chaining like response?.data?.id or check response && response.data &&
typeof response.data.id !== 'undefined'), and handle the missing-id case by
either emitting a fallback value (null/undefined) or not emitting and logging an
error so the UI doesn't crash.
- Around line 79-82: The PUT call uses this.$axios.$put without error handling,
causing the modal to close even on failure; wrap the request in a try/catch (or
add .catch()) around this.$axios.$put, only emit("close") and emit("saved", ...)
on success, and on error prevent closing and surface feedback (e.g., emit an
"error" event or call a notification/toast method) with the error message;
update the handler where this.$axios.$put(...) is called to reference the same
event names ("saved", "close") and add a clear error emission so the parent can
display validation/network/server errors.
- Around line 68-77: In edit(), validate required fields before building the
URL/query: retrieve the target object via getFoodProduct(), then ensure name
(trimmed) and group_id (present/non-empty) are valid and short-circuit (return)
with a user-visible error (e.g., set a local validation error state or call
this.$toast/emit) if they are missing; only build the URL and call the API when
name and group_id pass validation, leaving optional fields (isCooking, first,
second) as-is. Use the existing edit() method and getFoodProduct() to locate
where to add these checks.
🪄 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: ef82daf7-bffc-4e68-be59-5f8f8f5fdcce

📥 Commits

Reviewing files that changed from the base of the PR and between 8874ac8 and bd38165.

📒 Files selected for processing (5)
  • admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue
  • admin_view/nuxt-project/pages/health_center_document_review/index.vue
  • api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb
  • api/app/models/food_product.rb
  • api/test/models/food_product_test.rb
🚧 Files skipped from review as they are similar to previous changes (3)
  • api/test/models/food_product_test.rb
  • api/app/controllers/api/v1/health_center_submission_statuses_api_controller.rb
  • admin_view/nuxt-project/pages/health_center_document_review/index.vue

Comment thread admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue Outdated
Comment thread admin_view/nuxt-project/components/edit-modals/FoodProductEditModal.vue Outdated
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.

[feat] 保健所提出確認フロント画面の実装

2 participants