Skip to content

Commit 45a0674

Browse files
dadachiclaude
andauthored
Phase 1: Rails API substrate v2 refactor (#45)
* Add substrate v2 overview and phase 1 Rails API checklist * Clarify ItemTag policy uses Shop permissions (no separate ItemTag perms) * Remove NFC scan routes and display namespace * Remove display namespace * Remove static controller, scan actions, and root route * Refactor ItemTag schema: rename queue_number, add description/position, remove queue-specific fields * Refactor ItemTag model for generic CRUD * Simplify Shop model: replace queue auto-generation with single sample item Removes queue-specific Shop methods (latest_completed_item_tag, create_default_item_tags!, reset!, full_reload_entire_page) and replaces the after_create callback with create_sample_item_tag, which generates one generic 'Sample' ItemTag for first-run UX. Cascades caller cleanup: - shops_controller#reset action removed - delete :reset route removed - ShopPolicy#reset? removed - lib/tasks/shop.rake (only task was create_default_item_tags) deleted * Update ItemTag controller for new schema - Sort by (position, name) instead of queue_number - Strip already_completed cache-purge hack from #complete - Rename #reset action to #idle (matches AASM event); route updated - Strong params: permit name, description, position, state * Inline complete/idle state transitions, drop tag-named wrapper methods Removes Shop#complete_tag! and ItemTag#reset! (both carried queue-era '_tag' residue and 'reset' semantics). Controller actions now call AASM's generated complete!/idle! events directly with completed_by / completed_at set inline. This keeps method names domain-agnostic so nativeapptemplate-agent's identifier rename (e.g. ItemTag to Todo) doesn't need to rewrite method bodies. * Update serializers for new ItemTag schema ItemTagSerializer: - Drop queue_number, scan_state, customer_read_at, already_completed - Add name, description, position ShopSerializer: - Drop scanned_item_tags_count (referenced removed scan_state) - Drop display_shop_server_path (display route removed) * Update ItemTag policy: 2-tier admin/member, add idle, drop reset - Remove references to senior_manager?/junior_manager?/senior_member?/junior_member? predicates - Both admin and member can perform all ItemTag operations (collaborative model) - Rename reset? to idle? to match controller action Note: member? predicate doesn't yet exist; defined when AccountsShopkeeper::ROLES is updated in Step 13. * Update madmin resource for new ItemTag schema * Redesign roles (admin/member) and permissions (generic CRUD) Permissions: 6 generic CRUD primitives — create_shops, update_shops, delete_shops, update_organizations, invitation, read_data. Removes 5 queue-specific perms (manage_tags, write_info_to_tags, reset_all_tags, complete_or_reset_tags, show_tag_info). Roles: 2 tiers — admin, member. Removes 5 queue-operator tiers (senior_manager, junior_manager, senior_member, junior_member, guest). Mappings (collaborative SaaS / Notion-style): - admin: all 6 permissions - member: create_shops, update_shops, delete_shops, read_data (no update_organizations or invitation) Applied identically to all 4 envs (development, test, staging, production). A fresh db rebuild + db:seed_fu is required to drop orphan rows from prior fixture loads. * Collapse roles to admin/member tier across model and madmin AccountsShopkeeper::ROLES = [:admin, :member]. AccountsInvitation inherits via constant aliasing, so both models drop the 4 queue-era intermediate roles (senior_manager, junior_manager, senior_member, junior_member) and 'guest'. Madmin resources (AccountsShopkeeperResource, AccountsInvitationResource) updated to expose only :admin and :member attributes. Rolified concern is generic (iterates ROLES) so no change needed there. Existing rows' 'roles' jsonb column may carry orphan keys for removed roles — these are silently ignored since the model only reads ROLES. * Update locales for new ItemTag schema and item-tag UI label - Rename 'number tag(s)' → 'item tag(s)' in user-visible strings (aligns with substrate v2 UI label rename in overview §2.4) - Replace activerecord.attributes.item_tag.queue_number with name / description / position - Drop queue_number format/uniqueness error keys (validations gone) No removed permission/role tag refs existed in en.yml; no ja.yml present. * Update model tests for refactored schema and 2-tier roles - ItemTagTest: rewrite around name/description/position; drop scan_tag!/complete_tag!/reset!/scan!/unscan! tests and queue_number format/uniqueness coverage; assert names may duplicate across and within shops - ShopTest: replace default-item-tags / queue_number / latest_completed_item_tag / Shop#reset! tests with create_sample_item_tag success path and a stubbed-failure path covering the rescue branch - AccountsShopkeeperTest: collapse per-tier permission/role-helper tests to admin and member only - AccountsInvitationTest, AccountTest, RoleTest, ShopkeeperTest: rename junior_member/guest references to member * Update serializer tests for refactored ItemTag/Shop attributes - ItemTagSerializerTest: drop scan_state/customer_read_at/already_completed assertions; switch the complete-flow test to the inlined controller pattern (set completed_by/at then call AASM complete!) - ShopSerializerTest: drop scanned_item_tags_count and display_shop_server_path tests (attributes removed) - AccountSerializerTest, AccountsShopkeeperSerializerTest, AccountsInvitationSerializerTest: rename old-tier role refs to member * Update policy tests for admin/member roles and idle action - ItemTagPolicyTest: collapse per-tier tests to admin+member; rename reset? cases to idle? - ShopPolicyTest: drop reset? tests (action removed in Step 7) - PermissionPolicyTest: replace guest with admin/member coverage - AccountPolicyTest, AccountsShopkeeperPolicyTest, AccountsInvitationPolicyTest: rename old-tier role refs to member * Update controller tests for refactored API and 2-tier roles - ItemTagsControllerTest: switch payloads from queue_number to name; drop invalid-format and already_completed cases; rename reset to idle; cover the duplicate-name-allowed case - ShopsControllerTest: drop the reset action test (action removed) - BaseControllerTest: rewire the validation-error case to use blank name instead of duplicate queue_number - Accounts/AccountsInvitations/AccountsShopkeepers/Permissions/ RegistrationsControllerTest: rename old-tier role refs to member * Fix bin/ci: rubocop trailing blank line and brakeman fingerprint refresh - Drop the extra blank line at end of shops_controller_test.rb class body (Layout/EmptyLinesAroundClassBody, leftover from Step 7 reset action removal) - Refresh brakeman.ignore: AccountsShopkeeper::ROLES collapse changed the expanded permit() literal, so the warning fingerprint shifted. Same intentional rationale (admin-gated role assignment); only the resolved role list and fingerprint differ. * Documentation cleanup for substrate v2 README.md: - Drop the 'real-time page updates for Number Tags Webpage' qualifier on Turbo - Rename 'Number Tags (ItemTags)' to 'Item Tags' in the features list CLAUDE.md: - Correct the ItemTag description: name has no uniqueness constraint; describe the columns (name/description/position) and binary state instead docs/openapi.yaml (-90/+31): - ItemTag schema: drop queue_number/scan_state/customer_read_at/already_completed, add name (required)/description/position - Shop schema: drop scanned_item_tags_count and display_shop_server_path - /permissions meta: drop maximum_queue_number_length - Roles: drop senior_manager/junior_manager/senior_member/junior_member/guest, add member (5 sites: AccountsShopkeeper attrs/update, AccountsInvitation attrs/create/update) - Endpoints: remove DELETE /shops/{shopId}/reset; rename PATCH /item_tags/{itemTagId}/reset → /idle (resetItemTag → idleItemTag) - ItemTag create/update request bodies switched to name+description+position+state Also delete: - docs/pagination-item-tags.md (pre-implementation notes; pagy is now wired in the controller, doc is stale) - app/views/layouts/display.html.erb (orphaned after Step 3 display namespace removal — no controllers reference this layout) * Drop dead queue-length config, controller meta, and test assertion Step 17 residual sweep caught references the per-step grep missed: - config/settings.yml: drop maximum_queue_number_length and the item_tag.default_count / default_queue_number_length keys (only consumed by the removed format validator and create_default_item_tags!) - PermissionsController#index meta: drop maximum_queue_number_length (clients no longer need to enforce the queue-format length cap) - permissions_controller_test: drop the matching assertion * Set sample item_tag position to 1 Without an explicit position, the sample row gets NULL, which Postgres sorts last by default — surprising for a single-row first-run state and fragile once a client adds more items without managing position themselves. Setting position 1 gives the sample a definite slot and keeps the (:position, :name) sort meaningful for clients that don't implement reordering. * Remove orphan click_connectedly/click_repeatedly stimulus controllers Both auto-clicker controllers were used only by the display namespace queue-board views (now deleted in Step 3). The eager loader in controllers/index.js needs no change — it just won't find them. Stimulus itself is now essentially dead weight; defer the wider JS-stack removal to a follow-up sweep alongside Turbo. * Auto-assign ItemTag.position to max+1 on create when not provided Without this, clients that don't manage position end up with NULL positions on every item, the (shop_id, position) index goes unused, and ordering between user-added items falls to the :name tiebreaker. before_create callback no-ops when position was explicitly supplied, so Shop's create_sample_item_tag (position: 1) and clients that send their own positions are unaffected. Lighter than acts_as_list (no gem, no decrement-on-delete machinery — that stays out per phase 1 pitfall #7); this is just a sensible append-to-end default. * Broaden ShopPolicy to admin || member per collaborative model The substrate v2 design (overview §2.1 table) grants create_shops, update_shops, and delete_shops to both admin and member tiers. Pre-refactor policy was stricter than the design called for: - create? required owner? (so even non-owner admins couldn't create) - update? required admin? (so members couldn't update) - destroy? delegated to create? (so only owner could destroy) Aligned with the design and with ItemTagPolicy's pattern. Tests rewritten — two old assertions inverted (non-owner admin can now create; member can now update), and two read-only true-for-all tests specialised into admin and member variants for symmetry. * Add rake task to backfill item_tags.position for existing rows Assigns sequential position values per shop, ordered by created_at, starting from current max+1, skipping callbacks/validations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Document SOLID_QUEUE_IN_PUMA=true in .env.sample for dev Without it, deliver_later jobs (e.g. invitation emails) sit in the queue unprocessed since no worker runs in development. The Puma plugin (config/puma.rb) already activates when this env var is set. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove unused bin/update script bin/setup covers the dev-environment refresh flow; bin/update was a legacy Rails generator artifact and was no longer referenced. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add role_redesign:migrate rake task for legacy role data Fixture-only role redesign (c42249e) left production with stale data: - accounts_shopkeepers / accounts_invitations jsonb roles still keyed on senior_manager, junior_manager, senior_member, junior_member, guest - orphan rows in roles, permissions, roles_permissions for the dropped tiers and queue-specific perms (manage_tags, write_info_to_tags, reset_all_tags, complete_or_reset_tags, show_tag_info) The task folds non-admin keys to member, preserves admin, and deletes orphan role/permission/roles_permission rows in a single transaction. Idempotent — safe to re-run. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove apple-app-site-association (queue-scan deep link retired) Substrate v2 drops POST /scan and GET /scan_customer; the only Universal Link rule in the manifest was /scan/*?item_tag_id=…, which no longer resolves. No remaining iOS Universal Link routes in v2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove Turbo from README features list API-only Rails app — no Turbo runtime in use. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Simplify item_tag not_found message Drop the "switch organization" hint — Free client v2 has no Organizations tab, so the guidance is misleading. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add tests for previously uncovered code paths (#47) Covers ErrorsController, AdminUser, the static ShopkeeperAuth::{ConfirmationResults,ResetPasswords} pages, ShopkeeperMailer, and Shopkeeper::NotificationMailer (invited, confirmation_instructions, reset_password_instructions). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Refactor: small cleanups across models and controllers (#48) - ErrorsController: extract shared render_error helper for the duplicated JSON/HTML branching - RegistrationsController: render_*_error hooks now delegate to a single private render_resource_error to remove the triple copy - Account/Shop/ItemTag/AccountsShopkeeper: rename `the_limit_count` local to plain `limit` - AccountsInvitation#set_token: collapse the unique-token loop to use `break` as the assignment value - Shopkeeper: drop two commented-out send_devise_notification lines superseded by the custom mailer Behavior preserved; full test suite passes (393 runs, 792 assertions). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Ignore .claude/scheduled_tasks.lock Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Rate-limit shopkeeper sign-up to 10/IP/3min (#50) * Rate-limit shopkeeper sign-up to 5/IP/hour Mass account creation is currently only constrained by the broad 300/IP/5min rack-attack req/ip cap. Add an endpoint-specific limit on POST /shopkeeper_auth using Rails 8's built-in ActionController::RateLimiting: 5 requests per IP per hour. When exceeded, render 429 with a localized JSON error. Switch the test cache from :null_store to :memory_store so the limiter's counters can persist within a request sequence (:null_store no-ops increments). Clear Rails.cache in the standard test setup so throttle state doesn't leak between tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Tune sign-up rate limit to 10/3min (match jumpstart-pro reference) Aligns with the reference codebase (jumpstart-pro-rails uses `to: 10, within: 3.minutes` on user sign-up). The earlier 5/1.hour was overly strict for legit users — a confused user with bad password rules or autofill misfires could exhaust the quota and get locked out for an hour. 10/3min absorbs realistic retry flows while still constraining bots; per-IP limits are not the primary defense against rotating-IP attackers anyway. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Fix SessionsController nuking current_platform on header-less sign-in (#49) * Fix two latent bugs in permissions and sign-in flows PermissionsController#index compared `confirmed_*_version < current_*_version`, where `current_version` returns nil if no row is flagged current. `string < nil` raises ArgumentError → 500. Add a `version_outdated?` helper that treats a missing current version as "nothing to update". ShopkeeperAuth::SessionsController#create unconditionally assigned `request.headers["source"]` to current_platform and called `save!(validate: false)`. Sign-ins without the source header overwrote the user's stored platform with nil, bypassing the presence/inclusion validation. Now skip the assignment when the header is blank. Adds regression tests for both paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Revert PermissionsController nil-version guard Per review: a missing current PrivacyVersion/TermsVersion is a server-side data integrity problem (no row published as current). Crashing loud is preferable to silently telling the client "you're up to date" — the latter would mask the data issue and mislead clients. Keeps the SessionsController fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Require 'source' header on shopkeeper sign-in Per direction: the source header (ios/android) is mandatory. Reject the request with 401 when missing instead of skipping the current_platform update — silently signing the user in without the header would let API tools accumulate sessions that lack platform attribution and bypass the presence/inclusion validation on Shopkeeper. Adds the locale key and updates the regression test for the blank-header path. Updates the existing "no params" test to send the header so the bad_credentials assertion remains the path under test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Make 'source' header optional on shopkeeper sign-in Reverts the earlier "require source header" form. Anti-mass-signup is now handled at the right layer by the sign-up rate_limit introduced in PR #50, so the sign-in header has no security job left. current_platform is informational metadata; rejecting sign-ins on missing metadata is too aggressive — it breaks non-mobile callers (curl, CI, integration tools, future web client) without a real benefit. Skip the current_platform update when the header is blank: the existing stored value is preserved (instead of being nuked to nil by the original buggy code path). Drop the missing_source locale key. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Move login throttles into SessionsController via rate_limit (#51) Consolidates the rack-attack `logins/ip` and `logins/email` throttles onto the controller using Rails 8's ActionController::RateLimiting. Rules now sit next to the action they guard, mirroring the sign-up `rate_limit` shipped earlier. The general `req/ip` cap stays in rack-attack since `rate_limit` is per-controller and middleware is the right layer for an app-wide flood cap. The email throttle uses `if: -> { params[:email].present? }` to preserve the original "skip when no email" behavior — without this, all email-less requests would share a single counter. Adds an integration test covering both the IP-keyed throttle (same IP, different emails) and the email-keyed throttle (same email, rotating IPs). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Remove dead code: unused gems and locale keys (#52) Verified-unreferenced removals (every grep below ran across app/, lib/, config/ and matched only the definition site): - gem "jbuilder" — no .jbuilder views, no JBuilder usage; all responses go through jsonapi-serializer - gem "inline_svg" — no inline_svg helper calls anywhere - locale `forgot_your_password` — no callers - locale `send_me_reset_password_instructions` — no callers - locale `api.shopkeeper.accounts.owner_required` — no callers - locale `api.shopkeeper.accounts.admin_required` — no callers Kept `validate_sign_up_params` / `validate_account_update_params` overrides in RegistrationsController despite looking unused — the parent class wires them up via `before_action` (verified in the gem source). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Normalize NATEMPLATE_API_DOMAIN docs to NATIVEAPPTEMPLATE_API_DOMAIN (#54) Per docs-private/SUBSTRATE-NAMING-NORMALIZATION.md, all three substrates should use the canonical NATIVEAPPTEMPLATE_ stem so the agent's existing NativeAppTemplate -> <slug-pascal> rename pair handles every product-name occurrence. The Rails repo has no boot/config references to these env vars - only doc references that point users at the iOS/Android values - so update them for parity. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Update gems within Gemfile constraints Routine bundle update: bigdecimal, bootsnap, erb, ffi, irb, json, minitest, net-imap, nokogiri, pagy, parallel, parser, propshaft, puma, regexp_parser, rubocop, rubocop-ast, tailwindcss-ruby. Tests, rubocop, brakeman, bundler-audit all clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b3101af commit 45a0674

111 files changed

Lines changed: 1039 additions & 1952 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.sample

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@
88
#
99
# Never use 127.0.0.1, localhost, or 0.0.0.0. When your Wi-Fi IP
1010
# changes, update HOST here and the matching values in the mobile
11-
# apps (Xcode scheme NATEMPLATE_API_DOMAIN and Android
12-
# ~/.gradle/gradle.properties NATEMPLATE_API_DOMAIN).
11+
# apps (Xcode scheme NATIVEAPPTEMPLATE_API_DOMAIN and Android
12+
# ~/.gradle/gradle.properties NATIVEAPPTEMPLATE_API_DOMAIN).
1313

1414
HOST=192.168.1.21
1515
PORT=3000
16+
17+
# Run Solid Queue inside Puma so background jobs (e.g. invitation emails
18+
# via deliver_later) are processed in development. Mirrors production
19+
# (see render.yaml).
20+
SOLID_QUEUE_IN_PUMA=true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
.claude/logs/
6464
.claude/tmp/
6565
.claude/*.log
66+
.claude/scheduled_tasks.lock
6667

6768
# Private docs — not for publication
6869
/docs-private/

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ bin/rails dbconsole # Database console
7070
- `Account` - Top-level tenant/organization
7171
- `Shopkeeper` - Main user type (belongs to Account)
7272
- `Shop` - Core business entity (belongs to Account)
73-
- `ItemTag` - Belongs to Shop with unique name constraint
73+
- `ItemTag` - Belongs to Shop; has name/description/position and a binary state (idled/completed)
7474
- `Role` & `Permission` - Authorization system
7575
- State machines implemented with AASM gem
7676

@@ -96,7 +96,7 @@ bin/rails dbconsole # Database console
9696
- Run tests: `bin/rails test` (205 tests, 402 assertions)
9797

9898
### Development Server Configuration
99-
- `HOST` (Wi-Fi IP) and `PORT` are required in `.env`; `Procfile.dev` uses `${HOST:?...}` so Rails fails loudly if unset, and `development.rb` uses `ENV.fetch("HOST")` for `action_mailer.default_url_options`. Must match `NATEMPLATE_API_DOMAIN` in the iOS scheme and Android `gradle.properties`. Never `127.0.0.1`, `localhost`, or `0.0.0.0`.
99+
- `HOST` (Wi-Fi IP) and `PORT` are required in `.env`; `Procfile.dev` uses `${HOST:?...}` so Rails fails loudly if unset, and `development.rb` uses `ENV.fetch("HOST")` for `action_mailer.default_url_options`. Must match `NATIVEAPPTEMPLATE_API_DOMAIN` in the iOS scheme and Android `gradle.properties`. Never `127.0.0.1`, `localhost`, or `0.0.0.0`.
100100
- Mailbin for email testing at `/mailbin`
101101
- Admin interface at `/madmin`
102102
- Tailwind CSS compiled by tailwindcss-rails gem

Gemfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ gem "turbo-rails", "~> 2.0.3"
2020
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
2121
gem "stimulus-rails", "~> 1.0", ">= 1.0.2"
2222

23-
# Build JSON APIs with ease [https://github.com/rails/jbuilder]
24-
gem "jbuilder", "~> 2.14"
25-
2623
# Solid adapters for queue, cache, and cable (database-backed, no Redis needed)
2724
gem "solid_queue"
2825
gem "solid_cable"
@@ -49,7 +46,6 @@ gem "aasm"
4946
gem "after_commit_everywhere", "~> 1.6"
5047
gem "config"
5148
gem "acts_as_tenant"
52-
gem "inline_svg", "~> 1.10"
5349
gem "pagy", "~> 43"
5450
gem "seed-fu", "~> 2.3"
5551
gem "whenever", require: false

Gemfile.lock

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,9 @@ GEM
9494
erubi (~> 1.4)
9595
parser (>= 2.4)
9696
smart_properties
97-
bigdecimal (4.1.1)
97+
bigdecimal (4.1.2)
9898
bindex (0.8.1)
99-
bootsnap (1.23.0)
99+
bootsnap (1.24.1)
100100
msgpack (~> 1.2)
101101
brakeman (8.0.4)
102102
racc
@@ -142,7 +142,7 @@ GEM
142142
devise (> 3.5.2, < 5)
143143
rails (>= 4.2.0, < 8.2)
144144
drb (2.2.3)
145-
erb (6.0.3)
145+
erb (6.0.4)
146146
erb_lint (0.9.0)
147147
activesupport
148148
better_html (>= 2.0.1)
@@ -153,12 +153,12 @@ GEM
153153
erubi (1.13.1)
154154
et-orbi (1.4.0)
155155
tzinfo
156-
ffi (1.17.3-aarch64-linux-gnu)
157-
ffi (1.17.3-arm-linux-gnu)
158-
ffi (1.17.3-arm64-darwin)
159-
ffi (1.17.3-x86-linux-gnu)
160-
ffi (1.17.3-x86_64-darwin)
161-
ffi (1.17.3-x86_64-linux-gnu)
156+
ffi (1.17.4-aarch64-linux-gnu)
157+
ffi (1.17.4-arm-linux-gnu)
158+
ffi (1.17.4-arm64-darwin)
159+
ffi (1.17.4-x86-linux-gnu)
160+
ffi (1.17.4-x86_64-darwin)
161+
ffi (1.17.4-x86_64-linux-gnu)
162162
fugit (1.12.1)
163163
et-orbi (~> 1.4)
164164
raabro (~> 1.4)
@@ -179,22 +179,13 @@ GEM
179179
activesupport (>= 6.0.0)
180180
railties (>= 6.0.0)
181181
iniparse (1.5.0)
182-
inline_svg (1.10.0)
183-
activesupport (>= 3.0)
184-
nokogiri (>= 1.6)
185182
io-console (0.8.2)
186-
irb (1.17.0)
183+
irb (1.18.0)
187184
pp (>= 0.6.0)
188185
prism (>= 1.3.0)
189186
rdoc (>= 4.0.0)
190187
reline (>= 0.4.2)
191-
jbuilder (2.14.1)
192-
actionview (>= 7.0.0)
193-
activesupport (>= 7.0.0)
194-
json (2.19.3)
195-
json-schema (6.2.0)
196-
addressable (~> 2.8)
197-
bigdecimal (>= 3.1, < 5)
188+
json (2.19.4)
198189
jsonapi-serializer (2.2.0)
199190
activesupport (>= 4.2)
200191
language_server-protocol (3.17.0.5)
@@ -222,13 +213,11 @@ GEM
222213
turbo-rails
223214
marcel (1.1.0)
224215
matrix (0.4.3)
225-
mcp (0.10.0)
226-
json-schema (>= 4.1)
227216
mini_magick (5.3.1)
228217
logger
229218
mini_mime (1.1.5)
230219
mini_portile2 (2.8.9)
231-
minitest (6.0.4)
220+
minitest (6.0.6)
232221
drb (~> 2.0)
233222
prism (~> 1.5)
234223
minitest-mock (5.27.0)
@@ -245,7 +234,7 @@ GEM
245234
msgpack (1.8.0)
246235
multi_xml (0.8.1)
247236
bigdecimal (>= 3.1, < 5)
248-
net-imap (0.6.3)
237+
net-imap (0.6.4)
249238
date
250239
net-protocol
251240
net-pop (0.1.2)
@@ -255,31 +244,31 @@ GEM
255244
net-smtp (0.5.1)
256245
net-protocol
257246
nio4r (2.7.5)
258-
nokogiri (1.19.2)
247+
nokogiri (1.19.3)
259248
mini_portile2 (~> 2.8.2)
260249
racc (~> 1.4)
261-
nokogiri (1.19.2-aarch64-linux-gnu)
250+
nokogiri (1.19.3-aarch64-linux-gnu)
262251
racc (~> 1.4)
263-
nokogiri (1.19.2-arm-linux-gnu)
252+
nokogiri (1.19.3-arm-linux-gnu)
264253
racc (~> 1.4)
265-
nokogiri (1.19.2-arm64-darwin)
254+
nokogiri (1.19.3-arm64-darwin)
266255
racc (~> 1.4)
267-
nokogiri (1.19.2-x86_64-darwin)
256+
nokogiri (1.19.3-x86_64-darwin)
268257
racc (~> 1.4)
269-
nokogiri (1.19.2-x86_64-linux-gnu)
258+
nokogiri (1.19.3-x86_64-linux-gnu)
270259
racc (~> 1.4)
271260
orm_adapter (0.5.0)
272261
ostruct (0.6.3)
273262
overcommit (0.69.0)
274263
childprocess (>= 0.6.3, < 6)
275264
iniparse (~> 1.4)
276265
rexml (>= 3.3.9)
277-
pagy (43.5.1)
266+
pagy (43.5.3)
278267
json
279268
uri
280269
yaml
281-
parallel (1.27.0)
282-
parser (3.3.10.2)
270+
parallel (2.1.0)
271+
parser (3.3.11.1)
283272
ast (~> 2.4.1)
284273
racc
285274
pg (1.6.3)
@@ -291,15 +280,15 @@ GEM
291280
prettyprint
292281
prettyprint (0.2.0)
293282
prism (1.9.0)
294-
propshaft (1.3.1)
283+
propshaft (1.3.2)
295284
actionpack (>= 7.0.0)
296285
activesupport (>= 7.0.0)
297286
rack
298287
psych (5.3.1)
299288
date
300289
stringio
301290
public_suffix (7.0.5)
302-
puma (8.0.0)
291+
puma (8.0.1)
303292
nio4r (~> 2.0)
304293
pundit (2.5.2)
305294
activesupport (>= 3.0.0)
@@ -351,7 +340,7 @@ GEM
351340
erb
352341
psych (>= 4.0.0)
353342
tsort
354-
regexp_parser (2.11.3)
343+
regexp_parser (2.12.0)
355344
reline (0.6.3)
356345
io-console (~> 0.5)
357346
resend (1.3.0)
@@ -361,19 +350,18 @@ GEM
361350
actionpack (>= 7.0)
362351
railties (>= 7.0)
363352
rexml (3.4.4)
364-
rubocop (1.85.1)
353+
rubocop (1.86.1)
365354
json (~> 2.3)
366355
language_server-protocol (~> 3.17.0.2)
367356
lint_roller (~> 1.1.0)
368-
mcp (~> 0.6)
369-
parallel (~> 1.10)
357+
parallel (>= 1.10)
370358
parser (>= 3.3.0.2)
371359
rainbow (>= 2.2.2, < 4.0)
372360
regexp_parser (>= 2.9.3, < 3.0)
373361
rubocop-ast (>= 1.49.0, < 2.0)
374362
ruby-progressbar (~> 1.7)
375363
unicode-display_width (>= 2.4.0, < 4.0)
376-
rubocop-ast (1.49.0)
364+
rubocop-ast (1.49.1)
377365
parser (>= 3.3.7.2)
378366
prism (~> 1.7)
379367
rubocop-performance (1.26.1)
@@ -428,11 +416,11 @@ GEM
428416
tailwindcss-rails (4.4.0)
429417
railties (>= 7.0.0)
430418
tailwindcss-ruby (~> 4.0)
431-
tailwindcss-ruby (4.2.1)
432-
tailwindcss-ruby (4.2.1-aarch64-linux-gnu)
433-
tailwindcss-ruby (4.2.1-arm64-darwin)
434-
tailwindcss-ruby (4.2.1-x86_64-darwin)
435-
tailwindcss-ruby (4.2.1-x86_64-linux-gnu)
419+
tailwindcss-ruby (4.2.4)
420+
tailwindcss-ruby (4.2.4-aarch64-linux-gnu)
421+
tailwindcss-ruby (4.2.4-arm64-darwin)
422+
tailwindcss-ruby (4.2.4-x86_64-darwin)
423+
tailwindcss-ruby (4.2.4-x86_64-linux-gnu)
436424
thor (1.5.0)
437425
timeout (0.6.1)
438426
tsort (0.2.0)
@@ -494,8 +482,6 @@ DEPENDENCIES
494482
erb_lint
495483
image_processing (~> 1.12)
496484
importmap-rails
497-
inline_svg (~> 1.10)
498-
jbuilder (~> 2.14)
499485
jsonapi-serializer
500486
madmin (~> 2.0)
501487
mailbin

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ For more information, visit [nativeapptemplate.com](https://nativeapptemplate.co
2222
- **[pundit](https://github.com/varvet/pundit)**
2323
- **[acts_as_tenant](https://github.com/ErwinM/acts_as_tenant)**
2424
- **[pagy](https://github.com/ddnexus/pagy)**
25-
- **[Turbo](https://turbo.hotwired.dev/)** (real-time page updates for Number Tags Webpage)
2625
- **Test** (Minitest)
2726

2827
### Included Features
@@ -31,7 +30,7 @@ For more information, visit [nativeapptemplate.com](https://nativeapptemplate.co
3130
- Email Confirmation
3231
- Forgot Password
3332
- CRUD Operations for Shops (Create/Read/Update/Delete)
34-
- CRUD Operations for Shops' Nested Resource, Number Tags (ItemTags) (Create/Read/Update/Delete)
33+
- CRUD Operations for Shops' Nested Resource, Item Tags (Create/Read/Update/Delete)
3534
- URL Path-Based Multitenancy (prepends `/:account_id/` to URLs)
3635
- User Invitation to Organizations
3736
- Role-Based Permissions and Access Control
@@ -85,7 +84,7 @@ bin/setup
8584

8685
## Running NativeAppTemplate API on your Wi-Fi
8786

88-
Copy `.env.sample` to `.env` and set `HOST` to your current Wi-Fi IP. On macOS: `ipconfig getifaddr en0`. `bin/dev` binds Rails to that address so the dev server is reachable from both the host browser and from any phone on the same network at `http://<wifi-ip>:3000`. When your Wi-Fi IP changes, update `HOST` here and the matching `NATEMPLATE_API_DOMAIN` in the mobile apps (Xcode scheme for iOS, `~/.gradle/gradle.properties` for Android) — Rails fails loudly if `HOST` is unset, which keeps the three sides honest. Never use `127.0.0.1`, `localhost`, or `0.0.0.0`.
87+
Copy `.env.sample` to `.env` and set `HOST` to your current Wi-Fi IP. On macOS: `ipconfig getifaddr en0`. `bin/dev` binds Rails to that address so the dev server is reachable from both the host browser and from any phone on the same network at `http://<wifi-ip>:3000`. When your Wi-Fi IP changes, update `HOST` here and the matching `NATIVEAPPTEMPLATE_API_DOMAIN` in the mobile apps (Xcode scheme for iOS, `~/.gradle/gradle.properties` for Android) — Rails fails loudly if `HOST` is unset, which keeps the three sides honest. Never use `127.0.0.1`, `localhost`, or `0.0.0.0`.
8988

9089
To run your application, you'll use the `bin/dev` command:
9190

app/controllers/api/v1/shopkeeper/item_tags_controller.rb

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
class Api::V1::Shopkeeper::ItemTagsController < Api::V1::Shopkeeper::BaseController
22
before_action :set_shop, only: %i[index create]
3-
before_action :set_item_tag, only: %i[show update destroy complete reset]
3+
before_action :set_item_tag, only: %i[show update destroy complete idle]
44

55
def index
66
authorize ItemTag
77

88
@pagy, @item_tags = pagy(
9-
@shop.item_tags.order(queue_number: :asc).includes(:shop),
9+
@shop.item_tags.order(:position, :name).includes(:shop),
1010
limit: params[:page].present? ? Pagy::OPTIONS[:limit] : 1000
1111
)
1212

@@ -53,27 +53,23 @@ def destroy
5353
def complete
5454
authorize @item_tag
5555

56-
options = {}
57-
options[:include] = [:shop]
58-
59-
if @item_tag.completed?
60-
# Purge ItemTagSerializer cache
61-
@item_tag.already_completed = true
62-
@item_tag.save!(validate: false)
63-
render json: ItemTagSerializer.new(@item_tag, options).serializable_hash and return
56+
if @item_tag.may_complete?
57+
@item_tag.completed_by = current_shopkeeper
58+
@item_tag.completed_at = Time.current
59+
@item_tag.complete!
6460
end
6561

66-
@item_tag.complete_tag!(current_shopkeeper)
67-
62+
options = {}
63+
options[:include] = [:shop]
6864
render json: ItemTagSerializer.new(@item_tag, options).serializable_hash
6965
end
7066

71-
def reset
67+
def idle
7268
authorize @item_tag
7369

74-
ApplicationRecord.transaction do
75-
@item_tag.reset!
76-
end
70+
@item_tag.completed_by_id = nil
71+
@item_tag.completed_at = nil
72+
@item_tag.idle!
7773

7874
render json: ItemTagSerializer.new(@item_tag).serializable_hash
7975
end
@@ -91,6 +87,6 @@ def set_item_tag
9187
end
9288

9389
def item_tag_params
94-
params.require(:item_tag).permit(:queue_number)
90+
params.require(:item_tag).permit(:name, :description, :position, :state)
9591
end
9692
end

app/controllers/api/v1/shopkeeper/permissions_controller.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ def index
1717
android_app_version: current_android_app_version,
1818
should_update_privacy: should_update_privacy,
1919
should_update_terms: should_update_terms,
20-
maximum_queue_number_length: ConfigSettings.maximum_queue_number_length,
2120
shop_limit_count: ConfigSettings.shop.limit_count,
2221
account_limit_count: ConfigSettings.account.limit_count,
2322
accounts_shopkeeper_limit_count: ConfigSettings.accounts_shopkeeper.limit_count

app/controllers/api/v1/shopkeeper/shops_controller.rb

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Api::V1::Shopkeeper::ShopsController < Api::V1::Shopkeeper::BaseController
2-
before_action :set_shop, only: %i[show update destroy reset]
2+
before_action :set_shop, only: %i[show update destroy]
33

44
def index
55
authorize Shop
@@ -53,16 +53,6 @@ def destroy
5353
render json: {status: 200}, status: :ok
5454
end
5555

56-
def reset
57-
authorize @shop
58-
59-
ApplicationRecord.transaction do
60-
@shop.reset!
61-
end
62-
63-
render json: {status: 200}, status: :ok
64-
end
65-
6656
private
6757

6858
def set_shop

app/controllers/display/base_controller.rb

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)