Files
zpan/docs
Jasper VanandClaude Fable 5 583678967d feat: spaces, quota ownership, and sharing — design doc §5 implementation (#426)
* docs: add spaces/quota/sharing design decisions

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(store): require team owner role for billing and purchase endpoints

Team orgs now gate checkout, billing portal, credits (balance/ledger/
redemptions), and order management behind the owner role. Personal orgs
are unaffected. Implements docs/design/spaces-quota-sharing.md §2.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(store): clarify purchase target and gate billing UI to owners

The store page now states which space purchases fund, hides purchase
and billing surfaces from non-owner team members with guidance to
contact the owner, and labels team orders with the team name on the
cloud side. Implements docs/design/spaces-quota-sharing.md §2.1.2-3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(objects): cross-space copy/move with file manager entry

Adds POST /api/objects/:id/transfers (copy or move a file/folder into
another space) reusing the save-to-drive copy engine; move = copy +
trash source, and the source survives any partial copy. The file
manager gains a 'Copy/Move to space' action with a space/folder picker.

Also fixes a privilege hole: save-to-drive (and the new transfer
endpoint) previously accepted any personal org as a write target,
allowing writes into other users' personal spaces; targets are now
restricted to orgs with editor access or the caller's own personal
org. The transfer folder picker also fixes the save-to-drive dialog
listing the active org's folders instead of the selected target's.
Implements docs/design/spaces-quota-sharing.md §3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(admin): per-team quota management and default team quota

Admins can now grant, edit, and revoke storage entitlements for any
space (team or personal) via /api/admin/quotas/:orgId/entitlements and
a new admin Quotas page. New teams take their initial quota from the
default_team_quota system option (falling back to default_org_quota),
configurable in admin settings. Completes the v2.2 roadmap item
'Per-team storage quota set by admin'; implements design doc §2.3.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(quotas): allocate purchased storage packs between owned spaces

Space owners can move whole one-time purchased packs (cloud_order
grants) between spaces they own via
POST /api/quotas/me/entitlements/:id/transfers. Plans and admin grants
are not transferable, and a transfer is blocked when the source space's
usage would exceed its remaining quota. The storage page lists the
current space's packs with a move dialog. Implements design doc §2.2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(shares): received shares inbox on the shares page

GET /api/shares?box=received lists active shares addressed to the
current user (matched by user id or the email the share targeted),
with the sharer's name. The shares page gains a sent/received toggle;
received items open the share landing page. This is an inbox of share
links, not a mounted filesystem (design doc §4.4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(hooks): cover default team quota in site options hook

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(store,admin): drop purchase-target banner; scope admin quotas page to teams

The storage page no longer shows the 'purchases fund X space' line —
owners see the store normally and non-owner members keep the guidance
notice. The admin Quotas page now lists team spaces only; personal
quotas stay on the user detail page, removing the overlapping entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* revert(quotas): remove storage pack allocation

Allocation (§5.5) operated on an empty set: the store only sells
per-workspace subscriptions, and the original design restricted moves
to one-time cloud packs, which don't exist in the catalog. Subscriptions
can't be safely allocated either — the webhook cancellation/downgrade
path matches on the original targetOrgId, so a moved entitlement's
claw-back silently fails and leaves ghost capacity. The feature is also
redundant: family owners subscribe the team space directly (§2.1) and
self-hosted admins grant capacity straight to it (§2.3).

Removes the quota-allocation service, /api/quotas/me/entitlements
endpoints, the storage-page packs panel, isOrgOwner helper, and related
tests/i18n. Design doc §2.2 updated to record why it was deferred and
the prerequisite (a one-time pack SKU) for revisiting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(admin): replace quotas page with Teams management (list + detail)

Promotes the admin quota surface to a proper Teams section, sibling to
Users: a team list page where each row opens a team detail page, and the
detail page manages quota entitlements (grant/edit/revoke) — mirroring
the user detail page. All backed by org data.

- New /api/admin/teams (list + detail) with member count, owner, and
  effective storage usage; teams identified by non-personal slug so
  legacy teams with null metadata are included.
- New /admin/teams list + /admin/teams/$orgId detail routes; the old
  /admin/quotas page is removed and the nav item becomes 'Teams'.
- Entitlement CRUD continues to reuse the org-generic
  /api/admin/quotas/:orgId/entitlements endpoints (invisible to users).
- Personal-space quotas remain on the user detail page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(admin): use a distinct icon for the Teams nav item

Users and Teams both used people icons (Users / UsersRound) and were
hard to tell apart in the sidebar. Teams now uses Building2 (org).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(admin): chunk team member/owner IN-lists under D1's param cap

listTeams bound all team orgIds into single member-count and owner-name
queries; on D1 (100 bound-param cap) that breaks past ~100 teams.
getEffectiveQuotasByOrg already chunks at 90 — match it for the two new
queries. Members of a given org all land in one chunk, so per-org owner
ordering is preserved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:12:45 -04:00
..
2026-06-01 10:44:33 -04:00