- listProjects in members page load now uses .catch(() => null) so a
permissions or network error does not break the entire page
- projectAccessSelector disables "Add project" when projects list is
empty (prevents unusable required dropdown row)
- createMember resets orgProjects on modal close so subsequent opens
fetch a fresh list instead of showing a stale one
- edit modal init effect now checks supportsProjectRoles before setting
accessType to 'specific', preventing a silent role re-submission when
the org plan is downgraded below the project-roles threshold
Add support for assigning org members to specific projects with
per-project roles (owner, developer, editor, analyst) rather than
granting access to all projects in the organization.
- Add project-role helpers to billing store: isProjectSpecificRole,
parseProjectRole, buildProjectRole, getRoleLabel, projectRoles
- Pass projectId to getScopes in project layout so project-specific
members receive correct scopes inside their assigned projects
- Add owner-only guard on org settings page
- Load orgProjects alongside members in the members page load
- New projectAccessSelector component for inline project+role rows
with duplicate-project prevention
- Invite and edit modals use radio toggle (All / Specific projects)
with inline selector — gated to plans with supportsOrganizationRoles
- Members table shows per-project role badges for project-specific members
- roles.svelte accepts isProjectSpecific prop for contextual role descriptions
Extends the same fix to three non-settings flows that call functions.update
or sites.update and had the same bugs:
- functions/(modals)/createGit.svelte: enabled/logging || → ??; add deploymentRetention
- functions/domains/add-domain/+page.svelte: enabled/logging || → ??; add
providerSilentMode, providerRootDirectory, buildSpecification, and deploymentRetention
which were missing entirely from this connect() call
- sites/deployments/createGitDeploymentModal.svelte: enabled/logging/
providerSilentMode || → ??; add deploymentRetention
Extends the fix from #3064 to the remaining 20 settings update components
for both functions and sites. Two bugs were present in every file not
touched by that PR:
1. `enabled`, `logging`, and `providerSilentMode` were passed through
`|| undefined` instead of `?? undefined`, causing `false` values to
be silently coerced to `undefined` and reset to their API defaults
whenever any other setting was saved.
2. `deploymentRetention` was absent from every partial-update payload,
so saving name, timeout, scopes, schedule, runtime, permissions,
resource limits, build command, events, build triggers, or repository
settings would silently reset the retention value.
Also fixes `providerSilentMode: silentMode || undefined` in sites/
updateRepository.svelte to `silentMode` (matching the functions version),
so disabling silent mode is correctly preserved on save.
Bumps @appwrite.io/console SDK to 35c3dea which introduces the Organization
class. All projects.list/create/update calls are now routed through
sdk.forConsole.organization(orgId) so requests carry the
X-Appwrite-Organization header required by the new API contract.
The plan summary table used a hardcoded `addon_<key> -> label` lookup
to render addon line items, with a fallback that called every unknown
addon an "overage". Switch to reading `addon.name` from the API
response, which the aggregation endpoint now populates from the addon
catalog.
The hardcoded map stays as a fallback for older cloud builds that
don't yet send `addon.name`, and a plain resourceId is the final
fallback in place of the misleading "overage (N)" string.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The cloud backend (already merged) now cancels pending/failed invoices on
organization delete instead of throwing BILLING_INVOICE_PENDING. The
console was still gating the delete modal via the unpaid-invoices branch
of the delete-estimation endpoint, which rendered an alternate view
without the org-name confirmation input and left the Delete button
permanently disabled.
Drop the estimation-based gate (and the now-unused estimation fetch,
component, and import) so the confirm-name form always renders. The
backend will cancel any failed invoices server-side as part of the
delete. The informational upcoming-invoice Alert is preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace generic error display for 402 responses with a dedicated
budget limit error page. Includes a 'Go to billing' CTA when an
organization is in context, and a 'Change organization' secondary
action on both console-level and project-level error pages.
Optional fields (senderEmail, replyToEmail, senderName, replyToName,
username, password) could not be cleared once set. The frontend used
|| undefined which silently dropped empty strings, so the backend
never received "" and the old value persisted on reload.
smtp/+page.svelte:
- Use ?? undefined instead of || undefined so empty strings are sent
- Fix isButtonDisabled deepEqual comparison: normalize types on both
sides (?? '' for strings, ?? null for port, normalizeSecure() for
secure) to avoid false positives from null/undefined mismatches
- Derive project from data.project so it stays reactive after
invalidate() — fixes button not disabling after save without reload
- Remove second $effect that cleared fields on disable, which fought
the first effect and kept the button permanently enabled
emailTemplate.svelte:
- Use ?? undefined instead of || undefined for sender/reply fields
- Add disabled={!isSmtpEnabled} to replyToEmail and replyToName,
matching the existing behaviour of senderName and senderEmail