devpush 0.0.0-stage → 1.0.0-beta.4
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 +22 -2
- package/agent-release.json +37 -0
- package/dist/package/LICENSE.md +21 -0
- package/dist/package/migrate.mjs +182 -0
- package/dist/package/plugins.mjs +125 -0
- package/dist/package/vite.mjs +439 -0
- package/package.json +168 -3
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon.svg +1 -0
- package/public/logo-email.png +0 -0
- package/public/robots.txt +3 -0
- package/public/social.png +0 -0
- package/src/cloudflare-runtime.d.ts +78 -0
- package/src/components/about-dialog.tsx +70 -0
- package/src/components/copy-button.tsx +74 -0
- package/src/components/logo-mark.tsx +20 -0
- package/src/components/pending-collection.tsx +103 -0
- package/src/components/resource-avatar.tsx +65 -0
- package/src/components/route-error-state.tsx +95 -0
- package/src/components/section-navigation.tsx +39 -0
- package/src/components/settings-layout.tsx +111 -0
- package/src/components/storage-capabilities.tsx +11 -0
- package/src/components/table-pager.tsx +101 -0
- package/src/components/ui/alert-dialog.tsx +180 -0
- package/src/components/ui/alert.tsx +78 -0
- package/src/components/ui/avatar.tsx +58 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/button-group.tsx +38 -0
- package/src/components/ui/button.tsx +67 -0
- package/src/components/ui/card.tsx +82 -0
- package/src/components/ui/chart.tsx +307 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/combobox.tsx +256 -0
- package/src/components/ui/dialog.tsx +163 -0
- package/src/components/ui/dropdown-menu.tsx +110 -0
- package/src/components/ui/empty.tsx +65 -0
- package/src/components/ui/field.tsx +192 -0
- package/src/components/ui/input-group.tsx +118 -0
- package/src/components/ui/input-otp.tsx +70 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/pagination.tsx +34 -0
- package/src/components/ui/popover.tsx +86 -0
- package/src/components/ui/select.tsx +145 -0
- package/src/components/ui/skeleton.tsx +19 -0
- package/src/components/ui/sonner.tsx +47 -0
- package/src/components/ui/switch.tsx +31 -0
- package/src/components/ui/table.tsx +81 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +55 -0
- package/src/features/admin/admin-settings.tsx +1048 -0
- package/src/features/auth/current-user-context-query.ts +11 -0
- package/src/features/auth/login-form.tsx +442 -0
- package/src/features/auth/sign-out-menu-item.tsx +42 -0
- package/src/features/deployments/deployment-actions.tsx +336 -0
- package/src/features/deployments/deployment-alias-badge.tsx +35 -0
- package/src/features/deployments/deployment-detail.tsx +596 -0
- package/src/features/deployments/deployment-filters.ts +66 -0
- package/src/features/deployments/deployment-list.tsx +465 -0
- package/src/features/deployments/deployment-visit-links.tsx +104 -0
- package/src/features/deployments/deployments-page.tsx +376 -0
- package/src/features/logs/log-scroll-area.tsx +69 -0
- package/src/features/notifications/email-preferences.tsx +232 -0
- package/src/features/onboarding/profile-onboarding.tsx +162 -0
- package/src/features/plugins/contributions.tsx +118 -0
- package/src/features/projects/backup-job-logs.tsx +143 -0
- package/src/features/projects/backup-policy-delete-dialog.tsx +82 -0
- package/src/features/projects/backup-restore-actions.tsx +573 -0
- package/src/features/projects/backup-status-icon.tsx +33 -0
- package/src/features/projects/backup-target-list.tsx +222 -0
- package/src/features/projects/backup-timestamp.tsx +32 -0
- package/src/features/projects/backup-volume-picker.tsx +158 -0
- package/src/features/projects/compose-service-input.tsx +64 -0
- package/src/features/projects/deploy-readiness.ts +28 -0
- package/src/features/projects/environment-dot.tsx +91 -0
- package/src/features/projects/log-keyword-filter.tsx +48 -0
- package/src/features/projects/manual-deploy-dialog.tsx +563 -0
- package/src/features/projects/pending-section.ts +26 -0
- package/src/features/projects/project-backup-details-pending.tsx +305 -0
- package/src/features/projects/project-backup-details.tsx +936 -0
- package/src/features/projects/project-backup-policy.tsx +644 -0
- package/src/features/projects/project-backup-utils.ts +190 -0
- package/src/features/projects/project-backups-pending.tsx +136 -0
- package/src/features/projects/project-backups.tsx +876 -0
- package/src/features/projects/project-environment-settings.tsx +1128 -0
- package/src/features/projects/project-heading.tsx +68 -0
- package/src/features/projects/project-layout.tsx +188 -0
- package/src/features/projects/project-logs.tsx +461 -0
- package/src/features/projects/project-overview.tsx +280 -0
- package/src/features/projects/project-route-pending.tsx +1064 -0
- package/src/features/projects/project-routing-settings.tsx +663 -0
- package/src/features/projects/project-settings-section.tsx +64 -0
- package/src/features/projects/project-settings-utils.ts +101 -0
- package/src/features/projects/project-settings.tsx +1472 -0
- package/src/features/projects/repository-picker-dialog.tsx +404 -0
- package/src/features/repositories/new-project-dialog-launcher.tsx +44 -0
- package/src/features/repositories/new-project-dialog.tsx +1204 -0
- package/src/features/repositories/new-project-pending.tsx +28 -0
- package/src/features/repositories/repository-list-pending.tsx +59 -0
- package/src/features/servers/server-availability-alert.tsx +44 -0
- package/src/features/servers/server-availability.ts +29 -0
- package/src/features/servers/server-avatar.tsx +52 -0
- package/src/features/servers/server-detail-pending.tsx +315 -0
- package/src/features/servers/server-detail.tsx +855 -0
- package/src/features/servers/server-metric-chart-pending.tsx +38 -0
- package/src/features/servers/server-metric-chart.tsx +182 -0
- package/src/features/servers/server-metric-format.ts +50 -0
- package/src/features/servers/server-metrics-pending.tsx +75 -0
- package/src/features/servers/server-metrics.tsx +136 -0
- package/src/features/servers/team-servers.tsx +432 -0
- package/src/features/settings/settings-route-pending.tsx +406 -0
- package/src/features/teams/backup-storage-settings.tsx +483 -0
- package/src/features/teams/navigation.ts +51 -0
- package/src/features/teams/new-team-dialog.tsx +132 -0
- package/src/features/teams/pending-section.ts +19 -0
- package/src/features/teams/team-header.tsx +1286 -0
- package/src/features/teams/team-heading.tsx +57 -0
- package/src/features/teams/team-home.tsx +391 -0
- package/src/features/teams/team-invite.tsx +155 -0
- package/src/features/teams/team-route-pending.tsx +505 -0
- package/src/features/teams/team-settings.tsx +1555 -0
- package/src/features/teams/use-workspace-updates.ts +126 -0
- package/src/features/users/api-keys-settings.tsx +544 -0
- package/src/features/users/api-scope-selection.tsx +269 -0
- package/src/features/users/connected-apps-settings.tsx +449 -0
- package/src/features/users/mcp-consent.tsx +220 -0
- package/src/features/users/profile-picture.tsx +171 -0
- package/src/features/users/user-settings.tsx +1064 -0
- package/src/lib/account-settings-schema.ts +78 -0
- package/src/lib/action-feedback.ts +81 -0
- package/src/lib/admin-search-schema.ts +8 -0
- package/src/lib/agent-release.d.mts +21 -0
- package/src/lib/agent-release.mjs +90 -0
- package/src/lib/allowlist-schema.ts +79 -0
- package/src/lib/api-key-schema.ts +176 -0
- package/src/lib/auth/client.ts +20 -0
- package/src/lib/avatar-schema.ts +11 -0
- package/src/lib/backup-cron.ts +118 -0
- package/src/lib/backup-database.ts +41 -0
- package/src/lib/backup-feature.ts +2 -0
- package/src/lib/backup-paths.ts +7 -0
- package/src/lib/backup-policy-schema.ts +85 -0
- package/src/lib/backup-resource-schema.ts +12 -0
- package/src/lib/backup-restore-schema.ts +15 -0
- package/src/lib/backup-storage-schema.ts +55 -0
- package/src/lib/deferred-module.ts +38 -0
- package/src/lib/deployment-logs-query.ts +63 -0
- package/src/lib/display-name.ts +7 -0
- package/src/lib/error-message.ts +119 -0
- package/src/lib/form-errors.ts +10 -0
- package/src/lib/internal-redirect.ts +25 -0
- package/src/lib/manual-deploy-options-query.ts +21 -0
- package/src/lib/notification-preferences.ts +43 -0
- package/src/lib/onboarding.ts +11 -0
- package/src/lib/operation-logs-query.ts +51 -0
- package/src/lib/page-head.ts +14 -0
- package/src/lib/project-context-query.ts +257 -0
- package/src/lib/project-creation-schema.ts +64 -0
- package/src/lib/project-env-schema.ts +23 -0
- package/src/lib/project-environments-schema.ts +56 -0
- package/src/lib/project-routing-schema.ts +94 -0
- package/src/lib/project-runtime-config.ts +131 -0
- package/src/lib/project-settings-schema.ts +75 -0
- package/src/lib/public-api-schema.ts +128 -0
- package/src/lib/public-error.ts +337 -0
- package/src/lib/query.ts +13 -0
- package/src/lib/repository-selection-query.ts +11 -0
- package/src/lib/resource-cache.ts +30 -0
- package/src/lib/runtime-id.ts +19 -0
- package/src/lib/search.ts +14 -0
- package/src/lib/server-context-query.ts +43 -0
- package/src/lib/server-name-schema.ts +11 -0
- package/src/lib/settings-conflict.ts +45 -0
- package/src/lib/settings-version.ts +7 -0
- package/src/lib/team-context-query.ts +126 -0
- package/src/lib/team-context-schema.ts +8 -0
- package/src/lib/team-forms-schema.ts +45 -0
- package/src/lib/team-role.ts +13 -0
- package/src/lib/team-settings-schema.ts +48 -0
- package/src/lib/urls.ts +3 -0
- package/src/lib/use-avatar-input.ts +61 -0
- package/src/lib/use-refresh-error.ts +18 -0
- package/src/lib/use-settings-version.ts +23 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validation.ts +5 -0
- package/src/lib/workspace-updates.ts +53 -0
- package/src/package/config.ts +10 -0
- package/src/package/env.mjs +54 -0
- package/src/package/migrate-api.d.ts +5 -0
- package/src/package/migrate.ts +56 -0
- package/src/package/plugins.ts +141 -0
- package/src/package/sqlite.mjs +2 -0
- package/src/package/vite-api.d.ts +5 -0
- package/src/package/vite.ts +324 -0
- package/src/package/watch-guard.cjs +26 -0
- package/src/plugins/client.ts +57 -0
- package/src/plugins/contract.ts +188 -0
- package/src/plugins/default.server.ts +4 -0
- package/src/plugins/default.ui.ts +4 -0
- package/src/plugins/operations.ts +45 -0
- package/src/plugins/server.ts +197 -0
- package/src/plugins/version.ts +1 -0
- package/src/routeTree.gen.ts +1275 -0
- package/src/router.tsx +46 -0
- package/src/routes/$teamSlug.index.tsx +53 -0
- package/src/routes/$teamSlug.new-project.tsx +13 -0
- package/src/routes/$teamSlug.projects.$projectName.backups.$backupId.tsx +40 -0
- package/src/routes/$teamSlug.projects.$projectName.backups.tsx +82 -0
- package/src/routes/$teamSlug.projects.$projectName.deployments.$deploymentId.tsx +52 -0
- package/src/routes/$teamSlug.projects.$projectName.deployments.tsx +86 -0
- package/src/routes/$teamSlug.projects.$projectName.logs.tsx +55 -0
- package/src/routes/$teamSlug.projects.$projectName.settings.tsx +51 -0
- package/src/routes/$teamSlug.projects.$projectName.tsx +106 -0
- package/src/routes/$teamSlug.projects.tsx +40 -0
- package/src/routes/$teamSlug.servers.$serverId.tsx +31 -0
- package/src/routes/$teamSlug.servers.index.tsx +59 -0
- package/src/routes/$teamSlug.servers.tsx +7 -0
- package/src/routes/$teamSlug.settings.tsx +56 -0
- package/src/routes/$teamSlug.tsx +86 -0
- package/src/routes/[.]well-known.oauth-authorization-server.api.auth.ts +13 -0
- package/src/routes/[.]well-known.oauth-protected-resource.mcp.ts +6 -0
- package/src/routes/__root.tsx +155 -0
- package/src/routes/admin.tsx +55 -0
- package/src/routes/api/agents/certificates.ts +21 -0
- package/src/routes/api/agents/credentials/rotate.ts +22 -0
- package/src/routes/api/agents/heartbeat.ts +42 -0
- package/src/routes/api/agents/job-logs.ts +22 -0
- package/src/routes/api/agents/jobs/complete.ts +22 -0
- package/src/routes/api/agents/jobs/next.ts +29 -0
- package/src/routes/api/agents/jobs/progress.ts +32 -0
- package/src/routes/api/agents/jobs/status.ts +22 -0
- package/src/routes/api/agents/register.ts +45 -0
- package/src/routes/api/agents/runtime-logs.ts +22 -0
- package/src/routes/api/agents/switch.ts +25 -0
- package/src/routes/api/auth/$.ts +38 -0
- package/src/routes/api/cron/backups.ts +33 -0
- package/src/routes/api/cron/maintenance.ts +30 -0
- package/src/routes/api/github/install.callback.ts +51 -0
- package/src/routes/api/github/install.ts +41 -0
- package/src/routes/api/github/webhook.ts +11 -0
- package/src/routes/api/health.ts +19 -0
- package/src/routes/api/install/agent.$token.ts +50 -0
- package/src/routes/api/install/agent.binary.ts +83 -0
- package/src/routes/api/install/agent.checksum.ts +45 -0
- package/src/routes/api/install/agent.ts +26 -0
- package/src/routes/api/plugins/$.ts +11 -0
- package/src/routes/api/v1/$.ts +15 -0
- package/src/routes/api/workspaces/updates.ts +8 -0
- package/src/routes/auth.login.tsx +50 -0
- package/src/routes/deployments.$deploymentId.unavailable.tsx +21 -0
- package/src/routes/index.tsx +23 -0
- package/src/routes/invites.$token.tsx +35 -0
- package/src/routes/mcp.ts +13 -0
- package/src/routes/oauth.consent.tsx +54 -0
- package/src/routes/onboarding.tsx +37 -0
- package/src/routes/onboarding.workspace.tsx +44 -0
- package/src/routes/settings.tsx +30 -0
- package/src/routes/uploads/avatars/$fileName.ts +44 -0
- package/src/server/admin/access.ts +67 -0
- package/src/server/admin/functions.ts +122 -0
- package/src/server/admin/service.ts +260 -0
- package/src/server/agents/backup-state.ts +210 -0
- package/src/server/agents/certificates.ts +96 -0
- package/src/server/agents/claims.ts +116 -0
- package/src/server/agents/cleanup.ts +52 -0
- package/src/server/agents/compatibility.ts +116 -0
- package/src/server/agents/completion.ts +83 -0
- package/src/server/agents/container-cleanup-state.ts +51 -0
- package/src/server/agents/deployment-state.ts +111 -0
- package/src/server/agents/job-payloads.ts +159 -0
- package/src/server/agents/jobs.ts +1293 -0
- package/src/server/agents/protocol.ts +400 -0
- package/src/server/agents/reconciliation.ts +43 -0
- package/src/server/agents/runtime-logs.ts +337 -0
- package/src/server/agents/runtime-state.ts +125 -0
- package/src/server/api/admission.ts +12 -0
- package/src/server/api/authorization.ts +115 -0
- package/src/server/api/credentials.ts +569 -0
- package/src/server/api/cursors.ts +72 -0
- package/src/server/api/errors.ts +24 -0
- package/src/server/api/functions.ts +60 -0
- package/src/server/api/handler.ts +274 -0
- package/src/server/api/mutation-operations.ts +913 -0
- package/src/server/api/operation-context.ts +139 -0
- package/src/server/api/operation-contract.ts +1099 -0
- package/src/server/api/operations.ts +64 -0
- package/src/server/api/read-operations.ts +891 -0
- package/src/server/api/retention.ts +33 -0
- package/src/server/auth/access.ts +109 -0
- package/src/server/auth/auth-methods.ts +28 -0
- package/src/server/auth/better-auth.ts +518 -0
- package/src/server/auth/context.ts +28 -0
- package/src/server/auth/email-change.ts +47 -0
- package/src/server/auth/email-delivery.ts +51 -0
- package/src/server/auth/provider-identity.ts +19 -0
- package/src/server/auth/provider-tokens.ts +27 -0
- package/src/server/auth/session.server.ts +19 -0
- package/src/server/backups/database-commands.ts +40 -0
- package/src/server/backups/destination.ts +12 -0
- package/src/server/backups/feature.ts +6 -0
- package/src/server/backups/policy-validation.ts +51 -0
- package/src/server/backups/queue-state.ts +137 -0
- package/src/server/backups/service.ts +918 -0
- package/src/server/backups/storage-functions.ts +42 -0
- package/src/server/backups/storage-state.ts +67 -0
- package/src/server/backups/storage.ts +237 -0
- package/src/server/config.ts +274 -0
- package/src/server/db/atomic.ts +16 -0
- package/src/server/db/d1-connection.ts +83 -0
- package/src/server/db/errors.ts +17 -0
- package/src/server/db/index.cloudflare.ts +15 -0
- package/src/server/db/index.ts +35 -0
- package/src/server/db/libsql-connection.ts +72 -0
- package/src/server/db/log-schema.ts +102 -0
- package/src/server/db/migrate-libsql.ts +29 -0
- package/src/server/db/migrate-sqlite.ts +47 -0
- package/src/server/db/migration-history.ts +12 -0
- package/src/server/db/migrations-logs/0000_initial.sql +48 -0
- package/src/server/db/migrations-logs/0001_runtime_log_order.sql +2 -0
- package/src/server/db/migrations-logs/meta/0000_snapshot.json +327 -0
- package/src/server/db/migrations-logs/meta/0001_snapshot.json +329 -0
- package/src/server/db/migrations-logs/meta/_journal.json +20 -0
- package/src/server/db/migrations-sqlite/0000_initial.sql +894 -0
- package/src/server/db/migrations-sqlite/0001_agent_indexes.sql +5 -0
- package/src/server/db/migrations-sqlite/meta/0000_snapshot.json +5816 -0
- package/src/server/db/migrations-sqlite/meta/0001_snapshot.json +5841 -0
- package/src/server/db/migrations-sqlite/meta/_journal.json +20 -0
- package/src/server/db/oauth-schema.ts +216 -0
- package/src/server/db/remote-config.ts +47 -0
- package/src/server/db/schema.ts +1218 -0
- package/src/server/db/sqlite-connection.ts +94 -0
- package/src/server/db/sqlite-files.ts +27 -0
- package/src/server/db/sqlite-params.ts +19 -0
- package/src/server/db/worker-connection.ts +47 -0
- package/src/server/deleted-resources.ts +133 -0
- package/src/server/deletion-recovery.ts +272 -0
- package/src/server/email/layout.tsx +85 -0
- package/src/server/email/notification-template.tsx +62 -0
- package/src/server/email/otp-template.tsx +48 -0
- package/src/server/email/service.ts +251 -0
- package/src/server/email/team-invite-template.tsx +59 -0
- package/src/server/email/theme.ts +10 -0
- package/src/server/github/http.ts +14 -0
- package/src/server/github/install-state.ts +48 -0
- package/src/server/github/service.ts +831 -0
- package/src/server/github/webhook-handler.ts +198 -0
- package/src/server/github/webhooks.ts +205 -0
- package/src/server/http/auth.ts +11 -0
- package/src/server/http/error-diagnostics.ts +66 -0
- package/src/server/http/plugin-request.ts +15 -0
- package/src/server/http/proxy-request.ts +53 -0
- package/src/server/http/public-errors.ts +48 -0
- package/src/server/http/rate-limit.ts +59 -0
- package/src/server/http/request-body.ts +54 -0
- package/src/server/http/request-ip.ts +54 -0
- package/src/server/http/security-headers.ts +36 -0
- package/src/server/install/agent-binary.ts +230 -0
- package/src/server/install/agent-script.ts +565 -0
- package/src/server/install/release-binary.ts +65 -0
- package/src/server/logs/budget.ts +72 -0
- package/src/server/logs/cleanup.ts +64 -0
- package/src/server/logs/deployments.ts +98 -0
- package/src/server/logs/operations.ts +145 -0
- package/src/server/logs/retention.ts +39 -0
- package/src/server/maintenance.ts +61 -0
- package/src/server/mcp/connections.ts +358 -0
- package/src/server/mcp/functions.ts +63 -0
- package/src/server/mcp/handler.ts +295 -0
- package/src/server/mcp/oauth-discovery.cloudflare.ts +6 -0
- package/src/server/mcp/oauth-discovery.ts +8 -0
- package/src/server/mcp/oauth-provider.ts +220 -0
- package/src/server/mcp/urls.ts +20 -0
- package/src/server/notifications/functions.ts +51 -0
- package/src/server/notifications/observe.ts +303 -0
- package/src/server/notifications/outbox.ts +335 -0
- package/src/server/notifications/preferences.ts +131 -0
- package/src/server/notifications/query.ts +16 -0
- package/src/server/plugins/scope.ts +17 -0
- package/src/server/plugins/server-context.ts +157 -0
- package/src/server/plugins/services.server.ts +10 -0
- package/src/server/plugins/workspace-access.ts +74 -0
- package/src/server/projects/backup-policy-state.ts +68 -0
- package/src/server/projects/backups.ts +219 -0
- package/src/server/projects/compose-metadata.ts +174 -0
- package/src/server/projects/configuration-state.ts +90 -0
- package/src/server/projects/context.ts +909 -0
- package/src/server/projects/creation-state.ts +46 -0
- package/src/server/projects/deletion-state.ts +69 -0
- package/src/server/projects/deployment-config.ts +139 -0
- package/src/server/projects/deployment-logs.ts +97 -0
- package/src/server/projects/deployments.ts +1123 -0
- package/src/server/projects/domain-state.ts +71 -0
- package/src/server/projects/environment-branches.ts +75 -0
- package/src/server/projects/environment-state.ts +158 -0
- package/src/server/projects/environments.ts +424 -0
- package/src/server/projects/functions.ts +748 -0
- package/src/server/projects/generated-hostname.ts +77 -0
- package/src/server/projects/hostnames.ts +244 -0
- package/src/server/projects/mappers.ts +457 -0
- package/src/server/projects/names.ts +41 -0
- package/src/server/projects/project-env-vars.ts +28 -0
- package/src/server/projects/queue-state.ts +118 -0
- package/src/server/projects/repository.ts +368 -0
- package/src/server/projects/route-state.ts +37 -0
- package/src/server/projects/runtime-logs.ts +97 -0
- package/src/server/projects/settings-context.ts +58 -0
- package/src/server/projects/settings.ts +738 -0
- package/src/server/projects/types.ts +355 -0
- package/src/server/realtime/agent-events.ts +32 -0
- package/src/server/realtime/node-broadcaster.ts +71 -0
- package/src/server/realtime/node-handler.ts +55 -0
- package/src/server/realtime/service.ts +125 -0
- package/src/server/realtime/workspace-object.cloudflare.ts +62 -0
- package/src/server/realtime/workspace-object.ts +3 -0
- package/src/server/resources/color.ts +13 -0
- package/src/server/runtime/background.ts +21 -0
- package/src/server/runtime/node-asset-cache-plugin.ts +12 -0
- package/src/server/runtime/node-scheduler-plugin.ts +17 -0
- package/src/server/runtime/node-scheduler.ts +51 -0
- package/src/server/runtime/platform-env.cloudflare.ts +17 -0
- package/src/server/runtime/platform-env.ts +19 -0
- package/src/server/runtime/scheduled-task-error.ts +9 -0
- package/src/server/scheduled-tasks.ts +28 -0
- package/src/server/secrets/crypto.ts +56 -0
- package/src/server/servers/functions.ts +233 -0
- package/src/server/servers/lifecycle.ts +122 -0
- package/src/server/servers/metric-history-schema.ts +24 -0
- package/src/server/servers/metric-history.ts +55 -0
- package/src/server/servers/monitoring.ts +592 -0
- package/src/server/servers/service.ts +1211 -0
- package/src/server/servers/status.ts +32 -0
- package/src/server/servers/switch.ts +96 -0
- package/src/server/settings-version.ts +43 -0
- package/src/server/storage/capabilities.ts +6 -0
- package/src/server/storage/local-object-store.cloudflare.ts +7 -0
- package/src/server/storage/local-object-store.ts +50 -0
- package/src/server/storage/object-store.ts +160 -0
- package/src/server/teams/creation.ts +51 -0
- package/src/server/teams/delete-policy.ts +13 -0
- package/src/server/teams/deletion-state.ts +80 -0
- package/src/server/teams/functions.ts +324 -0
- package/src/server/teams/invitations.ts +38 -0
- package/src/server/teams/membership.ts +86 -0
- package/src/server/teams/queries.ts +261 -0
- package/src/server/teams/service.ts +1127 -0
- package/src/server/teams/types.ts +101 -0
- package/src/server/uploads/avatar.ts +133 -0
- package/src/server/users/deletion-state.ts +93 -0
- package/src/server/users/functions.ts +160 -0
- package/src/server/users/onboarding.ts +17 -0
- package/src/server/users/service.ts +644 -0
- package/src/server.ts +55 -0
- package/src/styles/shadcn.css +653 -0
- package/src/styles/theme.css +76 -0
- package/src/styles.css +177 -0
- package/wrangler.template.jsonc +37 -0
|
@@ -0,0 +1,1064 @@
|
|
|
1
|
+
import { ImageUploadControls } from '#devpush/components/storage-capabilities'
|
|
2
|
+
import { CopyButton } from '#devpush/components/copy-button'
|
|
3
|
+
import { ProjectBackupsPending } from './project-backups-pending'
|
|
4
|
+
import { ProjectBackupDetailsPending } from './project-backup-details-pending'
|
|
5
|
+
import { ProjectLogs } from './project-logs'
|
|
6
|
+
import { DeploymentsPage } from '#devpush/features/deployments/deployments-page'
|
|
7
|
+
import { createDeferredModule } from '#devpush/lib/deferred-module'
|
|
8
|
+
import type { DeferredModule } from '#devpush/lib/deferred-module'
|
|
9
|
+
import {
|
|
10
|
+
DeploymentDetail,
|
|
11
|
+
DeploymentMetadata,
|
|
12
|
+
DeploymentLogsPending,
|
|
13
|
+
} from '#devpush/features/deployments/deployment-detail'
|
|
14
|
+
import { stringSearchValidator } from '#devpush/lib/search'
|
|
15
|
+
import type {
|
|
16
|
+
ProjectContext,
|
|
17
|
+
PluginContext,
|
|
18
|
+
} from '#devpush/server/projects/types'
|
|
19
|
+
import { useParams, useRouterState, useHydrated } from '@tanstack/react-router'
|
|
20
|
+
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
21
|
+
import type { ReactNode } from 'react'
|
|
22
|
+
|
|
23
|
+
import { TeamPage } from '#devpush/features/teams/team-header'
|
|
24
|
+
import { getCachedTeamContexts } from '#devpush/lib/team-context-query'
|
|
25
|
+
import {
|
|
26
|
+
projectContextQueryOptions,
|
|
27
|
+
projectShellQueryOptions,
|
|
28
|
+
projectLogsQueryOptions,
|
|
29
|
+
} from '#devpush/lib/project-context-query'
|
|
30
|
+
import { ProjectOverview } from '#devpush/features/projects/project-overview'
|
|
31
|
+
import { DeploymentListPending } from '#devpush/features/deployments/deployment-list'
|
|
32
|
+
import { DeploymentVisitLinks } from '#devpush/features/deployments/deployment-visit-links'
|
|
33
|
+
import type { ProjectPendingSection } from '#devpush/features/projects/pending-section'
|
|
34
|
+
import { Skeleton } from '#devpush/components/ui/skeleton'
|
|
35
|
+
import { Button } from '#devpush/components/ui/button'
|
|
36
|
+
import { Input } from '#devpush/components/ui/input'
|
|
37
|
+
import { Textarea } from '#devpush/components/ui/textarea'
|
|
38
|
+
import { ResourceAvatar } from '#devpush/components/resource-avatar'
|
|
39
|
+
import {
|
|
40
|
+
Select,
|
|
41
|
+
SelectTrigger,
|
|
42
|
+
SelectValue,
|
|
43
|
+
} from '#devpush/components/ui/select'
|
|
44
|
+
import {
|
|
45
|
+
InputGroup,
|
|
46
|
+
InputGroupAddon,
|
|
47
|
+
InputGroupInput,
|
|
48
|
+
} from '#devpush/components/ui/input-group'
|
|
49
|
+
import {
|
|
50
|
+
CircleHelpIcon,
|
|
51
|
+
Clock3Icon,
|
|
52
|
+
LoaderIcon,
|
|
53
|
+
ServerIcon,
|
|
54
|
+
GitBranchIcon,
|
|
55
|
+
GitCommitHorizontalIcon,
|
|
56
|
+
ArrowRightIcon,
|
|
57
|
+
ArrowUpRightIcon,
|
|
58
|
+
MoreHorizontalIcon,
|
|
59
|
+
ChevronsUpDownIcon,
|
|
60
|
+
SettingsIcon,
|
|
61
|
+
PlusIcon,
|
|
62
|
+
RefreshCwIcon,
|
|
63
|
+
SearchIcon,
|
|
64
|
+
Trash2Icon,
|
|
65
|
+
UploadIcon,
|
|
66
|
+
} from 'lucide-react'
|
|
67
|
+
import {
|
|
68
|
+
Field,
|
|
69
|
+
FieldLabel,
|
|
70
|
+
FieldDescription,
|
|
71
|
+
FieldGroup,
|
|
72
|
+
} from '#devpush/components/ui/field'
|
|
73
|
+
import {
|
|
74
|
+
SettingsLayout,
|
|
75
|
+
SettingsSection,
|
|
76
|
+
} from '#devpush/components/settings-layout'
|
|
77
|
+
import { projectSettingsNavigationItems } from './project-settings-utils'
|
|
78
|
+
import { ProjectHeading } from './project-heading'
|
|
79
|
+
|
|
80
|
+
type PendingProjectContext = {
|
|
81
|
+
avatarUrl: string | null
|
|
82
|
+
color?:
|
|
83
|
+
| 'amber'
|
|
84
|
+
| 'blue'
|
|
85
|
+
| 'green'
|
|
86
|
+
| 'neutral'
|
|
87
|
+
| 'rose'
|
|
88
|
+
| 'lime'
|
|
89
|
+
| 'sky'
|
|
90
|
+
| 'zinc'
|
|
91
|
+
deployments?: ProjectContext['deployments']
|
|
92
|
+
environments?: ProjectContext['environments']
|
|
93
|
+
projectId?: string
|
|
94
|
+
githubRepo?: string
|
|
95
|
+
deploymentId: string | null
|
|
96
|
+
projectName: string
|
|
97
|
+
teamSlug: string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function ProjectRoutePending({
|
|
101
|
+
section,
|
|
102
|
+
}: {
|
|
103
|
+
section: ProjectPendingSection
|
|
104
|
+
}) {
|
|
105
|
+
return (
|
|
106
|
+
<ProjectPendingPage>
|
|
107
|
+
<ProjectSectionPending section={section} />
|
|
108
|
+
</ProjectPendingPage>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function ProjectSectionPending({
|
|
113
|
+
section,
|
|
114
|
+
}: {
|
|
115
|
+
section: ProjectPendingSection
|
|
116
|
+
}) {
|
|
117
|
+
const params = useParams({ strict: false })
|
|
118
|
+
const hydrated = useHydrated()
|
|
119
|
+
const queryClient = useQueryClient()
|
|
120
|
+
const projectName =
|
|
121
|
+
typeof params.projectName === 'string' ? params.projectName : 'Project'
|
|
122
|
+
const teamSlug =
|
|
123
|
+
typeof params.teamSlug === 'string' ? params.teamSlug : 'team'
|
|
124
|
+
const cachedProject = (
|
|
125
|
+
hydrated ? getCachedTeamContexts(queryClient, teamSlug) : []
|
|
126
|
+
)
|
|
127
|
+
.flatMap((context) => context.projects)
|
|
128
|
+
.find((project) => project.name === projectName)
|
|
129
|
+
const { data: shellResult } = useQuery({
|
|
130
|
+
...projectShellQueryOptions({ projectName, teamSlug }),
|
|
131
|
+
enabled: false,
|
|
132
|
+
})
|
|
133
|
+
const { data: overviewResult } = useQuery({
|
|
134
|
+
...projectContextQueryOptions(
|
|
135
|
+
{ projectName, teamSlug, scope: 'overview' },
|
|
136
|
+
queryClient,
|
|
137
|
+
),
|
|
138
|
+
enabled: false,
|
|
139
|
+
})
|
|
140
|
+
const cachedShell = hydrated ? shellResult : undefined
|
|
141
|
+
const cachedOverview = hydrated ? overviewResult : undefined
|
|
142
|
+
const pendingContext: PendingProjectContext = {
|
|
143
|
+
deployments: (hydrated
|
|
144
|
+
? queryClient.getQueriesData<ProjectContext>({
|
|
145
|
+
queryKey: ['project-context', teamSlug, projectName],
|
|
146
|
+
})
|
|
147
|
+
: []
|
|
148
|
+
)
|
|
149
|
+
.sort(
|
|
150
|
+
([first], [second]) =>
|
|
151
|
+
(queryClient.getQueryState(second)?.dataUpdatedAt ?? 0) -
|
|
152
|
+
(queryClient.getQueryState(first)?.dataUpdatedAt ?? 0),
|
|
153
|
+
)
|
|
154
|
+
.flatMap(([, value]) => value?.deployments ?? [])
|
|
155
|
+
.filter(
|
|
156
|
+
(item, index, items) =>
|
|
157
|
+
items.findIndex((other) => other.id === item.id) === index,
|
|
158
|
+
),
|
|
159
|
+
environments: cachedShell?.environments,
|
|
160
|
+
projectId: cachedShell?.project.id ?? cachedProject?.id,
|
|
161
|
+
githubRepo: cachedShell?.project.githubRepo ?? cachedProject?.githubRepo,
|
|
162
|
+
avatarUrl:
|
|
163
|
+
cachedShell?.project.avatarUrl ?? cachedProject?.avatarUrl ?? null,
|
|
164
|
+
color: cachedShell?.project.color ?? cachedProject?.color,
|
|
165
|
+
deploymentId:
|
|
166
|
+
typeof params.deploymentId === 'string' ? params.deploymentId : null,
|
|
167
|
+
projectName,
|
|
168
|
+
teamSlug,
|
|
169
|
+
}
|
|
170
|
+
const search = useRouterState({ select: (state) => state.location.search })
|
|
171
|
+
const { data: logsResult } = useQuery({
|
|
172
|
+
...projectLogsQueryOptions(
|
|
173
|
+
{
|
|
174
|
+
projectName,
|
|
175
|
+
teamSlug,
|
|
176
|
+
...stringSearchValidator([
|
|
177
|
+
'branch',
|
|
178
|
+
'date',
|
|
179
|
+
'deploymentId',
|
|
180
|
+
'environmentId',
|
|
181
|
+
'keyword',
|
|
182
|
+
] as const)(search),
|
|
183
|
+
},
|
|
184
|
+
queryClient,
|
|
185
|
+
),
|
|
186
|
+
enabled: false,
|
|
187
|
+
})
|
|
188
|
+
const cachedLogs = hydrated ? logsResult : undefined
|
|
189
|
+
if (section === 'logs' && cachedLogs)
|
|
190
|
+
return <ProjectLogs context={cachedLogs} />
|
|
191
|
+
const scope = section === 'backup' ? 'backups' : section
|
|
192
|
+
const sectionResult = queryClient.getQueryData<ProjectContext>([
|
|
193
|
+
'project-context',
|
|
194
|
+
teamSlug,
|
|
195
|
+
projectName,
|
|
196
|
+
scope,
|
|
197
|
+
])
|
|
198
|
+
const cached = hydrated ? sectionResult : undefined
|
|
199
|
+
if (cached && section === 'settings')
|
|
200
|
+
return (
|
|
201
|
+
<DeferredPage
|
|
202
|
+
module={projectSettingsModule}
|
|
203
|
+
fallback={<ProjectSettingsPending context={pendingContext} />}
|
|
204
|
+
>
|
|
205
|
+
{(loaded) => <loaded.ProjectSettings context={cached} />}
|
|
206
|
+
</DeferredPage>
|
|
207
|
+
)
|
|
208
|
+
if (cached && section === 'deployments')
|
|
209
|
+
return (
|
|
210
|
+
<DeploymentsPage
|
|
211
|
+
context={cached}
|
|
212
|
+
filters={stringSearchValidator([
|
|
213
|
+
'branch', 'date', 'environment', 'status',
|
|
214
|
+
] as const)(search)}
|
|
215
|
+
/>
|
|
216
|
+
)
|
|
217
|
+
if (cached && section === 'backups')
|
|
218
|
+
return (
|
|
219
|
+
<DeferredPage
|
|
220
|
+
module={projectBackupsModule}
|
|
221
|
+
fallback={
|
|
222
|
+
<ProjectBackupsPending
|
|
223
|
+
heading={<ProjectHeadingPending context={pendingContext} crumbs={['Backups']} />}
|
|
224
|
+
canManage={cachedShell ? cachedShell.team.role !== 'member' : undefined}
|
|
225
|
+
/>
|
|
226
|
+
}
|
|
227
|
+
>
|
|
228
|
+
{(loaded) => (
|
|
229
|
+
<loaded.ProjectBackups
|
|
230
|
+
context={cached}
|
|
231
|
+
filters={stringSearchValidator([
|
|
232
|
+
'environment', 'server', 'status', 'time',
|
|
233
|
+
] as const)(search)}
|
|
234
|
+
/>
|
|
235
|
+
)}
|
|
236
|
+
</DeferredPage>
|
|
237
|
+
)
|
|
238
|
+
if (cached && section === 'backup' && typeof params.backupId === 'string')
|
|
239
|
+
return (
|
|
240
|
+
<DeferredPage
|
|
241
|
+
module={projectBackupDetailsModule}
|
|
242
|
+
fallback={
|
|
243
|
+
<BackupDetailPending
|
|
244
|
+
context={pendingContext}
|
|
245
|
+
canManage={cachedShell ? cachedShell.team.role !== 'member' : undefined}
|
|
246
|
+
/>
|
|
247
|
+
}
|
|
248
|
+
>
|
|
249
|
+
{(loaded) => (
|
|
250
|
+
<loaded.ProjectBackupDetails context={cached} backupId={String(params.backupId)} />
|
|
251
|
+
)}
|
|
252
|
+
</DeferredPage>
|
|
253
|
+
)
|
|
254
|
+
const deploymentResult = queryClient.getQueryData<PluginContext>([
|
|
255
|
+
'deployment-context',
|
|
256
|
+
teamSlug,
|
|
257
|
+
projectName,
|
|
258
|
+
params.deploymentId,
|
|
259
|
+
])
|
|
260
|
+
const deployment = hydrated ? deploymentResult : undefined
|
|
261
|
+
if (section === 'deployment' && deployment)
|
|
262
|
+
return <DeploymentDetail context={deployment} />
|
|
263
|
+
let content
|
|
264
|
+
|
|
265
|
+
if (section === 'settings') {
|
|
266
|
+
content = <ProjectSettingsPending context={pendingContext} />
|
|
267
|
+
} else if (section === 'deployment') {
|
|
268
|
+
content = <DeploymentDetailPending context={pendingContext} />
|
|
269
|
+
} else if (section === 'backup') {
|
|
270
|
+
content = (
|
|
271
|
+
<BackupDetailPending
|
|
272
|
+
context={pendingContext}
|
|
273
|
+
canManage={cachedShell ? cachedShell.team.role !== 'member' : undefined}
|
|
274
|
+
/>
|
|
275
|
+
)
|
|
276
|
+
} else if (section === 'logs') {
|
|
277
|
+
content = <ProjectLogsPending context={pendingContext} />
|
|
278
|
+
} else if (section === 'deployments') {
|
|
279
|
+
content = <DeploymentsPending context={pendingContext} />
|
|
280
|
+
} else if (section === 'backups') {
|
|
281
|
+
content = (
|
|
282
|
+
<ProjectBackupsPending
|
|
283
|
+
heading={
|
|
284
|
+
<ProjectHeadingPending
|
|
285
|
+
context={pendingContext}
|
|
286
|
+
crumbs={['Backups']}
|
|
287
|
+
/>
|
|
288
|
+
}
|
|
289
|
+
canManage={cachedShell ? cachedShell.team.role !== 'member' : undefined}
|
|
290
|
+
/>
|
|
291
|
+
)
|
|
292
|
+
} else {
|
|
293
|
+
content =
|
|
294
|
+
cachedOverview || cachedShell ? (
|
|
295
|
+
<ProjectOverview
|
|
296
|
+
context={cachedOverview ?? cachedShell!}
|
|
297
|
+
deploymentsPending={!cachedOverview}
|
|
298
|
+
/>
|
|
299
|
+
) : (
|
|
300
|
+
<ProjectOverviewPending context={pendingContext} />
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return content
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const projectSettingsModule = createDeferredModule(() => import('./project-settings'))
|
|
308
|
+
const projectBackupsModule = createDeferredModule(() => import('./project-backups'))
|
|
309
|
+
const projectBackupDetailsModule = createDeferredModule(() => import('./project-backup-details'))
|
|
310
|
+
|
|
311
|
+
export function preloadProjectPendingPage(section: ProjectPendingSection) {
|
|
312
|
+
if (section === 'settings') void projectSettingsModule.load()
|
|
313
|
+
else if (section === 'backups') void projectBackupsModule.load()
|
|
314
|
+
else if (section === 'backup') void projectBackupDetailsModule.load()
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function DeferredPage<T>({ module, fallback, children }: {
|
|
318
|
+
module: DeferredModule<T>
|
|
319
|
+
fallback: ReactNode
|
|
320
|
+
children: (loaded: T) => ReactNode
|
|
321
|
+
}) {
|
|
322
|
+
const loaded = module.useModule(true)
|
|
323
|
+
return loaded ? children(loaded) : fallback
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function ProjectPendingPage({ children }: { children: ReactNode }) {
|
|
327
|
+
return (
|
|
328
|
+
<TeamPage contentClassName="space-y-8" projectNavigation pending>
|
|
329
|
+
{children}
|
|
330
|
+
</TeamPage>
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function ProjectOverviewPending({
|
|
335
|
+
context,
|
|
336
|
+
}: {
|
|
337
|
+
context: PendingProjectContext
|
|
338
|
+
}) {
|
|
339
|
+
return (
|
|
340
|
+
<div className="space-y-8" aria-label="Loading project overview">
|
|
341
|
+
<ProjectHeadingPending context={context} />
|
|
342
|
+
|
|
343
|
+
<section className="space-y-4">
|
|
344
|
+
<SectionHeadingPending action label="Environments" />
|
|
345
|
+
<div className="grid gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
|
|
346
|
+
{Array.from({ length: 1 }, (_, index) => (
|
|
347
|
+
<div
|
|
348
|
+
className="rounded-xl border bg-card p-4 text-sm text-card-foreground"
|
|
349
|
+
key={index}
|
|
350
|
+
>
|
|
351
|
+
<Skeleton className="mb-4 h-4 w-28" />
|
|
352
|
+
<div className="space-y-1">
|
|
353
|
+
<Skeleton className="h-5 w-4/5" />
|
|
354
|
+
<Skeleton className="h-5 w-24" />
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
))}
|
|
358
|
+
</div>
|
|
359
|
+
</section>
|
|
360
|
+
|
|
361
|
+
<section className="space-y-4">
|
|
362
|
+
<SectionHeadingPending label="Latest deployments" />
|
|
363
|
+
<DeploymentListPending label="Latest deployments loading" />
|
|
364
|
+
</section>
|
|
365
|
+
</div>
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function DeploymentsPending({ context }: { context: PendingProjectContext }) {
|
|
370
|
+
return (
|
|
371
|
+
<div className="space-y-8" aria-label="Loading deployments">
|
|
372
|
+
<ProjectHeadingPending context={context} crumbs={['Deployments']} />
|
|
373
|
+
<section className="space-y-4">
|
|
374
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
375
|
+
<PendingFilter
|
|
376
|
+
name="environment"
|
|
377
|
+
label="All environments"
|
|
378
|
+
unknownSelection
|
|
379
|
+
/>
|
|
380
|
+
<PendingFilter name="status" label="All statuses" />
|
|
381
|
+
<PendingFilter name="date" label="All dates" />
|
|
382
|
+
<PendingFilter name="branch" label="All branches" />
|
|
383
|
+
</div>
|
|
384
|
+
<DeploymentListPending label="Deployments loading" />
|
|
385
|
+
</section>
|
|
386
|
+
</div>
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function DeploymentDetailPending({
|
|
391
|
+
context,
|
|
392
|
+
}: {
|
|
393
|
+
context: PendingProjectContext
|
|
394
|
+
}) {
|
|
395
|
+
const deployment = context.deployments?.find(
|
|
396
|
+
(item) => item.id === context.deploymentId,
|
|
397
|
+
)
|
|
398
|
+
return (
|
|
399
|
+
<div className="space-y-8" aria-label="Loading deployment">
|
|
400
|
+
<ProjectHeadingPending
|
|
401
|
+
context={context}
|
|
402
|
+
crumbs={[
|
|
403
|
+
'Deployments',
|
|
404
|
+
context.deploymentId?.slice(0, 7) ?? 'Deployment',
|
|
405
|
+
]}
|
|
406
|
+
monoLast
|
|
407
|
+
controls={
|
|
408
|
+
<div className="flex items-center gap-2">
|
|
409
|
+
<DeploymentVisitLinks canVisit={false} visitUrl={null} aliases={[]} />
|
|
410
|
+
<Button
|
|
411
|
+
variant="outline"
|
|
412
|
+
size="icon"
|
|
413
|
+
disabled
|
|
414
|
+
aria-label="Deployment options"
|
|
415
|
+
>
|
|
416
|
+
<MoreHorizontalIcon />
|
|
417
|
+
</Button>
|
|
418
|
+
</div>
|
|
419
|
+
}
|
|
420
|
+
/>
|
|
421
|
+
|
|
422
|
+
{deployment ? (
|
|
423
|
+
<DeploymentMetadata
|
|
424
|
+
deployment={deployment}
|
|
425
|
+
deployments={context.deployments ?? []}
|
|
426
|
+
teamSlug={context.teamSlug}
|
|
427
|
+
projectName={context.projectName}
|
|
428
|
+
/>
|
|
429
|
+
) : (
|
|
430
|
+
<section className="space-y-4 text-sm">
|
|
431
|
+
<div className="flex min-h-5 flex-wrap items-center gap-x-8 gap-y-2">
|
|
432
|
+
<div className="w-28 shrink-0">
|
|
433
|
+
<MetadataItemPending icon={<CircleHelpIcon />} textWidth="w-20" />
|
|
434
|
+
</div>
|
|
435
|
+
<div className="min-w-16">
|
|
436
|
+
<MetadataItemPending icon={<Clock3Icon />} textWidth="w-8" />
|
|
437
|
+
</div>
|
|
438
|
+
<div className="h-5 min-w-20" />
|
|
439
|
+
</div>
|
|
440
|
+
<div className="flex min-h-5 flex-wrap items-center gap-x-8 gap-y-2">
|
|
441
|
+
<MetadataItemPending
|
|
442
|
+
icon={<Skeleton className="size-2.5 shrink-0 rounded-full" />}
|
|
443
|
+
textWidth="w-20"
|
|
444
|
+
/>
|
|
445
|
+
<MetadataItemPending icon={<ServerIcon />} textWidth="w-36" />
|
|
446
|
+
<MetadataItemPending icon={<GitBranchIcon />} textWidth="w-10" />
|
|
447
|
+
<MetadataItemPending
|
|
448
|
+
icon={<GitCommitHorizontalIcon />}
|
|
449
|
+
iconSize={20}
|
|
450
|
+
textWidth="w-40"
|
|
451
|
+
/>
|
|
452
|
+
</div>
|
|
453
|
+
</section>
|
|
454
|
+
)}
|
|
455
|
+
<section className="space-y-4">
|
|
456
|
+
<DeploymentLogAreaPending />
|
|
457
|
+
<div className="flex min-h-8 items-start justify-end">
|
|
458
|
+
<Button variant="outline" size="sm" disabled>
|
|
459
|
+
All logs
|
|
460
|
+
<ArrowRightIcon data-icon="inline-end" />
|
|
461
|
+
</Button>
|
|
462
|
+
</div>
|
|
463
|
+
</section>
|
|
464
|
+
</div>
|
|
465
|
+
)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function MetadataItemPending({
|
|
469
|
+
icon,
|
|
470
|
+
iconSize = 16,
|
|
471
|
+
textWidth,
|
|
472
|
+
}: {
|
|
473
|
+
icon: ReactNode
|
|
474
|
+
iconSize?: 16 | 20
|
|
475
|
+
textWidth: string
|
|
476
|
+
}) {
|
|
477
|
+
return (
|
|
478
|
+
<div
|
|
479
|
+
className={`flex h-5 items-center gap-x-2 text-muted-foreground [&>svg]:shrink-0 [&>svg]:opacity-50 ${iconSize === 20 ? '[&>svg]:size-5' : '[&>svg]:size-4'}`}
|
|
480
|
+
>
|
|
481
|
+
{icon}
|
|
482
|
+
<Skeleton className={`h-4 ${textWidth}`} />
|
|
483
|
+
</div>
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
function DeploymentLogAreaPending() {
|
|
487
|
+
return (
|
|
488
|
+
<div className="relative h-[500px] rounded-lg border p-2" aria-busy="true">
|
|
489
|
+
<DeploymentLogsPending />
|
|
490
|
+
</div>
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function ProjectLogsPending({ context }: { context: PendingProjectContext }) {
|
|
495
|
+
const keyword = useRouterState({
|
|
496
|
+
select: (state) => state.location.search.keyword,
|
|
497
|
+
})
|
|
498
|
+
return (
|
|
499
|
+
<div className="space-y-8" aria-label="Loading project logs">
|
|
500
|
+
<ProjectHeadingPending context={context} crumbs={['Logs']} />
|
|
501
|
+
<section>
|
|
502
|
+
<div className="space-y-4">
|
|
503
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
504
|
+
<InputGroup className="w-48">
|
|
505
|
+
<InputGroupAddon>
|
|
506
|
+
<SearchIcon />
|
|
507
|
+
</InputGroupAddon>
|
|
508
|
+
<InputGroupInput
|
|
509
|
+
aria-label="Find in logs"
|
|
510
|
+
placeholder="Find in logs"
|
|
511
|
+
value={typeof keyword === 'string' ? keyword : ''}
|
|
512
|
+
readOnly
|
|
513
|
+
disabled
|
|
514
|
+
/>
|
|
515
|
+
</InputGroup>
|
|
516
|
+
<PendingFilter
|
|
517
|
+
name="deploymentId"
|
|
518
|
+
label="All deployments"
|
|
519
|
+
unknownSelection
|
|
520
|
+
/>
|
|
521
|
+
<PendingFilter
|
|
522
|
+
name="environmentId"
|
|
523
|
+
label="All environments"
|
|
524
|
+
unknownSelection
|
|
525
|
+
/>
|
|
526
|
+
<PendingFilter name="date" label="All dates" />
|
|
527
|
+
<PendingFilter name="branch" label="All branches" />
|
|
528
|
+
<Button
|
|
529
|
+
className="ml-auto"
|
|
530
|
+
variant="ghost"
|
|
531
|
+
size="icon"
|
|
532
|
+
disabled
|
|
533
|
+
aria-label="Refresh"
|
|
534
|
+
>
|
|
535
|
+
<RefreshCwIcon />
|
|
536
|
+
</Button>
|
|
537
|
+
</div>
|
|
538
|
+
<LogAreaPending />
|
|
539
|
+
</div>
|
|
540
|
+
</section>
|
|
541
|
+
</div>
|
|
542
|
+
)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function ProjectSettingsPending({
|
|
546
|
+
context,
|
|
547
|
+
}: {
|
|
548
|
+
context: PendingProjectContext
|
|
549
|
+
}) {
|
|
550
|
+
return (
|
|
551
|
+
<div className="space-y-8" aria-label="Loading project settings">
|
|
552
|
+
<ProjectHeadingPending context={context} crumbs={['Project settings']} />
|
|
553
|
+
<SettingsLayout navigation={projectSettingsNavigationItems}>
|
|
554
|
+
<ProjectGeneralPending context={context} />
|
|
555
|
+
<BuildDeployPending />
|
|
556
|
+
<EnvironmentSettingsPending context={context} />
|
|
557
|
+
<EnvironmentVariablesPending />
|
|
558
|
+
<ServerSettingsPending context={context} />
|
|
559
|
+
<RouteSettingsPending />
|
|
560
|
+
<DomainSettingsPending />
|
|
561
|
+
<DangerSettingsPending />
|
|
562
|
+
</SettingsLayout>
|
|
563
|
+
</div>
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function ProjectGeneralPending({
|
|
568
|
+
context,
|
|
569
|
+
}: {
|
|
570
|
+
context: PendingProjectContext
|
|
571
|
+
}) {
|
|
572
|
+
return (
|
|
573
|
+
<SettingsSection
|
|
574
|
+
id="general"
|
|
575
|
+
title="General"
|
|
576
|
+
description="General settings for the project including the Git repository."
|
|
577
|
+
footer={<SettingsFooterPending />}
|
|
578
|
+
>
|
|
579
|
+
<div className="space-y-8">
|
|
580
|
+
<Field>
|
|
581
|
+
<FieldLabel>Avatar</FieldLabel>
|
|
582
|
+
<div className="flex items-center gap-x-2">
|
|
583
|
+
<ResourceAvatar
|
|
584
|
+
name={context.projectName}
|
|
585
|
+
src={context.avatarUrl ?? undefined}
|
|
586
|
+
color={context.color}
|
|
587
|
+
size="md"
|
|
588
|
+
/>
|
|
589
|
+
<ImageUploadControls>
|
|
590
|
+
<Button variant="outline" size="sm" disabled>
|
|
591
|
+
<UploadIcon data-icon="inline-start" />
|
|
592
|
+
Upload image
|
|
593
|
+
</Button>
|
|
594
|
+
</ImageUploadControls>
|
|
595
|
+
{context.avatarUrl ? (
|
|
596
|
+
<Button
|
|
597
|
+
variant="destructive"
|
|
598
|
+
size="icon-sm"
|
|
599
|
+
disabled
|
|
600
|
+
aria-label="Remove image"
|
|
601
|
+
>
|
|
602
|
+
<Trash2Icon />
|
|
603
|
+
</Button>
|
|
604
|
+
) : null}
|
|
605
|
+
</div>
|
|
606
|
+
</Field>
|
|
607
|
+
<Field>
|
|
608
|
+
<FieldLabel>Project name</FieldLabel>
|
|
609
|
+
<Input value={context.projectName} readOnly disabled />
|
|
610
|
+
</Field>
|
|
611
|
+
<Field>
|
|
612
|
+
<FieldLabel>Git repository</FieldLabel>
|
|
613
|
+
<div className="flex items-center">
|
|
614
|
+
<Button
|
|
615
|
+
variant="outline"
|
|
616
|
+
disabled
|
|
617
|
+
className="rounded-r-none border-r-0 font-normal"
|
|
618
|
+
>
|
|
619
|
+
{context.githubRepo ?? <Skeleton className="h-4 w-40" />}
|
|
620
|
+
<ChevronsUpDownIcon data-icon="inline-end" />
|
|
621
|
+
</Button>
|
|
622
|
+
<Button
|
|
623
|
+
variant="outline"
|
|
624
|
+
size="icon"
|
|
625
|
+
className="rounded-l-none"
|
|
626
|
+
disabled
|
|
627
|
+
aria-label="View on GitHub"
|
|
628
|
+
>
|
|
629
|
+
<ArrowUpRightIcon />
|
|
630
|
+
</Button>
|
|
631
|
+
</div>
|
|
632
|
+
</Field>
|
|
633
|
+
<Field>
|
|
634
|
+
<FieldLabel>Project ID</FieldLabel>
|
|
635
|
+
<InputGroup>
|
|
636
|
+
<InputGroupInput
|
|
637
|
+
className="font-mono text-[13px] text-muted-foreground"
|
|
638
|
+
value={context.projectId ?? ''}
|
|
639
|
+
readOnly
|
|
640
|
+
disabled={!context.projectId}
|
|
641
|
+
/>
|
|
642
|
+
<InputGroupAddon align="inline-end">
|
|
643
|
+
<CopyButton
|
|
644
|
+
value={context.projectId ?? ''}
|
|
645
|
+
label="Copy project ID"
|
|
646
|
+
inputGroup
|
|
647
|
+
disabled={!context.projectId}
|
|
648
|
+
/>
|
|
649
|
+
</InputGroupAddon>
|
|
650
|
+
</InputGroup>
|
|
651
|
+
<FieldDescription>
|
|
652
|
+
A unique identifier used in API, environment variables, etc.
|
|
653
|
+
</FieldDescription>
|
|
654
|
+
</Field>
|
|
655
|
+
</div>
|
|
656
|
+
</SettingsSection>
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function BuildDeployPending() {
|
|
661
|
+
return (
|
|
662
|
+
<SettingsSection
|
|
663
|
+
id="build-and-deploy"
|
|
664
|
+
title="Build & Deploy"
|
|
665
|
+
description="Configure the Compose file, deployment commands, and health checks."
|
|
666
|
+
footer={<SettingsFooterPending />}
|
|
667
|
+
>
|
|
668
|
+
<FieldGroup>
|
|
669
|
+
<SettingsFieldPending
|
|
670
|
+
label="Compose file path"
|
|
671
|
+
description="Repository-relative path to the Compose file. Defaults to compose.yml."
|
|
672
|
+
/>
|
|
673
|
+
<div className="grid gap-4 md:grid-cols-[minmax(0,2fr)_minmax(14rem,1fr)]">
|
|
674
|
+
<SettingsFieldPending
|
|
675
|
+
label="Pre-deploy command"
|
|
676
|
+
multiline
|
|
677
|
+
description={
|
|
678
|
+
<>
|
|
679
|
+
Runs once before services are updated. For example,{' '}
|
|
680
|
+
<code>php artisan migrate --force</code>. Leave empty to skip.
|
|
681
|
+
</>
|
|
682
|
+
}
|
|
683
|
+
/>
|
|
684
|
+
<SettingsFieldPending
|
|
685
|
+
label="Pre-deploy service"
|
|
686
|
+
description="Service to run the pre-deploy command."
|
|
687
|
+
/>
|
|
688
|
+
</div>
|
|
689
|
+
<SettingsFieldPending
|
|
690
|
+
label="Deploy command"
|
|
691
|
+
multiline
|
|
692
|
+
placeholder="docker compose up -d --no-build"
|
|
693
|
+
description={
|
|
694
|
+
<>
|
|
695
|
+
Runs on the server to update services after images and the
|
|
696
|
+
pre-deploy command are ready. When empty, uses{' '}
|
|
697
|
+
<code>docker compose up -d --no-build</code>.
|
|
698
|
+
</>
|
|
699
|
+
}
|
|
700
|
+
/>
|
|
701
|
+
<SettingsFieldPending
|
|
702
|
+
label="Rollback command"
|
|
703
|
+
multiline
|
|
704
|
+
placeholder="docker compose up -d --no-build"
|
|
705
|
+
description="Runs on the server when deploying a previous revision. When empty, uses the deploy command."
|
|
706
|
+
/>
|
|
707
|
+
<SettingsFieldPending
|
|
708
|
+
label="Command timeout"
|
|
709
|
+
description="Maximum runtime for either command: 60–7,200 seconds."
|
|
710
|
+
/>
|
|
711
|
+
<SettingsFieldPending
|
|
712
|
+
label="Health check path"
|
|
713
|
+
description="Path requested after deployment to confirm the service is ready."
|
|
714
|
+
/>
|
|
715
|
+
</FieldGroup>
|
|
716
|
+
</SettingsSection>
|
|
717
|
+
)
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function SettingsFieldPending({
|
|
721
|
+
multiline = false,
|
|
722
|
+
placeholder,
|
|
723
|
+
description,
|
|
724
|
+
label,
|
|
725
|
+
}: {
|
|
726
|
+
multiline?: boolean
|
|
727
|
+
placeholder?: string
|
|
728
|
+
description?: ReactNode
|
|
729
|
+
label: string
|
|
730
|
+
}) {
|
|
731
|
+
return (
|
|
732
|
+
<Field>
|
|
733
|
+
<FieldLabel>{label}</FieldLabel>
|
|
734
|
+
{multiline ? (
|
|
735
|
+
<Textarea
|
|
736
|
+
aria-label={label}
|
|
737
|
+
disabled
|
|
738
|
+
rows={1}
|
|
739
|
+
className="min-h-8 resize-none overflow-hidden py-1.5 font-mono"
|
|
740
|
+
placeholder={placeholder}
|
|
741
|
+
/>
|
|
742
|
+
) : (
|
|
743
|
+
<Skeleton className="h-8 w-full" />
|
|
744
|
+
)}
|
|
745
|
+
{description ? <FieldDescription>{description}</FieldDescription> : null}
|
|
746
|
+
</Field>
|
|
747
|
+
)
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function SettingsFooterPending() {
|
|
751
|
+
return (
|
|
752
|
+
<>
|
|
753
|
+
<Button variant="ghost" disabled>
|
|
754
|
+
Cancel
|
|
755
|
+
</Button>
|
|
756
|
+
<Button disabled>Save</Button>
|
|
757
|
+
</>
|
|
758
|
+
)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
function EnvironmentSettingsPending({
|
|
762
|
+
context,
|
|
763
|
+
}: {
|
|
764
|
+
context: PendingProjectContext
|
|
765
|
+
}) {
|
|
766
|
+
return (
|
|
767
|
+
<SettingsTablePending
|
|
768
|
+
title="Environments"
|
|
769
|
+
description="Deployments will be associated with the first environment with a matching branch pattern."
|
|
770
|
+
columns="grid-cols-[repeat(3,minmax(0,1fr))_2rem]"
|
|
771
|
+
headers={['Name', 'Identifier', 'Branch', '']}
|
|
772
|
+
rows={context.environments?.length ?? 1}
|
|
773
|
+
values={context.environments?.map((environment) => [
|
|
774
|
+
environment.name,
|
|
775
|
+
environment.slug,
|
|
776
|
+
environment.branch,
|
|
777
|
+
null,
|
|
778
|
+
])}
|
|
779
|
+
action="Add environment"
|
|
780
|
+
/>
|
|
781
|
+
)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function EnvironmentVariablesPending() {
|
|
785
|
+
return (
|
|
786
|
+
<SettingsTablePending
|
|
787
|
+
title="Environment variables"
|
|
788
|
+
description="Variables available at build time and runtime. Changes will take effect after a new deployment."
|
|
789
|
+
columns="grid-cols-[200px_minmax(0,1fr)_220px_2rem]"
|
|
790
|
+
headers={['Key', 'Value', 'Environment', '']}
|
|
791
|
+
rows={1}
|
|
792
|
+
action="Add variable"
|
|
793
|
+
/>
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function ServerSettingsPending({
|
|
798
|
+
context,
|
|
799
|
+
}: {
|
|
800
|
+
context: PendingProjectContext
|
|
801
|
+
}) {
|
|
802
|
+
return (
|
|
803
|
+
<SettingsTablePending
|
|
804
|
+
title="Servers"
|
|
805
|
+
description="Choose a default server, then override individual environments when needed."
|
|
806
|
+
columns="grid-cols-[minmax(0,1fr)_minmax(0,1fr)]"
|
|
807
|
+
headers={['Scope', 'Server']}
|
|
808
|
+
rows={1 + (context.environments?.length ?? 1)}
|
|
809
|
+
values={[
|
|
810
|
+
['Default', null],
|
|
811
|
+
...(context.environments?.map((environment) => [
|
|
812
|
+
environment.name,
|
|
813
|
+
null,
|
|
814
|
+
]) ?? [[null, null]]),
|
|
815
|
+
]}
|
|
816
|
+
/>
|
|
817
|
+
)
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function RouteSettingsPending() {
|
|
821
|
+
return (
|
|
822
|
+
<SettingsTablePending
|
|
823
|
+
title="Routes"
|
|
824
|
+
description="Default routes apply to every environment. Environment routes add paths or override the default target for the same path."
|
|
825
|
+
columns="grid-cols-[minmax(8rem,0.8fr)_minmax(0,1.1fr)_1rem_minmax(0,1.1fr)_minmax(5.5rem,0.4fr)_3.5rem_2.5rem]"
|
|
826
|
+
headers={['Scope', 'Path', '', 'Service', 'Port', 'Strip', '']}
|
|
827
|
+
rows={1}
|
|
828
|
+
action="Add route"
|
|
829
|
+
/>
|
|
830
|
+
)
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function DomainSettingsPending() {
|
|
834
|
+
return (
|
|
835
|
+
<SettingsTablePending
|
|
836
|
+
title="Domains"
|
|
837
|
+
description="Custom domain names associated with this project's environments."
|
|
838
|
+
columns="grid-cols-[minmax(0,1.4fr)_minmax(0,1.05fr)_minmax(0,1.05fr)_3.5rem_5.5rem_2.5rem]"
|
|
839
|
+
headers={['Host', 'Environment', 'Base route', 'HTTPS', '', '']}
|
|
840
|
+
rows={1}
|
|
841
|
+
action="Add domain"
|
|
842
|
+
/>
|
|
843
|
+
)
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
function DangerSettingsPending() {
|
|
847
|
+
return (
|
|
848
|
+
<SettingsSection
|
|
849
|
+
id="danger"
|
|
850
|
+
title="Danger"
|
|
851
|
+
description="Permanently delete the project and all of its configuration, environments and deployments."
|
|
852
|
+
>
|
|
853
|
+
<Button variant="destructive" disabled>
|
|
854
|
+
Delete project
|
|
855
|
+
</Button>
|
|
856
|
+
</SettingsSection>
|
|
857
|
+
)
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
function SettingsTablePending({
|
|
861
|
+
action,
|
|
862
|
+
columns,
|
|
863
|
+
description,
|
|
864
|
+
headers,
|
|
865
|
+
rows,
|
|
866
|
+
title,
|
|
867
|
+
values,
|
|
868
|
+
}: {
|
|
869
|
+
action?: string
|
|
870
|
+
columns: string
|
|
871
|
+
description: string
|
|
872
|
+
headers: Array<string>
|
|
873
|
+
rows: number
|
|
874
|
+
values?: Array<Array<string | null>>
|
|
875
|
+
title: string
|
|
876
|
+
}) {
|
|
877
|
+
return (
|
|
878
|
+
<SettingsSection
|
|
879
|
+
id={title.toLowerCase().replaceAll(' ', '-')}
|
|
880
|
+
title={title}
|
|
881
|
+
description={description}
|
|
882
|
+
footer={<SettingsFooterPending />}
|
|
883
|
+
>
|
|
884
|
+
<div
|
|
885
|
+
className={`grid gap-x-2 border-b ${title === 'Routes' || title === 'Domains' ? 'pb-2' : 'pb-3'} text-sm font-medium ${columns}`}
|
|
886
|
+
>
|
|
887
|
+
{headers.map((header, index) => (
|
|
888
|
+
<span key={`${header}:${index}`}>{header}</span>
|
|
889
|
+
))}
|
|
890
|
+
</div>
|
|
891
|
+
{Array.from({ length: rows }, (_, row) => (
|
|
892
|
+
<div
|
|
893
|
+
className={`grid min-h-12 items-center gap-x-2 border-b py-2 ${columns}`}
|
|
894
|
+
key={row}
|
|
895
|
+
>
|
|
896
|
+
{headers.map((header, column) =>
|
|
897
|
+
values?.[row]?.[column] != null ? (
|
|
898
|
+
title === 'Servers' ? (
|
|
899
|
+
<span className="text-sm" key={column}>
|
|
900
|
+
{values[row][column]}
|
|
901
|
+
</span>
|
|
902
|
+
) : (
|
|
903
|
+
<Input
|
|
904
|
+
key={column}
|
|
905
|
+
value={values[row][column] ?? ''}
|
|
906
|
+
readOnly
|
|
907
|
+
disabled
|
|
908
|
+
/>
|
|
909
|
+
)
|
|
910
|
+
) : (
|
|
911
|
+
<Skeleton
|
|
912
|
+
className={
|
|
913
|
+
header === '' ? 'size-8 justify-self-end' : 'h-8 w-full'
|
|
914
|
+
}
|
|
915
|
+
key={column}
|
|
916
|
+
/>
|
|
917
|
+
),
|
|
918
|
+
)}
|
|
919
|
+
</div>
|
|
920
|
+
))}
|
|
921
|
+
{action ? (
|
|
922
|
+
<Button className="mt-3" variant="outline" disabled>
|
|
923
|
+
<PlusIcon data-icon="inline-start" />
|
|
924
|
+
{action}
|
|
925
|
+
</Button>
|
|
926
|
+
) : null}
|
|
927
|
+
</SettingsSection>
|
|
928
|
+
)
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function BackupDetailPending({
|
|
932
|
+
context,
|
|
933
|
+
canManage,
|
|
934
|
+
}: {
|
|
935
|
+
context: PendingProjectContext
|
|
936
|
+
canManage?: boolean
|
|
937
|
+
}) {
|
|
938
|
+
return (
|
|
939
|
+
<ProjectBackupDetailsPending
|
|
940
|
+
canManage={canManage}
|
|
941
|
+
heading={
|
|
942
|
+
<ProjectHeadingPending context={context} crumbs={['Backups', null]} />
|
|
943
|
+
}
|
|
944
|
+
/>
|
|
945
|
+
)
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
function ProjectHeadingPending({
|
|
949
|
+
controls,
|
|
950
|
+
context,
|
|
951
|
+
crumbs = [],
|
|
952
|
+
monoLast = false,
|
|
953
|
+
}: {
|
|
954
|
+
controls?: ReactNode
|
|
955
|
+
context: PendingProjectContext
|
|
956
|
+
crumbs?: Array<string | null>
|
|
957
|
+
monoLast?: boolean
|
|
958
|
+
}) {
|
|
959
|
+
return (
|
|
960
|
+
<ProjectHeading
|
|
961
|
+
teamSlug={context.teamSlug}
|
|
962
|
+
project={{
|
|
963
|
+
avatarUrl: context.avatarUrl,
|
|
964
|
+
color: context.color,
|
|
965
|
+
name: context.projectName,
|
|
966
|
+
}}
|
|
967
|
+
crumbs={crumbs.map((crumb, index) =>
|
|
968
|
+
crumb ? (
|
|
969
|
+
<span
|
|
970
|
+
className={
|
|
971
|
+
monoLast && index === crumbs.length - 1
|
|
972
|
+
? 'truncate font-mono'
|
|
973
|
+
: 'truncate'
|
|
974
|
+
}
|
|
975
|
+
>
|
|
976
|
+
{crumb}
|
|
977
|
+
</span>
|
|
978
|
+
) : (
|
|
979
|
+
<Skeleton className="h-8 w-32" />
|
|
980
|
+
),
|
|
981
|
+
)}
|
|
982
|
+
actions={controls}
|
|
983
|
+
/>
|
|
984
|
+
)
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function SectionHeadingPending({
|
|
988
|
+
action = false,
|
|
989
|
+
label,
|
|
990
|
+
}: {
|
|
991
|
+
action?: boolean
|
|
992
|
+
label: string
|
|
993
|
+
}) {
|
|
994
|
+
return (
|
|
995
|
+
<div className="flex h-8 items-center justify-between gap-4">
|
|
996
|
+
<h2 className="text-base font-medium">{label}</h2>
|
|
997
|
+
{action ? (
|
|
998
|
+
<Button disabled>
|
|
999
|
+
<SettingsIcon data-icon="inline-start" />
|
|
1000
|
+
Manage environments
|
|
1001
|
+
</Button>
|
|
1002
|
+
) : null}
|
|
1003
|
+
</div>
|
|
1004
|
+
)
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function LogAreaPending() {
|
|
1008
|
+
return (
|
|
1009
|
+
<div className="relative h-[500px] rounded-lg border p-2" aria-busy="true">
|
|
1010
|
+
<div
|
|
1011
|
+
role="status"
|
|
1012
|
+
className="flex h-full items-center justify-center gap-2 text-sm text-muted-foreground"
|
|
1013
|
+
>
|
|
1014
|
+
<LoaderIcon
|
|
1015
|
+
className="size-4 shrink-0 animate-spin"
|
|
1016
|
+
aria-hidden="true"
|
|
1017
|
+
/>
|
|
1018
|
+
Loading logs
|
|
1019
|
+
</div>
|
|
1020
|
+
</div>
|
|
1021
|
+
)
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function PendingFilter({
|
|
1025
|
+
name,
|
|
1026
|
+
label,
|
|
1027
|
+
className,
|
|
1028
|
+
unknownSelection = false,
|
|
1029
|
+
}: {
|
|
1030
|
+
name: string
|
|
1031
|
+
label: string
|
|
1032
|
+
className?: string
|
|
1033
|
+
unknownSelection?: boolean
|
|
1034
|
+
}) {
|
|
1035
|
+
const search = useRouterState({ select: (state) => state.location.search })
|
|
1036
|
+
const value = (search as Record<string, unknown>)[name]
|
|
1037
|
+
const selected = typeof value === 'string' && value !== 'all' && value !== ''
|
|
1038
|
+
const dates: Record<string, string> = {
|
|
1039
|
+
today: 'Today',
|
|
1040
|
+
'24h': 'Last 24 hours',
|
|
1041
|
+
'7d': 'Last 7 days',
|
|
1042
|
+
'30d': 'Last 30 days',
|
|
1043
|
+
}
|
|
1044
|
+
const selectedLabel = selected
|
|
1045
|
+
? name === 'date'
|
|
1046
|
+
? (dates[value] ?? label)
|
|
1047
|
+
: name === 'status'
|
|
1048
|
+
? value.charAt(0).toUpperCase() + value.slice(1)
|
|
1049
|
+
: value
|
|
1050
|
+
: label
|
|
1051
|
+
return (
|
|
1052
|
+
<Select disabled value={selected ? value : 'all'}>
|
|
1053
|
+
<SelectTrigger className={className} aria-label={label}>
|
|
1054
|
+
<SelectValue placeholder={label}>
|
|
1055
|
+
{selected && unknownSelection ? (
|
|
1056
|
+
<Skeleton className="h-4 w-24" />
|
|
1057
|
+
) : (
|
|
1058
|
+
selectedLabel
|
|
1059
|
+
)}
|
|
1060
|
+
</SelectValue>
|
|
1061
|
+
</SelectTrigger>
|
|
1062
|
+
</Select>
|
|
1063
|
+
)
|
|
1064
|
+
}
|