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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium-invites/SKILL.md +41 -0
- data/CHANGELOG.md +15 -0
- data/app/assets/plutonium.js +35 -0
- data/app/assets/plutonium.js.map +3 -3
- data/app/assets/plutonium.min.js +24 -24
- data/app/assets/plutonium.min.js.map +3 -3
- data/app/views/plutonium/_flash.html.erb +1 -1
- data/docs/guides/user-invites.md +64 -0
- data/docs/superpowers/plans/2026-05-06-multi-invite-model-support.md +1487 -0
- data/docs/superpowers/plans/2026-05-06-multi-invite-model-support.md.tasks.json +15 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/invites/install_generator.rb +136 -35
- data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +8 -2
- data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +7 -1
- data/lib/generators/pu/invites/templates/db/migrate/create_user_invites.rb.tt +4 -4
- data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +9 -4
- data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/welcome_controller.rb.tt +2 -2
- data/lib/generators/pu/invites/templates/packages/invites/app/definitions/invites/user_invite_definition.rb.tt +1 -1
- data/lib/generators/pu/invites/templates/packages/invites/app/mailers/invites/user_invite_mailer.rb.tt +8 -8
- data/lib/generators/pu/invites/templates/packages/invites/app/models/invites/user_invite.rb.tt +13 -4
- data/lib/generators/pu/invites/templates/packages/invites/app/policies/invites/user_invite_policy.rb.tt +3 -3
- data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/landing.html.erb.tt +1 -1
- data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/show.html.erb.tt +1 -1
- data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/signup.html.erb.tt +2 -2
- data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.html.erb.tt +4 -4
- data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.text.erb.tt +4 -4
- data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +5 -1
- data/lib/generators/pu/saas/welcome/templates/app/views/layouts/welcome.html.erb.tt +5 -1
- data/lib/plutonium/invites/concerns/invite_token.rb +11 -3
- data/lib/plutonium/invites/concerns/invite_user.rb +13 -4
- data/lib/plutonium/invites/controller.rb +14 -1
- data/lib/plutonium/invites/pending_invite_check.rb +37 -28
- data/lib/plutonium/resource/controllers/interactive_actions.rb +13 -9
- data/lib/plutonium/resource/policy.rb +23 -8
- data/lib/plutonium/ui/layout/sidebar.rb +1 -1
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/js/controllers/flatpickr_controller.js +23 -0
- data/src/js/controllers/sidebar_controller.js +28 -1
- metadata +5 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= render "flash_toasts" %>
|
|
1
|
+
<%= render "plutonium/flash_toasts" %>
|
data/docs/guides/user-invites.md
CHANGED
|
@@ -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
|