plutonium 0.49.0 → 0.49.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-invites/SKILL.md +41 -0
  3. data/CHANGELOG.md +15 -0
  4. data/app/assets/plutonium.js +35 -0
  5. data/app/assets/plutonium.js.map +3 -3
  6. data/app/assets/plutonium.min.js +24 -24
  7. data/app/assets/plutonium.min.js.map +3 -3
  8. data/app/views/plutonium/_flash.html.erb +1 -1
  9. data/docs/guides/user-invites.md +64 -0
  10. data/docs/superpowers/plans/2026-05-06-multi-invite-model-support.md +1487 -0
  11. data/docs/superpowers/plans/2026-05-06-multi-invite-model-support.md.tasks.json +15 -0
  12. data/gemfiles/rails_7.gemfile.lock +1 -1
  13. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  14. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  15. data/lib/generators/pu/invites/install_generator.rb +136 -35
  16. data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +8 -2
  17. data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +7 -1
  18. data/lib/generators/pu/invites/templates/db/migrate/create_user_invites.rb.tt +4 -4
  19. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +9 -4
  20. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/welcome_controller.rb.tt +2 -2
  21. data/lib/generators/pu/invites/templates/packages/invites/app/definitions/invites/user_invite_definition.rb.tt +1 -1
  22. data/lib/generators/pu/invites/templates/packages/invites/app/mailers/invites/user_invite_mailer.rb.tt +8 -8
  23. data/lib/generators/pu/invites/templates/packages/invites/app/models/invites/user_invite.rb.tt +13 -4
  24. data/lib/generators/pu/invites/templates/packages/invites/app/policies/invites/user_invite_policy.rb.tt +3 -3
  25. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/landing.html.erb.tt +1 -1
  26. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/show.html.erb.tt +1 -1
  27. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/signup.html.erb.tt +2 -2
  28. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.html.erb.tt +4 -4
  29. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.text.erb.tt +4 -4
  30. data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +5 -1
  31. data/lib/generators/pu/saas/welcome/templates/app/views/layouts/welcome.html.erb.tt +5 -1
  32. data/lib/plutonium/invites/concerns/invite_token.rb +11 -3
  33. data/lib/plutonium/invites/concerns/invite_user.rb +13 -4
  34. data/lib/plutonium/invites/controller.rb +14 -1
  35. data/lib/plutonium/invites/pending_invite_check.rb +37 -28
  36. data/lib/plutonium/resource/controllers/interactive_actions.rb +13 -9
  37. data/lib/plutonium/resource/policy.rb +23 -8
  38. data/lib/plutonium/ui/layout/sidebar.rb +1 -1
  39. data/lib/plutonium/version.rb +1 -1
  40. data/package.json +1 -1
  41. data/src/js/controllers/flatpickr_controller.js +23 -0
  42. data/src/js/controllers/sidebar_controller.js +28 -1
  43. metadata +5 -3
@@ -1 +1 @@
1
- <%= render "flash_toasts" %>
1
+ <%= render "plutonium/flash_toasts" %>
@@ -48,6 +48,7 @@ rails g pu:invites:install \
48
48
  |--------|---------|-------------|
49
49
  | `--entity-model` | Entity | Entity model for scoping invites |
50
50
  | `--user-model` | User | User account model |
51
+ | `--invite-model` | `<EntityModel><UserModel>Invite` | Invite class name. Omit for single-flow apps; set per-invocation to run the generator more than once for distinct flows. |
51
52
  | `--membership-model` | EntityUser | Join model for memberships |
52
53
  | `--roles` | member,admin | Available invitation roles |
53
54
  | `--rodauth` | user | Rodauth configuration name |
@@ -489,6 +490,69 @@ invite.expired?
489
490
  invite.cancelled?
490
491
  ```
491
492
 
493
+ ## Multiple invite flows in one app
494
+
495
+ Some apps invite users to several distinct kinds of entity — for example, customers joining organizations and funders joining projects. Run `pu:invites:install` once per flow; each invocation produces independent migrations, models, policies, definitions, mailers, controllers, view templates, and route helpers.
496
+
497
+ ### Default-derivation rule
498
+
499
+ When you omit `--invite-model`, the generator derives the class name as `<EntityModel><UserModel>Invite`. With the defaults (`--entity-model=Organization --user-model=User`) the generated class is `Invites::OrganizationUserInvite` — there is no literal `UserInvite` default. Single-flow apps never need to pass `--invite-model`.
500
+
501
+ Multi-flow apps either:
502
+
503
+ - Run the generator more than once with the **same** entity/user but different `--invite-model` values (rare), or
504
+ - Run with **different** `--entity-model` / `--user-model` pairs (common). Different derivations make `--invite-model` unnecessary; pass it only when you want a custom class name.
505
+
506
+ ```bash
507
+ rails g pu:invites:install \
508
+ --entity-model=FunderOrganization \
509
+ --user-model=SpenderAccount \
510
+ --invite-model=FunderInvite
511
+
512
+ rails g pu:invites:install \
513
+ --entity-model=Project \
514
+ --user-model=Member \
515
+ --invite-model=ProjectInvite
516
+ ```
517
+
518
+ After the second invocation you'll have, for example, `Invites::FunderInvite` on table `funder_invites` with controller `Invites::FunderInvitationsController` mounted at `/funder_invitations/:token`, alongside `Invites::ProjectInvite` on `project_invites` mounted at `/project_invitations/:token`. Route helpers are prefixed (`funder_invitation_path`, `project_invitation_path`).
519
+
520
+ ### How the welcome flow finds pending invites
521
+
522
+ The shared `Invites::WelcomeController` keeps a running list of invite classes. Each install run injects its class into the array, preserving order:
523
+
524
+ ```ruby
525
+ # packages/invites/app/controllers/invites/welcome_controller.rb
526
+ def invite_classes
527
+ [::Invites::FunderInvite, ::Invites::ProjectInvite]
528
+ end
529
+ ```
530
+
531
+ After login, `Plutonium::Invites::PendingInviteCheck#pending_invite` iterates `invite_classes` and returns the first valid pending invite for the cookie-stored token (first-match wins). Plug a third-party invite class in by overriding `invite_classes` directly:
532
+
533
+ ```ruby
534
+ class WelcomeController < ApplicationController
535
+ include Plutonium::Invites::PendingInviteCheck
536
+
537
+ def invite_classes
538
+ [::Invites::FunderInvite, ::Invites::ProjectInvite, ::Foreign::ApiInvite]
539
+ end
540
+ end
541
+ ```
542
+
543
+ ### Per-flow controller overrides
544
+
545
+ The install generator emits an `invitation_path_for` override on each invitations controller so post-login redirects target the correct prefixed route:
546
+
547
+ ```ruby
548
+ # packages/invites/app/controllers/invites/funder_invitations_controller.rb
549
+ def invitation_path_for(token)
550
+ funder_invitation_path(token: token)
551
+ end
552
+ ```
553
+
554
+ You won't normally edit this — but it's worth knowing the hook exists, because that's how multi-flow controllers stay independent from the shared `Plutonium::Invites::Controller` concern.
555
+
492
556
  ## Next Steps
493
557
 
494
558
  - [Authentication](/guides/authentication) - Set up Rodauth