@meith/plugin-dues 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -16,6 +16,20 @@ every guarantee that capability makes applies here. The load-bearing one:
16
16
  stop, or Stripe closes the account, every sold membership drains away at its
17
17
  own boundary and the board is left correct.
18
18
 
19
+ **A purchase makes the plan's group the member's primary group**, and the group
20
+ they were in — Registered, on most boards — becomes a secondary membership
21
+ alongside it. So a member who buys shows as what they bought: its title, its
22
+ colour, its badge. When the membership lapses or is revoked, the board hands
23
+ the old primary group straight back. A member who would rather be shown as
24
+ something else can pick any group they are in under UserCP → Profile; the
25
+ choice is theirs, and it drops away with the membership it names.
26
+
27
+ **Staff are the exception, and they are meant to be.** A moderator or
28
+ administrator who buys gets the group and everything it carries, as a secondary
29
+ membership — but their standing does not move, and they are still shown as
30
+ staff. Selling a badge is fine; selling somebody out of the one that says who
31
+ answers for the board is not.
32
+
19
33
  **Try it before you read any of this**: [demo.meith.dev](https://demo.meith.dev)
20
34
  runs this plugin against a Stripe that is not Stripe — a shop with a year of
21
35
  history behind it, and a checkout a visitor can actually go through. See
@@ -26,8 +40,9 @@ history behind it, and a checkout a visitor can actually go through. See
26
40
  1. **Register the plugin** where plugins are registered:
27
41
 
28
42
  ```ts
29
- // apps/community/community.plugins.ts (this repository's board uses
30
- // DUES_TEST_BOARD=1 to switch a test-shaped version of this on)
43
+ // apps/community/community.plugins.ts (this repository's own demo and
44
+ // test boards register their versions in community.demo.plugins.ts,
45
+ // behind DEMO_MODE and DUES_TEST_BOARD)
31
46
  import { dues } from '@meith/plugin-dues'
32
47
 
33
48
  export const INSTALLED_PLUGINS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meith/plugin-dues",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Membership dues for a Meith board: Stripe-backed subscriptions as a contained plugin.",
5
5
  "license": "LGPL-3.0-or-later",
6
6
  "repository": {
@@ -20,7 +20,7 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@meith/plugin-kit": "^0.2.0"
23
+ "@meith/plugin-kit": "^0.3.1"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "react": "^19.2.0"
@@ -39,7 +39,7 @@ export function dues(input: DuesConfigInput): PluginDefinition {
39
39
  return definePlugin({
40
40
  key: 'dues',
41
41
  name: 'Dues',
42
- version: '0.1.0',
42
+ version: '0.3.1',
43
43
  description:
44
44
  `Sells ${config.label.toLowerCase()} — time-limited membership of a group — ` +
45
45
  'through Stripe, for yourself or as a gift.',
@@ -121,6 +121,7 @@ export async function settlePaidOrder(
121
121
  groupKey: order.groupKey,
122
122
  until: clampGrantUntil(graceUntil, now),
123
123
  reason: grantReason(order),
124
+ primary: true,
124
125
  })
125
126
  } catch (error) {
126
127
  const reason = error instanceof Error ? error.message : String(error)
@@ -255,6 +256,7 @@ export async function applyInternalEvent(
255
256
  groupKey: membership.groupKey,
256
257
  until: clampGrantUntil(graceUntil, deps.now()),
257
258
  reason: `dues renewal of subscription ${event.subscriptionId}`,
259
+ primary: true,
258
260
  })
259
261
  } catch (error) {
260
262
  const reason = error instanceof Error ? error.message : String(error)
package/src/ui/admin.tsx CHANGED
@@ -19,7 +19,12 @@ import {
19
19
  } from '../store'
20
20
  import { SUBSCRIBED_EVENT_TYPES } from '../stripe/events'
21
21
 
22
- const CARD = 'flex flex-col gap-3 rounded-lg border border-border p-4'
22
+ const QUIET_PANEL =
23
+ 'rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground'
24
+
25
+ const CARD =
26
+ 'flex flex-col gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground ' +
27
+ 'shadow-elevation'
23
28
  const TH = 'px-2 py-1.5 text-left text-xs font-medium text-muted-foreground'
24
29
  const TD = 'px-2 py-1.5 align-top'
25
30
  const INPUT =
@@ -352,7 +357,7 @@ export async function MembersPage({ context }: { context: PluginAdminPageContext
352
357
  its own.
353
358
  </p>
354
359
  {memberships.length === 0 ? (
355
- <p className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
360
+ <p className={QUIET_PANEL}>
356
361
  Nothing sold yet.
357
362
  </p>
358
363
  ) : (
package/src/ui/pages.tsx CHANGED
@@ -14,7 +14,12 @@ import {
14
14
  type PlanRow,
15
15
  } from '../store'
16
16
 
17
- const CARD = 'flex flex-col gap-3 rounded-lg border border-border p-4'
17
+ const QUIET_PANEL =
18
+ 'rounded-lg border border-border bg-card p-4 text-sm text-muted-foreground'
19
+
20
+ const CARD =
21
+ 'flex flex-col gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground ' +
22
+ 'shadow-elevation'
18
23
  const INPUT =
19
24
  'w-full rounded-md border border-border bg-background px-3 py-2 text-sm ' +
20
25
  'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring'
@@ -263,7 +268,7 @@ export async function PlansPage({
263
268
  <Notice query={context.query} />
264
269
  <HeldCard memberships={memberships} />
265
270
  {plans.length === 0 && (
266
- <p className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
271
+ <p className={QUIET_PANEL}>
267
272
  Nothing is on sale just now.
268
273
  </p>
269
274
  )}
@@ -311,7 +316,7 @@ export function GoPage({
311
316
 
312
317
  if (!allowed) {
313
318
  return (
314
- <p className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
319
+ <p className={QUIET_PANEL}>
315
320
  That is not somewhere this board sends people.{' '}
316
321
  <a href="/plugins/dues" className="font-medium underline underline-offset-2">
317
322
  Back to safety.
@@ -356,7 +361,7 @@ export async function ReturnPage({
356
361
  (order.buyerUserId !== viewerId && order.recipientUserId !== viewerId)
357
362
  ) {
358
363
  return (
359
- <p className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
364
+ <p className={QUIET_PANEL}>
360
365
  There is no order of yours here.{' '}
361
366
  <a href="/plugins/dues" className="font-medium underline underline-offset-2">
362
367
  Back to {config.label.toLowerCase()}
@@ -489,7 +494,7 @@ export async function ManagePage({
489
494
  )}
490
495
 
491
496
  {memberships.length === 0 ? (
492
- <p className="rounded-lg border border-border p-4 text-sm text-muted-foreground">
497
+ <p className={QUIET_PANEL}>
493
498
  You hold no {config.label.toLowerCase()} yet.{' '}
494
499
  <a href="/plugins/dues" className="font-medium underline underline-offset-2">
495
500
  The plans are here.