Commit 011f77a
authored
feat(ui): InvitationsPage names what's gating the invite form (#68)
* feat(ui): InvitationsPage names what's gating the invite form
Previously the invite form was wrapped in <Can I='invite' a='TeamMember'>,
which silently rendered nothing whenever the user lacked permission.
For a user on the free plan or in a viewer/member role this looked
like a broken page: the description says "Invite teammates" but no
input or button is anywhere to be found.
Replace the silent hide with explicit branches that name the gate:
- `lockedReason: "feature"` — the active plan doesn't include team
invites. Renders an upgrade panel + a "View plans" CTA linking to
/account/billing.
- `lockedReason: "role"` — the plan does include invites, but the
current user's role is member or viewer. Renders an "ask an Owner
or Admin" explainer.
- `lockedReason: null` — the user can invite. Renders the form as
before.
The reason is computed in useInvitationsPage and exposed on the view
object, so the page does pure branching and tests can assert on
either state by testid (`invite-locked-feature`, `invite-locked-role`).
English + German i18n strings added in lockstep.
This is the first instance of a broader UX pattern: any UI section
gated by ACL / plan / capability should render a deliberate
empty-state that names what's gated and how to unlock it, never an
invisible hide. The reusable component sweep is a follow-up PR.
* fix(ui): rewrite invitation locked-state copy for the developer audience
BoringStack is a template, not a shipped product — the operator
reading these panels is a developer building on top, not the end
user of a deployed app. The previous copy ("Upgrade to add admins,
members, viewers...") was end-user marketing. Replace with copy
that:
- Names the actual gate by symbol: `can_invite_team` plan feature
for the feature lock, `apps/ui/src/lib/acl/ability.ts` (and its
server mirror) for the role lock.
- Tells the developer how to unblock it in dev vs prod.
- Calls out that the locked-state surface itself is where they
wire their own upgrade flow when they ship the template.
Each panel now also renders a secondary "ACL & feature resolution"
button linking to https://boringstack.xyz/api/acl/, so a reader who
doesn't know the ACL system can jump straight into the reference.
en + de copy updated in lockstep.
* fix(ci): pin size-limit-action to apps/ui so it finds the build script
The bundle-diff job set `defaults: working-directory: apps/ui` at the
job level, but `andresz1/size-limit-action` spawns `npm run build` in
its own subprocess that doesn't inherit that default. Every
UI-touching PR has been failing with `npm error Missing script:
"build"` because the action ran npm from the repo root, where no
build script exists.
The action exposes a `directory:` input that pins its CWD. Setting
it to `apps/ui` makes npm find the right package.json. No code
change to apps/ui; only the workflow is touched.
* chore(ci): remove the bundle-diff workflow
The `bundle-diff` workflow has been failing across multiple PRs with
different errors depending on how it's configured (CWD wrong, then
script-as-binary exec mismatch with `directory:` set). Iterating
against the action's quirks blows CI time on a check that:
- Isn't in branch-protection required checks, so it never gated
merges anyway.
- Only delivers an advisory PR comment with the bundle-size delta.
Remove it for now. A future PR can wire up a bundle-size guard via a
different action (or a custom script that runs `size-limit` directly)
once we have time to do it right.
* Revert "chore(ci): remove the bundle-diff workflow"
This reverts commit dd69cef.
* fix(ci): let size-limit-action manage its own install lifecycle
PR-branch build was succeeding (vite output visible in logs) but the
action then failed with "Unable to locate executable file: build".
That's `@actions/exec` failing because v1.8.0 has a code path that,
when `skip_step: install` is set together with `directory:`, exec()s
the `script` string as a binary instead of via `npm run`.
Drop `skip_step: install` so the action runs its own `npm install`
on both the PR branch and the base branch, which keeps it on the
tested code path that wraps `script` in `npm run`. Costs ~20s extra
per run but is reliable. Also remove the now-unused bun setup +
pre-install step since the action no longer skips install.
apps/ui's bun lockfile is npm-compatible (the node_modules layout
matches), so npm install resolves the same dependency tree.
* fix(ci): replace size-limit-action with a direct bun + size-limit workflow
The third-party action (`andresz1/size-limit-action`) has been failing
across multiple PRs with three distinct issues:
1. Working directory: the action's exec() subprocess doesn't inherit
the job-level `defaults.run.working-directory`.
2. Script-as-binary: with `skip_step: install + directory:` it exec's
the script string as a binary.
3. Peer-dep ERESOLVE: the action's `npm install` fails on apps/ui's
tree because npm 7+ enforces peerDeps strictly while bun (the
canonical installer here) resolves them leniently.
Replace the action with a hand-rolled workflow that uses bun
end-to-end:
bun install --frozen-lockfile → matches local dev
bun run build → same vite invocation as devs
bunx size-limit → hard-fails on any budget breach
The 255 KB initial-route cap and the other budgets in
`apps/ui/.size-limit.json` still gate the PR — `size-limit` exits
non-zero when any limit is exceeded. The full per-chunk table prints
in the job log so reviewers can scan deltas.
What's lost vs the old action: the auto-posted PR comment with a
base-vs-PR diff table. The job log still shows actual sizes; the
log diff is a click away. If we want the comment back later, it's a
~20-line `actions/github-script` step over two `size-limit --json`
captures.
* fix(ci): never block required checks on branch-protection name-drift
Two required-check names (`size-diff`, `linkcheck`) were leaving PRs
stuck in "Expected — Waiting for status to be reported" because their
workflows wouldn't trigger at all on PRs whose changed files didn't
match the workflow's `paths:` filter.
Two changes:
- **`size-diff`**: keep the job name as `size-diff` (matches the
required-check entry). Remove the top-level `paths:` filter so the
workflow always runs on PRs. Add an internal `dorny/paths-filter`
step and gate every expensive step (`if: steps.filter.outputs.ui ==
'true'`). Non-UI PRs now report green immediately without booting
bun or running size-limit.
- **`linkcheck`**: same restructure. Top-level `paths:` filter dropped
from `pull_request:` (kept on `push:` to avoid wasting CI on
non-docs main pushes). Internal paths-filter gates the build +
lychee steps. Non-docs PRs report green without doing real work.
Pattern this repo already uses for several other workflows
(observation 12769): keep the workflow's gate as `if:
steps.filter.outputs.X == 'true'` rather than `paths:` at trigger
level, so branch-protection required checks always get a status.1 parent da59261 commit 011f77a
9 files changed
Lines changed: 226 additions & 56 deletions
File tree
- .github/workflows
- apps/ui/src
- features/accounts/components/InvitationsPage
- lib/i18n/locales
- de
- en
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
44 | 61 | | |
| 62 | + | |
45 | 63 | | |
46 | 64 | | |
47 | 65 | | |
48 | 66 | | |
| 67 | + | |
49 | 68 | | |
50 | 69 | | |
51 | 70 | | |
| 71 | + | |
52 | 72 | | |
53 | 73 | | |
54 | 74 | | |
55 | 75 | | |
| 76 | + | |
56 | 77 | | |
57 | 78 | | |
58 | 79 | | |
59 | 80 | | |
| 81 | + | |
60 | 82 | | |
61 | 83 | | |
62 | 84 | | |
63 | 85 | | |
64 | 86 | | |
65 | 87 | | |
| 88 | + | |
66 | 89 | | |
67 | 90 | | |
68 | 91 | | |
69 | 92 | | |
70 | 93 | | |
71 | 94 | | |
| 95 | + | |
72 | 96 | | |
73 | 97 | | |
74 | 98 | | |
| |||
79 | 103 | | |
80 | 104 | | |
81 | 105 | | |
| 106 | + | |
82 | 107 | | |
83 | 108 | | |
84 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
4 | 10 | | |
5 | 11 | | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | 12 | | |
14 | 13 | | |
15 | 14 | | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
22 | 33 | | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | 34 | | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | 35 | | |
31 | 36 | | |
32 | 37 | | |
33 | 38 | | |
34 | 39 | | |
35 | 40 | | |
36 | | - | |
| 41 | + | |
37 | 42 | | |
38 | 43 | | |
39 | 44 | | |
40 | 45 | | |
41 | | - | |
| 46 | + | |
42 | 47 | | |
43 | 48 | | |
44 | 49 | | |
45 | 50 | | |
46 | 51 | | |
47 | 52 | | |
48 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
49 | 58 | | |
50 | | - | |
| 59 | + | |
51 | 60 | | |
52 | 61 | | |
53 | 62 | | |
54 | 63 | | |
55 | 64 | | |
56 | | - | |
| 65 | + | |
| 66 | + | |
57 | 67 | | |
58 | 68 | | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
Lines changed: 27 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
| |||
47 | 50 | | |
48 | 51 | | |
49 | 52 | | |
50 | | - | |
| 53 | + | |
| 54 | + | |
51 | 55 | | |
52 | 56 | | |
53 | 57 | | |
| |||
70 | 74 | | |
71 | 75 | | |
72 | 76 | | |
73 | | - | |
| 77 | + | |
74 | 78 | | |
75 | 79 | | |
76 | 80 | | |
| |||
79 | 83 | | |
80 | 84 | | |
81 | 85 | | |
82 | | - | |
| 86 | + | |
83 | 87 | | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
84 | 105 | | |
85 | 106 | | |
86 | | - | |
| 107 | + | |
87 | 108 | | |
88 | 109 | | |
89 | 110 | | |
| |||
95 | 116 | | |
96 | 117 | | |
97 | 118 | | |
| 119 | + | |
98 | 120 | | |
99 | 121 | | |
100 | 122 | | |
| |||
Lines changed: 17 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | | - | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
21 | 24 | | |
22 | 25 | | |
23 | 26 | | |
24 | 27 | | |
25 | 28 | | |
26 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
27 | 41 | | |
28 | 42 | | |
29 | 43 | | |
| |||
73 | 87 | | |
74 | 88 | | |
75 | 89 | | |
| 90 | + | |
76 | 91 | | |
77 | 92 | | |
78 | 93 | | |
| |||
Lines changed: 17 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
110 | 110 | | |
111 | 111 | | |
112 | 112 | | |
113 | | - | |
| 113 | + | |
114 | 114 | | |
115 | 115 | | |
116 | 116 | | |
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
125 | 131 | | |
126 | 132 | | |
127 | | - | |
| 133 | + | |
128 | 134 | | |
129 | 135 | | |
130 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
131 | 146 | | |
132 | 147 | | |
133 | 148 | | |
| |||
0 commit comments