diff --git a/app/controllers/account/entropies_controller.rb b/app/controllers/account/entropies_controller.rb index 327098ee2..44c9debe5 100644 --- a/app/controllers/account/entropies_controller.rb +++ b/app/controllers/account/entropies_controller.rb @@ -4,11 +4,12 @@ class Account::EntropiesController < ApplicationController before_action :ensure_admin def update - Current.account.entropy.update!(entropy_params) + @account = Current.account + @account.entropy.update!(entropy_params) respond_to do |format| format.html { redirect_to account_settings_path, notice: "Account updated" } - format.json { head :no_content } + format.json { render "account/settings/show", status: :ok } end rescue ActiveRecord::RecordInvalid head :unprocessable_entity diff --git a/app/controllers/account/settings_controller.rb b/app/controllers/account/settings_controller.rb index 47036d606..e4b949070 100644 --- a/app/controllers/account/settings_controller.rb +++ b/app/controllers/account/settings_controller.rb @@ -5,7 +5,10 @@ class Account::SettingsController < ApplicationController before_action :set_account def show - @users = @account.users.active.alphabetically.includes(:identity) + respond_to do |format| + format.html { @users = @account.users.active.alphabetically.includes(:identity) } + format.json + end end def update diff --git a/app/controllers/boards/entropies_controller.rb b/app/controllers/boards/entropies_controller.rb index 132733aac..89e262545 100644 --- a/app/controllers/boards/entropies_controller.rb +++ b/app/controllers/boards/entropies_controller.rb @@ -10,7 +10,7 @@ class Boards::EntropiesController < ApplicationController respond_to do |format| format.turbo_stream - format.json { head :no_content } + format.json { render "boards/show", status: :ok } end rescue ActiveRecord::RecordInvalid head :unprocessable_entity diff --git a/app/views/account/settings/show.json.jbuilder b/app/views/account/settings/show.json.jbuilder index 9a62dabc6..55b623f9d 100644 --- a/app/views/account/settings/show.json.jbuilder +++ b/app/views/account/settings/show.json.jbuilder @@ -1 +1,3 @@ -json.(Current.account, :name) +json.(@account, :id, :name, :cards_count) +json.created_at @account.created_at.utc +json.auto_postpone_period_in_days @account.entropy.auto_postpone_period_in_days diff --git a/app/views/boards/_board.json.jbuilder b/app/views/boards/_board.json.jbuilder index 7c85a1dc5..0eeff1896 100644 --- a/app/views/boards/_board.json.jbuilder +++ b/app/views/boards/_board.json.jbuilder @@ -1,6 +1,7 @@ json.cache! board do json.(board, :id, :name, :all_access) json.created_at board.created_at.utc + json.auto_postpone_period_in_days board.auto_postpone_period_in_days json.url board_url(board) json.creator board.creator, partial: "users/user", as: :user diff --git a/docs/API.md b/docs/API.md index cec58f36e..e8b60697c 100644 --- a/docs/API.md +++ b/docs/API.md @@ -434,6 +434,7 @@ __Response:__ "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", @@ -460,6 +461,7 @@ __Response:__ "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", @@ -484,7 +486,7 @@ Creates a new Board in the account. |-----------|------|----------|-------------| | `name` | string | Yes | The name of the board | | `all_access` | boolean | No | Whether any user in the account can access this board. Defaults to `true` | -| `auto_postpone_period` | integer | No | Number of days of inactivity before cards are automatically postponed | +| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | | `public_description` | string | No | Rich text description shown on the public board page | __Request:__ @@ -514,7 +516,7 @@ Updates a Board. Only board administrators can update a board. |-----------|------|----------|-------------| | `name` | string | No | The name of the board | | `all_access` | boolean | No | Whether any user in the account can access this board | -| `auto_postpone_period` | integer | No | Number of days of inactivity before cards are automatically postponed | +| `auto_postpone_period_in_days` | integer | No | Number of days of inactivity before cards are automatically postponed (e.g. `30`) | | `public_description` | string | No | Rich text description shown on the public board page | | `user_ids` | array | No | Array of *all* user IDs who should have access to this board (only applicable when `all_access` is `false`) | @@ -524,7 +526,7 @@ __Request:__ { "board": { "name": "Updated board name", - "auto_postpone_period": 14, + "auto_postpone_period_in_days": 30, "public_description": "This is a **public** description of the board.", "all_access": false, "user_ids": [ @@ -567,6 +569,7 @@ HTTP/1.1 201 Created "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", @@ -591,6 +594,72 @@ __Response:__ Returns `204 No Content` on success. +## Account + +### `GET /account/settings` + +Returns the current account. + +__Response:__ + +```json +{ + "id": "03f5v9zjvypwh0t0e2rfh0h7k", + "name": "37signals", + "cards_count": 5, + "created_at": "2025-12-05T19:36:35.401Z", + "auto_postpone_period_in_days": 30 +} +``` + +The `auto_postpone_period_in_days` is the account-level default in days (e.g. `30`). Cards are automatically moved to "Not Now" after this period of inactivity. Each board can override this with its own value. + +### `PUT /account/entropy` + +Updates the account-level default auto close period. Requires admin role. + +__Request:__ + +```json +{ + "entropy": { + "auto_postpone_period_in_days": 30 + } +} +``` + +__Response:__ + +Returns the account object: + +```json +{ + "id": "03f5v9zjvypwh0t0e2rfh0h7k", + "name": "37signals", + "cards_count": 5, + "created_at": "2025-12-05T19:36:35.401Z", + "auto_postpone_period_in_days": 30 +} +``` + +### `PUT /:account_slug/boards/:board_id/entropy` + +Updates the auto close period for a specific board. Requires board admin permission. + +__Request:__ + +```json +{ + "board": { + "auto_postpone_period_in_days": 90 + } +} +``` + +__Response:__ + +Returns the board object. + ## Webhooks Webhooks notify another application when something happens on a board. Only account admins can list, view, create, update, delete, or reactivate webhooks. @@ -757,6 +826,7 @@ __Response:__ "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", @@ -810,6 +880,7 @@ __Response:__ "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", @@ -1077,6 +1148,7 @@ __Response:__ "name": "Fizzy", "all_access": true, "created_at": "2025-12-05T19:36:35.534Z", + "auto_postpone_period_in_days": 30, "url": "http://fizzy.localhost:3006/897362094/boards/03f5v9zkft4hj9qq0lsn9ohcm", "creator": { "id": "03f5v9zjw7pz8717a4no1h8a7", diff --git a/test/controllers/accounts/entropies_controller_test.rb b/test/controllers/accounts/entropies_controller_test.rb index d00c0d768..fa2819d48 100644 --- a/test/controllers/accounts/entropies_controller_test.rb +++ b/test/controllers/accounts/entropies_controller_test.rb @@ -16,8 +16,9 @@ class Account::EntropiesControllerTest < ActionDispatch::IntegrationTest test "update as JSON" do put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 7 } }, as: :json - assert_response :no_content + assert_response :success assert_equal 7.days, entropies("37s_account").reload.auto_postpone_period + assert_equal 7, @response.parsed_body["auto_postpone_period_in_days"] end test "update requires admin" do @@ -35,4 +36,20 @@ class Account::EntropiesControllerTest < ActionDispatch::IntegrationTest assert_response :unprocessable_entity assert_equal original_period, entropies("37s_account").reload.auto_postpone_period end + + test "update as JSON rejects invalid auto_postpone_period" do + original_period = entropies("37s_account").auto_postpone_period + + put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 1 } }, as: :json + + assert_response :unprocessable_entity + assert_equal original_period, entropies("37s_account").reload.auto_postpone_period + end + + test "update as JSON requires admin" do + logout_and_sign_in_as :david + + put account_entropy_path, params: { entropy: { auto_postpone_period_in_days: 7 } }, as: :json + assert_response :forbidden + end end diff --git a/test/controllers/accounts/settings_controller_test.rb b/test/controllers/accounts/settings_controller_test.rb index 6f3fba5b0..88a6ef137 100644 --- a/test/controllers/accounts/settings_controller_test.rb +++ b/test/controllers/accounts/settings_controller_test.rb @@ -16,13 +16,6 @@ class Account::SettingsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to account_settings_path end - test "show as JSON" do - get account_settings_path, as: :json - assert_response :success - - assert_equal Current.account.name, @response.parsed_body["name"] - end - test "update as JSON" do put account_settings_path, params: { account: { name: "New Account Name" } }, as: :json @@ -36,4 +29,13 @@ class Account::SettingsControllerTest < ActionDispatch::IntegrationTest put account_settings_path, params: { account: { name: "New Account Name" } } assert_response :forbidden end + + test "show as JSON" do + get account_settings_path, as: :json + + assert_response :success + assert_equal Current.account.name, @response.parsed_body["name"] + assert_equal Current.account.cards_count, @response.parsed_body["cards_count"] + assert_equal Current.account.entropy.auto_postpone_period_in_days, @response.parsed_body["auto_postpone_period_in_days"] + end end diff --git a/test/controllers/api/flat_json_params_test.rb b/test/controllers/api/flat_json_params_test.rb index 90f55f8fd..7a9a5ea5d 100644 --- a/test/controllers/api/flat_json_params_test.rb +++ b/test/controllers/api/flat_json_params_test.rb @@ -41,14 +41,14 @@ class FlatJsonParamsTest < ActionDispatch::IntegrationTest put board_entropy_path(board), params: { auto_postpone_period_in_days: 90 }, as: :json - assert_response :no_content + assert_response :success assert_equal 90.days, board.entropy.reload.auto_postpone_period end test "update account entropy with flat JSON" do put account_entropy_path, params: { auto_postpone_period_in_days: 7 }, as: :json - assert_response :no_content + assert_response :success assert_equal 7.days, Current.account.entropy.reload.auto_postpone_period end diff --git a/test/controllers/boards/entropies_controller_test.rb b/test/controllers/boards/entropies_controller_test.rb index e0fd4fa52..4173694ca 100644 --- a/test/controllers/boards/entropies_controller_test.rb +++ b/test/controllers/boards/entropies_controller_test.rb @@ -17,10 +17,13 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest end test "update as JSON" do - put board_entropy_path(@board), params: { board: { auto_postpone_period_in_days: 90 } }, as: :json + assert_no_difference -> { Current.account.entropy.reload.auto_postpone_period } do + put board_entropy_path(@board), params: { board: { auto_postpone_period_in_days: 90 } }, as: :json - assert_response :no_content - assert_equal 90.days, @board.entropy.reload.auto_postpone_period + assert_response :success + assert_equal 90.days, @board.entropy.reload.auto_postpone_period + assert_equal 90, @response.parsed_body["auto_postpone_period_in_days"] + end end test "update requires board admin permission" do @@ -42,4 +45,24 @@ class Boards::EntropiesControllerTest < ActionDispatch::IntegrationTest assert_response :unprocessable_entity assert_equal original_period, @board.entropy.reload.auto_postpone_period end + + test "update as JSON rejects invalid auto_postpone_period" do + original_period = @board.entropy.auto_postpone_period + + put board_entropy_path(@board), params: { board: { auto_postpone_period_in_days: 1 } }, as: :json + + assert_response :unprocessable_entity + assert_equal original_period, @board.entropy.reload.auto_postpone_period + end + + test "update as JSON requires board admin permission" do + logout_and_sign_in_as :jz + + original_period = @board.entropy.auto_postpone_period + + put board_entropy_path(@board), params: { board: { auto_postpone_period_in_days: 7 } }, as: :json + + assert_response :forbidden + assert_equal original_period, @board.entropy.reload.auto_postpone_period + end end diff --git a/test/controllers/boards_controller_test.rb b/test/controllers/boards_controller_test.rb index 915310108..059c38026 100644 --- a/test/controllers/boards_controller_test.rb +++ b/test/controllers/boards_controller_test.rb @@ -258,6 +258,7 @@ class BoardsControllerTest < ActionDispatch::IntegrationTest get board_path(boards(:writebook)), as: :json assert_response :success assert_equal boards(:writebook).name, @response.parsed_body["name"] + assert_equal boards(:writebook).auto_postpone_period_in_days, @response.parsed_body["auto_postpone_period_in_days"] end test "show as JSON includes public_url when published" do