From 009cf0ecde336a6cfcd204d2ef9a5ccefb3a3da3 Mon Sep 17 00:00:00 2001 From: awisniew207 Date: Thu, 29 Jan 2026 15:29:37 -0800 Subject: [PATCH 1/7] chore: only apply changes to the app-dashboard --- packages/apps/app-dashboard/.env.example | 2 +- packages/apps/app-dashboard/package.json | 4 +- .../ability/forms/ChangeAbilityOwnerForm.tsx | 2 +- .../ability/forms/CreateAbilityForm.tsx | 2 +- .../forms/CreateAbilityVersionForm.tsx | 2 +- .../ability/forms/EditAbilityForm.tsx | 2 +- .../ability/forms/EditAbilityVersionForm.tsx | 2 +- .../RefreshAbilityVersionPoliciesForm.tsx | 2 +- .../ability/views/AbilitiesListView.tsx | 2 +- .../ability/views/AbilityDetailsView.tsx | 2 +- .../views/AbilityVersionDetailsView.tsx | 2 +- .../ability/views/AbilityVersionsListView.tsx | 2 +- .../wrappers/AbilityOverviewWrapper.tsx | 2 +- .../wrappers/AbilityVersionDetailsWrapper.tsx | 2 +- .../wrappers/ui/AbilityActionButtons.tsx | 2 +- .../ui/AbilityVersionActionButtons.tsx | 2 +- .../wrappers/ui/UndeleteAbilityButton.tsx | 2 +- .../app/forms/CreateAppForm.tsx | 90 ++- .../forms/CreateAppVersionAbilitiesForm.tsx | 66 -- .../app/forms/CreateAppVersionForm.tsx | 99 --- .../app/forms/DeleteAppForm.tsx | 26 +- .../app/forms/DeleteAppVersionForm.tsx | 168 ----- .../app/forms/EditAppForm.tsx | 81 +-- .../app/forms/EditAppVersionAbilityForm.tsx | 76 -- .../app/forms/EditAppVersionForm.tsx | 97 --- .../app/forms/EditPublishedAppForm.tsx | 159 ---- .../app/forms/ManageDelegateesForm.tsx | 151 ++-- .../app/forms/SetActiveVersionForm.tsx | 150 ++++ .../app/views/AppDetailsView.tsx | 61 +- .../app/views/AppVersionAbilitiesDisplay.tsx | 70 -- .../app/views/AppVersionDetailView.tsx | 182 ----- .../app/views/AppVersionDetails.tsx | 68 -- .../app/views/AppVersionsListView.tsx | 226 ------ .../app/views/AppsListView.tsx | 34 +- .../app/views/ManageAppVersionAbilities.tsx | 197 ----- .../app/wrappers/AppOverviewWrapper.tsx | 314 ++++---- .../wrappers/AppVersionAbilitiesWrapper.tsx | 192 ----- .../app/wrappers/AppVersionDetailWrapper.tsx | 216 ------ .../app/wrappers/AppVersionDetailsWrapper.tsx | 427 +++++++++++ .../app/wrappers/AppVersionsWrapper.tsx | 251 +++++-- .../app/wrappers/CreateAppVersionWrapper.tsx | 554 ++++++++++++++ .../app/wrappers/CreateAppWrapper.tsx | 166 ++++- .../app/wrappers/PublishAppVersionWrapper.tsx | 346 --------- .../create-app/CreateAppProgressBar.tsx | 60 ++ .../create-app/CreateAppStepAbilities.tsx | 106 +++ .../create-app/CreateAppStepDelegatees.tsx | 103 +++ .../create-app/CreateAppStepMetadata.tsx | 136 ++++ .../create-app/CreateAppStepReview.tsx | 159 ++++ .../create-app/CreateAppSubmitting.tsx | 60 ++ .../app/wrappers/create-app/index.ts | 8 + .../app/wrappers/create-app/types.ts | 19 + .../create-app/useCreateAppFormState.ts | 405 +++++++++++ .../developer-dashboard/app/wrappers/index.ts | 18 +- .../app/wrappers/ui/AppMismatchResolution.tsx | 173 ----- .../app/wrappers/ui/AppPublishedButtons.tsx | 253 +++---- .../app/wrappers/ui/AppUnpublishedButtons.tsx | 72 -- .../wrappers/ui/AppVersionDeletedButtons.tsx | 16 - .../ui/AppVersionMismatchResolution.tsx | 173 ----- .../ui/AppVersionPublishedButtons.tsx | 226 ------ .../ui/AppVersionUnpublishedButtons.tsx | 74 -- .../ui/EditAppVersionAbilityButton.tsx | 101 --- .../app/wrappers/ui/PackageInstallCommand.tsx | 2 +- .../wrappers/ui/PublishAppVersionButton.tsx | 89 --- .../app/wrappers/ui/UndeleteAppButton.tsx | 60 -- .../ui/UndeleteAppVersionAbilityButton.tsx | 57 -- .../wrappers/ui/UndeleteAppVersionButton.tsx | 64 -- .../developer-dashboard/auth/SignInScreen.tsx | 291 ++++++++ .../form-fields/ImageUploadField.tsx | 2 +- .../form-fields/PolicyCheckboxField.tsx | 2 +- .../policy/forms/ChangePolicyOwnerForm.tsx | 2 +- .../policy/forms/CreatePolicyForm.tsx | 2 +- .../policy/forms/CreatePolicyVersionForm.tsx | 2 +- .../policy/forms/DeletePolicyForm.tsx | 2 +- .../policy/forms/EditPolicyForm.tsx | 2 +- .../policy/forms/EditPolicyVersionForm.tsx | 2 +- .../policy/views/PolicyDetailsView.tsx | 2 +- .../policy/views/PolicyListView.tsx | 2 +- .../policy/views/PolicyVersionDetailsView.tsx | 2 +- .../policy/views/PolicyVersionsListView.tsx | 2 +- .../policy/wrappers/PolicyOverviewWrapper.tsx | 2 +- .../wrappers/PolicyVersionDetailsWrapper.tsx | 2 +- .../wrappers/ui/PolicyActionButtons.tsx | 2 +- .../ui/PolicyVersionActionButtons.tsx | 2 +- .../wrappers/ui/UndeletePolicyButton.tsx | 2 +- .../sidebar/AbilityList.tsx | 216 ------ .../developer-dashboard/sidebar/AppList.tsx | 217 ------ .../sidebar/DeveloperSidebarWrapper.tsx | 5 - .../sidebar/PolicyList.tsx | 218 ------ .../developer-dashboard/sidebar/Sidebar.tsx | 10 +- .../developer-dashboard/ui/AbilityCard.tsx | 2 +- .../{ => ui}/AbilitySelectorModal.tsx | 8 +- .../{ => ui}/AbilityVersionSelectorModal.tsx | 2 +- .../developer-dashboard/ui/ActionButton.tsx | 2 +- .../developer-dashboard/ui/AppCard.tsx | 2 +- .../ui/ConnectPageModal.tsx | 139 ---- .../developer-dashboard/ui/DashboardCard.tsx | 2 +- .../{ => ui}/DashboardContent.tsx | 2 +- .../developer-dashboard/ui/PolicyCard.tsx | 2 +- .../ui/ResourceNotOwnedError.tsx | 2 +- .../developer-dashboard/ui/VersionCard.tsx | 2 +- .../explorer/ui/AbilityInfoView.tsx | 2 +- .../explorer/ui/ActiveAppVersion.tsx | 68 -- .../components/explorer/ui/AllAppVersions.tsx | 66 -- .../src/components/explorer/ui/AppCard.tsx | 4 +- .../src/components/explorer/ui/AppFilter.tsx | 2 +- .../src/components/explorer/ui/AppHeader.tsx | 29 +- .../src/components/explorer/ui/AppHero.tsx | 2 +- .../src/components/explorer/ui/AppInfo.tsx | 10 +- .../components/explorer/ui/AppsDisplay.tsx | 2 +- .../explorer/ui/ExplorerErrorPage.tsx | 2 +- .../components/explorer/ui/ExplorerNav.tsx | 23 +- .../components/explorer/ui/FeaturedApps.tsx | 2 +- .../components/explorer/ui/VersionInfo.tsx | 58 -- .../views/AbilityVersionPoliciesView.tsx | 2 +- .../explorer/views/AppExploreView.tsx | 2 +- .../components/explorer/views/AppInfoView.tsx | 152 ++-- .../explorer/views/PolicyVersionInfoView.tsx | 2 +- .../explorer/wrappers/AppInfoWrapper.tsx | 32 +- .../src/components/shared/AccountTooltip.tsx | 17 +- .../src/components/shared/Footer.tsx | 2 +- .../src/components/shared/LandingPartners.tsx | 2 +- .../src/components/shared/ui/Breadcrumb.tsx | 2 +- .../src/components/shared/ui/CopyButton.tsx | 2 +- .../shared/ui/CountryCodeSelector.tsx | 109 --- .../src/components/shared/ui/LoadingLock.tsx | 2 +- .../shared/ui/MutationButtonStates.tsx | 2 +- .../src/components/shared/ui/StatCard.tsx | 2 +- .../src/components/shared/ui/input.tsx | 12 +- .../src/components/shared/ui/select.tsx | 2 +- .../src/components/shared/ui/sidebar.tsx | 2 +- .../src/components/shared/ui/textarea.tsx | 2 +- .../user-dashboard/auth/AuthMethods.tsx | 217 ------ .../user-dashboard/auth/ConnectMethods.tsx | 115 --- .../user-dashboard/auth/EthWalletAuth.tsx | 266 ------- .../user-dashboard/auth/SignUpView.tsx | 75 -- .../user-dashboard/auth/StytchOTP.tsx | 381 ---------- .../user-dashboard/auth/WebAuthn.tsx | 206 ------ .../connect/AppUnavailableConnect.tsx | 70 -- .../connect/AppVersionNotInRegistry.tsx | 86 --- .../connect/AuthConnectScreen.tsx | 43 -- .../connect/AuthenticationErrorScreen.tsx | 197 ----- .../connect/BadRedirectUriError.tsx | 131 ---- .../user-dashboard/connect/Connect.tsx | 315 -------- .../user-dashboard/connect/ConnectPage.tsx | 259 ------- .../connect/ConnectPageWraper.tsx | 291 -------- .../connect/DeletedAppConnect.tsx | 195 ----- .../connect/DeletedAppErrorScreen.tsx | 207 ------ .../connect/DisabledVersionConnect.tsx | 76 -- .../connect/EditPermissionsCard.tsx | 334 --------- .../connect/GeneralErrorScreen.tsx | 82 --- .../connect/RepermitConnect.tsx | 157 ---- .../connect/ReturningUserConnect.tsx | 246 ------- .../user-dashboard/connect/StatusMessage.tsx | 147 ---- .../connect/UnifiedConnectSkeleton.tsx | 181 ----- .../connect/UpdateVersionCard.tsx | 254 ------- .../connect/ui/AbilityAccordion.tsx | 106 --- .../connect/ui/AbilityHeader.tsx | 72 -- .../connect/ui/ActionButtons.tsx | 72 -- .../user-dashboard/connect/ui/ActionCard.tsx | 87 --- .../user-dashboard/connect/ui/AppInfo.tsx | 121 ---- .../connect/ui/ConnectAppHeader.tsx | 40 -- .../connect/ui/ConnectPageHeader.tsx | 118 --- .../user-dashboard/connect/ui/InfoBanner.tsx | 67 -- .../user-dashboard/connect/ui/PolicyForm.tsx | 220 ------ .../connect/ui/RequiredPolicies.tsx | 132 ---- .../user-dashboard/connect/ui/StatusCard.tsx | 83 --- .../AppVersionNotInRegistryUpdate.tsx | 66 -- .../dashboard/PermittedAppsPage.tsx | 329 --------- .../dashboard/PermittedAppsWrapper.tsx | 193 ----- .../dashboard/RepermitConnectPage.tsx | 198 ----- .../dashboard/RepermitConnectPageWrapper.tsx | 100 --- .../dashboard/UpdateVersionPage.tsx | 242 ------- .../dashboard/UpdateVersionPageWrapper.tsx | 147 ---- .../dashboard/UserPermissionPage.tsx | 437 ----------- .../dashboard/UserPermissionWrapper.tsx | 132 ---- .../ui/AppPermissionDashboardHeader.tsx | 214 ------ .../dashboard/ui/PageHeader.tsx | 53 -- .../dashboard/ui/PermittedAppCard.tsx | 192 ----- .../dashboard/ui/PermittedAppInfo.tsx | 126 ---- .../dashboard/ui/UserPermissionButtons.tsx | 90 --- .../ui/VincentYieldPromotionCard.tsx | 99 --- .../landing/ConnectToVincentYieldModal.tsx | 155 ---- .../landing/VincentYieldModal.tsx | 103 --- .../user-dashboard/sidebar/AppSidebar.tsx | 156 ---- .../components/user-dashboard/ui/Footer.tsx | 80 --- .../user-dashboard/wallet/WalletModal.tsx | 678 ------------------ .../wallet/WalletPageHeader.tsx | 93 --- .../withdraw/WalletConnect/ActiveSessions.tsx | 166 ----- .../withdraw/WalletConnect/DAppIcon.tsx | 45 -- .../WalletConnect/PendingRequests.tsx | 262 ------- .../withdraw/WalletConnect/QrReader.tsx | 103 --- .../withdraw/WalletConnect/RequestHandler.ts | 170 ----- .../WalletConnect/SessionProposal.tsx | 213 ------ .../withdraw/WalletConnect/WalletConnect.tsx | 256 ------- .../WalletConnect/WalletConnectCard.tsx | 65 -- .../WalletConnect/WalletConnectStore.ts | 125 ---- .../WalletConnect/WalletConnectUtil.ts | 281 -------- .../withdraw/manual/ChainSelector.tsx | 87 --- .../withdraw/manual/ManualWithdraw.tsx | 129 ---- .../withdraw/manual/ManualWithdrawForm.tsx | 188 ----- .../withdraw/manual/TokenSelector.tsx | 75 -- .../user-dashboard/withdraw/types.ts | 11 - packages/apps/app-dashboard/src/config/env.ts | 7 +- .../apps/app-dashboard/src/config/registry.ts | 18 + .../ability/useAbilityAddressCheck.ts | 7 +- .../ability/useUserAbilities.ts | 5 +- .../app/useAppAddressCheck.ts | 61 +- .../app/useOnChainAppOwnership.ts | 101 +++ .../developer-dashboard/app/useUserApps.ts | 135 +++- .../policy/usePolicyAddressCheck.ts | 7 +- .../policy/useUserPolicies.ts | 5 +- .../src/hooks/developer-dashboard/useAuth.tsx | 327 +++++++++ .../useDeveloperSidebarData.ts | 42 -- .../developer-dashboard/useEnsureChain.ts | 97 +++ .../useVincentApiWithJWT.ts | 139 ---- .../developer-dashboard/useWagmiSigner.ts | 79 ++ .../src/hooks/shared/useFarcaster.ts | 29 - .../src/hooks/useBlockchainAppData.ts | 71 +- .../src/hooks/useBlockchainAppVersionData.ts | 61 -- .../src/hooks/useBlockchainAppVersions.ts | 75 ++ .../src/hooks/useBlockchainAppVersionsData.ts | 68 -- .../WalletConnect/useWalletConnectRequests.ts | 346 --------- .../WalletConnect/useWalletConnectSession.ts | 404 ----------- .../connect/useAddPermittedActions.ts | 76 -- .../user-dashboard/connect/useAuthGuard.tsx | 17 - .../user-dashboard/connect/useCanGoBack.ts | 13 - .../connect/useCheckAppVersionExists.ts | 116 --- .../connect/useConnectFormData.ts | 137 ---- .../user-dashboard/connect/useConnectInfo.ts | 487 ------------- .../user-dashboard/connect/useJwtRedirect.ts | 102 --- .../connect/usePendingAppConnectPkp.ts | 25 - .../user-dashboard/connect/useUriPrecheck.ts | 52 -- .../connect/useUrlRedirectUri.ts | 28 - .../dashboard/useFetchAppVersionsMap.ts | 150 ---- .../dashboard/useFetchUserPermissions.ts | 74 -- .../dashboard/useFormatUserPermissions.ts | 103 --- .../src/hooks/user-dashboard/index.ts | 6 - .../src/hooks/user-dashboard/useAccounts.ts | 57 -- .../hooks/user-dashboard/useAgentPkpForApp.ts | 81 --- .../hooks/user-dashboard/useAllAgentApps.ts | 36 - .../src/hooks/user-dashboard/useAuthInfo.ts | 234 ------ .../hooks/user-dashboard/useAuthenticate.ts | 156 ---- .../user-dashboard/yield/useFetchYieldData.ts | 58 -- packages/apps/app-dashboard/src/index.css | 30 +- .../layout/developer-dashboard/AppLayout.tsx | 76 +- .../src/layout/shared/GlobeLayout.tsx | 2 +- .../shared}/GlobeOffsetContext.tsx | 0 .../user-dashboard/UserDashboardLayout.tsx | 27 - .../user-dashboard/UserLayoutWithSidebar.tsx | 165 ----- .../ui/theme.ts => lib/themeClasses.ts} | 0 .../developer-dashboard/DashboardPage.tsx | 2 +- .../src/pages/developer-dashboard/index.tsx | 1 - .../pages/explorer/ExplorerLandingPage.tsx | 114 --- .../src/pages/shared/RootPage.tsx | 2 +- .../user-dashboard/WalletPageWrapper.tsx | 81 --- .../src/pages/user-dashboard/all-wallets.tsx | 203 ------ .../src/pages/user-dashboard/faq.tsx | 352 --------- .../src/pages/user-dashboard/wallet.tsx | 104 --- .../src/providers/AuthProviderWrapper.tsx | 6 + .../src/providers/StytchProviderWrapper.tsx | 13 - .../src/providers/WagmiProviderWrapper.tsx | 7 +- .../app-dashboard/src/providers/index.tsx | 13 +- packages/apps/app-dashboard/src/routes.tsx | 87 +-- .../app-dashboard/src/store/agentPkpsApi.ts | 113 --- .../apps/app-dashboard/src/store/store.ts | 40 +- .../src/types/app-dashboard/viewTypes.ts | 11 - .../types/developer-dashboard/viewTypes.ts | 11 - .../src/types/shared/StatusType.ts | 1 - .../developer-dashboard/initPkpSigner.ts | 34 - .../developer-dashboard/readOnlySigner.ts | 4 +- .../src/utils/user-dashboard/addPayee.ts | 37 - .../src/utils/user-dashboard/countryCodes.ts | 11 - .../user-dashboard/get-pkp-nft-contract.ts | 36 - .../src/utils/user-dashboard/getAgentPkps.ts | 209 ------ .../user-dashboard/getAppVersionStatus.ts | 118 --- .../user-dashboard/getValidSessionSigs.ts | 66 -- .../user-dashboard/hasConfigurablePolicies.ts | 47 -- .../src/utils/user-dashboard/hexToBase58.ts | 28 - .../src/utils/user-dashboard/lit.ts | 527 -------------- .../user-dashboard/transactionService.ts | 239 ------ .../utils/user-dashboard/withdrawHandler.ts | 293 -------- .../apps/app-dashboard/tailwind.config.ts | 48 +- pnpm-lock.yaml | 59 +- 283 files changed, 4504 insertions(+), 23224 deletions(-) delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionAbilitiesForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/DeleteAppVersionForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionAbilityForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditPublishedAppForm.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/SetActiveVersionForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionAbilitiesDisplay.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetailView.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetails.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionsListView.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/views/ManageAppVersionAbilities.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionAbilitiesWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailWrapper.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailsWrapper.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppVersionWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/PublishAppVersionWrapper.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppProgressBar.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepAbilities.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepDelegatees.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepMetadata.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepReview.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppSubmitting.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/index.ts create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/types.ts create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/useCreateAppFormState.ts delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppMismatchResolution.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppUnpublishedButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionDeletedButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionMismatchResolution.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionPublishedButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionUnpublishedButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/EditAppVersionAbilityButton.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PublishAppVersionButton.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppButton.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionAbilityButton.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionButton.tsx create mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/auth/SignInScreen.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AbilityList.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AppList.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/DeveloperSidebarWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/PolicyList.tsx rename packages/apps/app-dashboard/src/components/developer-dashboard/{ => ui}/AbilitySelectorModal.tsx (98%) rename packages/apps/app-dashboard/src/components/developer-dashboard/{ => ui}/AbilityVersionSelectorModal.tsx (98%) delete mode 100644 packages/apps/app-dashboard/src/components/developer-dashboard/ui/ConnectPageModal.tsx rename packages/apps/app-dashboard/src/components/developer-dashboard/{ => ui}/DashboardContent.tsx (99%) delete mode 100644 packages/apps/app-dashboard/src/components/explorer/ui/ActiveAppVersion.tsx delete mode 100644 packages/apps/app-dashboard/src/components/explorer/ui/AllAppVersions.tsx delete mode 100644 packages/apps/app-dashboard/src/components/explorer/ui/VersionInfo.tsx delete mode 100644 packages/apps/app-dashboard/src/components/shared/ui/CountryCodeSelector.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/AuthMethods.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/ConnectMethods.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/EthWalletAuth.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/SignUpView.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/StytchOTP.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/auth/WebAuthn.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/AppUnavailableConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/AppVersionNotInRegistry.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/AuthConnectScreen.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/AuthenticationErrorScreen.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/BadRedirectUriError.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/Connect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ConnectPage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ConnectPageWraper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/DeletedAppConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/DeletedAppErrorScreen.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/DisabledVersionConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/EditPermissionsCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/GeneralErrorScreen.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/RepermitConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ReturningUserConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/StatusMessage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/UnifiedConnectSkeleton.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/UpdateVersionCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/AbilityAccordion.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/AbilityHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/ActionButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/ActionCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/AppInfo.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/ConnectAppHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/ConnectPageHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/InfoBanner.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/PolicyForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/RequiredPolicies.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/connect/ui/StatusCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/AppVersionNotInRegistryUpdate.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/PermittedAppsPage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/PermittedAppsWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/RepermitConnectPage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/RepermitConnectPageWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/UpdateVersionPage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/UpdateVersionPageWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/UserPermissionPage.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/UserPermissionWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/AppPermissionDashboardHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/PageHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/PermittedAppCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/PermittedAppInfo.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/UserPermissionButtons.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/dashboard/ui/VincentYieldPromotionCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/landing/ConnectToVincentYieldModal.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/landing/VincentYieldModal.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/sidebar/AppSidebar.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/ui/Footer.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/wallet/WalletModal.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/wallet/WalletPageHeader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/ActiveSessions.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/DAppIcon.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/PendingRequests.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/QrReader.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/RequestHandler.ts delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/SessionProposal.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/WalletConnect.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/WalletConnectCard.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/WalletConnectStore.ts delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/WalletConnect/WalletConnectUtil.ts delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/manual/ChainSelector.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/manual/ManualWithdraw.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/manual/ManualWithdrawForm.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/manual/TokenSelector.tsx delete mode 100644 packages/apps/app-dashboard/src/components/user-dashboard/withdraw/types.ts create mode 100644 packages/apps/app-dashboard/src/config/registry.ts create mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/app/useOnChainAppOwnership.ts create mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/useAuth.tsx delete mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/useDeveloperSidebarData.ts create mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/useEnsureChain.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/useVincentApiWithJWT.ts create mode 100644 packages/apps/app-dashboard/src/hooks/developer-dashboard/useWagmiSigner.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/shared/useFarcaster.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/useBlockchainAppVersionData.ts create mode 100644 packages/apps/app-dashboard/src/hooks/useBlockchainAppVersions.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/useBlockchainAppVersionsData.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/WalletConnect/useWalletConnectRequests.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/WalletConnect/useWalletConnectSession.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useAddPermittedActions.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useAuthGuard.tsx delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useCanGoBack.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useCheckAppVersionExists.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useConnectFormData.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useConnectInfo.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useJwtRedirect.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/usePendingAppConnectPkp.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useUriPrecheck.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/connect/useUrlRedirectUri.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/dashboard/useFetchAppVersionsMap.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/dashboard/useFetchUserPermissions.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/dashboard/useFormatUserPermissions.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/index.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/useAccounts.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/useAgentPkpForApp.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/useAllAgentApps.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/useAuthInfo.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/useAuthenticate.ts delete mode 100644 packages/apps/app-dashboard/src/hooks/user-dashboard/yield/useFetchYieldData.ts rename packages/apps/app-dashboard/src/{contexts => layout/shared}/GlobeOffsetContext.tsx (100%) delete mode 100644 packages/apps/app-dashboard/src/layout/user-dashboard/UserDashboardLayout.tsx delete mode 100644 packages/apps/app-dashboard/src/layout/user-dashboard/UserLayoutWithSidebar.tsx rename packages/apps/app-dashboard/src/{components/user-dashboard/connect/ui/theme.ts => lib/themeClasses.ts} (100%) delete mode 100644 packages/apps/app-dashboard/src/pages/developer-dashboard/index.tsx delete mode 100644 packages/apps/app-dashboard/src/pages/explorer/ExplorerLandingPage.tsx delete mode 100644 packages/apps/app-dashboard/src/pages/user-dashboard/WalletPageWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/pages/user-dashboard/all-wallets.tsx delete mode 100644 packages/apps/app-dashboard/src/pages/user-dashboard/faq.tsx delete mode 100644 packages/apps/app-dashboard/src/pages/user-dashboard/wallet.tsx create mode 100644 packages/apps/app-dashboard/src/providers/AuthProviderWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/providers/StytchProviderWrapper.tsx delete mode 100644 packages/apps/app-dashboard/src/store/agentPkpsApi.ts delete mode 100644 packages/apps/app-dashboard/src/types/app-dashboard/viewTypes.ts delete mode 100644 packages/apps/app-dashboard/src/types/developer-dashboard/viewTypes.ts delete mode 100644 packages/apps/app-dashboard/src/types/shared/StatusType.ts delete mode 100644 packages/apps/app-dashboard/src/utils/developer-dashboard/initPkpSigner.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/addPayee.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/countryCodes.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/get-pkp-nft-contract.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/getAgentPkps.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/getAppVersionStatus.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/getValidSessionSigs.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/hasConfigurablePolicies.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/hexToBase58.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/lit.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/transactionService.ts delete mode 100644 packages/apps/app-dashboard/src/utils/user-dashboard/withdrawHandler.ts diff --git a/packages/apps/app-dashboard/.env.example b/packages/apps/app-dashboard/.env.example index b7dab947b..f210828d8 100644 --- a/packages/apps/app-dashboard/.env.example +++ b/packages/apps/app-dashboard/.env.example @@ -6,7 +6,7 @@ VITE_STYTCH_PROJECT_ID= VITE_LIT_PAYER_SECRET_KEY= VITE_LIT_RELAY_API_KEY= -VITE_VINCENT_YELLOWSTONE_RPC=https://yellowstone-rpc.litprotocol.com/ +VITE_VINCENT_BASE_SEPOLIA_RPC= VITE_WALLETCONNECT_PROJECT_ID= diff --git a/packages/apps/app-dashboard/package.json b/packages/apps/app-dashboard/package.json index a9475a593..6d909c4ad 100644 --- a/packages/apps/app-dashboard/package.json +++ b/packages/apps/app-dashboard/package.json @@ -23,8 +23,8 @@ "@lit-protocol/pkp-ethers": "^7.2.3", "@lit-protocol/types": "^7.2.3", "@lit-protocol/vincent-app-sdk": "workspace:*", - "@lit-protocol/vincent-contracts-sdk": "5.1.0", - "@lit-protocol/vincent-registry-sdk": "4.3.0", + "@lit-protocol/vincent-contracts-sdk": "workspace:*", + "@lit-protocol/vincent-registry-sdk": "workspace:*", "@radix-ui/react-checkbox": "^1.2.3", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-label": "^2.1.7", diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/ChangeAbilityOwnerForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/ChangeAbilityOwnerForm.tsx index 238991337..4a5d85fb5 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/ChangeAbilityOwnerForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/ChangeAbilityOwnerForm.tsx @@ -7,7 +7,7 @@ import { Button } from '@/components/shared/ui/button'; import { Form } from '@/components/shared/ui/form'; import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField } from '../../form-fields'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; //const { abilityOwnerDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityForm.tsx index f3bc45eed..97d6ca630 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityForm.tsx @@ -9,7 +9,7 @@ import { Button } from '@/components/shared/ui/button'; import { TextField, LongTextField, ImageUploadField } from '../../form-fields'; import { DeploymentStatusSelectField } from '../../form-fields/array/DeploymentStatusSelectField'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; const { abilityDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityVersionForm.tsx index 4f73d7a02..031d0008c 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityVersionForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/CreateAbilityVersionForm.tsx @@ -8,7 +8,7 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField, LongTextField } from '../../form-fields'; import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; import { Ability } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { abilityVersionDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityForm.tsx index 13d04c2e7..1213b94fb 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityForm.tsx @@ -9,7 +9,7 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField, LongTextField, SelectField, ImageUploadField } from '../../form-fields'; import { Ability, AbilityVersion } from '@/types/developer-dashboard/appTypes'; import { DeploymentStatusSelectField } from '../../form-fields/array/DeploymentStatusSelectField'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { abilityDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityVersionForm.tsx index eb4ce67fd..a82368b38 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityVersionForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/EditAbilityVersionForm.tsx @@ -9,7 +9,7 @@ import { AbilityVersion } from '@/types/developer-dashboard/appTypes'; import { LongTextField } from '../../form-fields'; import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; const { abilityVersionDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/RefreshAbilityVersionPoliciesForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/RefreshAbilityVersionPoliciesForm.tsx index af189b66a..49937c58a 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/RefreshAbilityVersionPoliciesForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/forms/RefreshAbilityVersionPoliciesForm.tsx @@ -1,6 +1,6 @@ import { AlertCircle, CheckCircle } from 'lucide-react'; import { Button } from '@/components/shared/ui/button'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; +import { theme } from '@/lib/themeClasses'; interface RefreshAbilityVersionPoliciesFormProps { onSubmit: () => Promise; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilitiesListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilitiesListView.tsx index 1d011bc93..7f26c861a 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilitiesListView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilitiesListView.tsx @@ -7,7 +7,7 @@ import { formatDate } from '@/utils/developer-dashboard/formatDateAndTime'; import { UndeleteAbilityButton } from '@/components/developer-dashboard/ability/wrappers'; import { Ability } from '@/types/developer-dashboard/appTypes'; import { AbilityCard } from '@/components/developer-dashboard/ui/AbilityCard'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { Logo } from '@/components/shared/ui/Logo'; interface AbilitiesListViewProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityDetailsView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityDetailsView.tsx index 389771e93..d9884daa3 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityDetailsView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityDetailsView.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Ability, AbilityVersion } from '@/types/developer-dashboard/appTypes'; import { Badge } from '@/components/shared/ui/badge'; import { Logo } from '@/components/shared/ui/Logo'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { motion } from 'framer-motion'; import { AppDetail } from '@/components/developer-dashboard/ui/AppDetail'; import { AbilityActionButtons } from '../wrappers/ui/AbilityActionButtons'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionDetailsView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionDetailsView.tsx index 5a60bc088..c1e789a49 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionDetailsView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionDetailsView.tsx @@ -3,7 +3,7 @@ import { ExternalLink } from 'lucide-react'; import { motion } from 'framer-motion'; import { Ability, AbilityVersion } from '@/types/developer-dashboard/appTypes'; import { Badge } from '@/components/shared/ui/badge'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { AbilityVersionActionButtons } from '../wrappers/ui/AbilityVersionActionButtons'; import { AppDetail } from '@/components/developer-dashboard/ui/AppDetail'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionsListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionsListView.tsx index 7f9656486..b71cabd02 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionsListView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/views/AbilityVersionsListView.tsx @@ -3,7 +3,7 @@ import { Ability, AbilityVersion } from '@/types/developer-dashboard/appTypes'; import { Package, Plus } from 'lucide-react'; import { motion } from 'framer-motion'; import { UndeleteAbilityVersionButton } from '../wrappers/ui/UndeleteAbilityVersionButton'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { VersionCard } from '@/components/developer-dashboard/ui/VersionCard'; interface AbilityVersionsListViewProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityOverviewWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityOverviewWrapper.tsx index b389b746f..1afb18863 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityOverviewWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityOverviewWrapper.tsx @@ -16,7 +16,7 @@ import { DialogHeader, DialogTitle, } from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; type ViewType = | 'details' diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityVersionDetailsWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityVersionDetailsWrapper.tsx index 4d48e533a..7bc7e1cf9 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityVersionDetailsWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/AbilityVersionDetailsWrapper.tsx @@ -19,7 +19,7 @@ import { DialogHeader, DialogTitle, } from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; type ViewType = 'details' | 'edit-version' | 'delete-version' | 'refresh-policies'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityActionButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityActionButtons.tsx index 99818b39e..5b8f66125 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityActionButtons.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityActionButtons.tsx @@ -1,5 +1,5 @@ import { Edit, Trash2, Plus, List, ArrowLeftRight } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; interface AbilityActionButtonsProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityVersionActionButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityVersionActionButtons.tsx index bb0d5b05b..7ebe9c7b3 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityVersionActionButtons.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/AbilityVersionActionButtons.tsx @@ -1,5 +1,5 @@ import { Edit, Trash2, RefreshCw } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; interface AbilityVersionActionButtonsProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/UndeleteAbilityButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/UndeleteAbilityButton.tsx index 478068450..ee3590c07 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/UndeleteAbilityButton.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ability/wrappers/ui/UndeleteAbilityButton.tsx @@ -6,7 +6,7 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; import Loading from '@/components/shared/ui/Loading'; import { Ability } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface UndeleteAbilityWrapperProps { ability: Ability; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppForm.tsx index 9de79254d..a3e4dc9fd 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppForm.tsx @@ -10,55 +10,67 @@ import { Button } from '@/components/shared/ui/button'; import { TextField, LongTextField, - ArrayField, ImageUploadField, } from '@/components/developer-dashboard/form-fields'; import { DeploymentStatusSelectField } from '@/components/developer-dashboard/form-fields/array/DeploymentStatusSelectField'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { appDoc } = docSchemas; -const { - name, - description, - contactEmail, - appUserUrl, - logo, - redirectUris, - deploymentStatus, - delegateeAddresses, -} = appDoc.shape; +const { appId, name, description, contactEmail, appUrl, logo, deploymentStatus } = appDoc.shape; +// Schema for creating a new app (appId will be generated on-chain) export const CreateAppSchema = z .object({ name, description, contactEmail, - appUserUrl, + appUrl, + logo, + deploymentStatus, + }) + .strict(); + +// Schema for adding existing on-chain app to registry +export const CreateAppWithIdSchema = z + .object({ + appId, + name, + description, + contactEmail, + appUrl, logo, - redirectUris, deploymentStatus, - delegateeAddresses, }) .strict(); export type CreateAppFormData = z.infer; +export type CreateAppWithIdFormData = z.infer; interface CreateAppFormProps { - onSubmit: (data: CreateAppFormData) => Promise; + onSubmit: (data: CreateAppFormData | CreateAppWithIdFormData) => Promise; isSubmitting?: boolean; + existingAppId?: number; + onSuccess?: () => void; } -export function CreateAppForm({ onSubmit, isSubmitting = false }: CreateAppFormProps) { +export function CreateAppForm({ + onSubmit, + isSubmitting = false, + existingAppId, + onSuccess, +}: CreateAppFormProps) { const [submitError, setSubmitError] = useState(null); const [submitSuccess, setSubmitSuccess] = useState(false); - const form = useForm({ - resolver: zodResolver(CreateAppSchema), + // Use different schema based on whether we have an existing app ID + const schema = existingAppId ? CreateAppWithIdSchema : CreateAppSchema; + + const form = useForm({ + resolver: zodResolver(schema), defaultValues: { - redirectUris: [''], - delegateeAddresses: [''], + ...(existingAppId && { appId: existingAppId }), deploymentStatus: 'dev', }, }); @@ -74,12 +86,15 @@ export function CreateAppForm({ onSubmit, isSubmitting = false }: CreateAppFormP formState: { errors }, } = form; - const handleFormSubmit = async (data: CreateAppFormData) => { + const handleFormSubmit = async (data: CreateAppFormData | CreateAppWithIdFormData) => { setSubmitError(null); setSubmitSuccess(false); try { await onSubmit(data); setSubmitSuccess(true); + if (onSuccess) { + onSuccess(); + } } catch (error) { console.error('Failed to create app:', error); setSubmitError(extractErrorMessage(error, 'Failed to create app')); @@ -129,10 +144,10 @@ export function CreateAppForm({ onSubmit, isSubmitting = false }: CreateAppFormP /> @@ -163,31 +178,6 @@ export function CreateAppForm({ onSubmit, isSubmitting = false }: CreateAppFormP - {/* Full Width Fields */} -
- - - -
- {/* Status Messages */} {submitError && (
diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionAbilitiesForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionAbilitiesForm.tsx deleted file mode 100644 index 038a7843a..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionAbilitiesForm.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useState } from 'react'; -import { Button } from '@/components/shared/ui/button'; -import { Ability } from '@/types/developer-dashboard/appTypes'; -import { AbilitySelectorModal } from '../../AbilitySelectorModal'; -import { Plus } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -interface CreateAppVersionAbilitiesFormProps { - onAbilityAdd: (ability: Ability) => Promise; - existingAbilities?: string[]; // Array of package names already added - availableAbilities: Ability[]; -} - -export function CreateAppVersionAbilitiesForm({ - onAbilityAdd, - existingAbilities = [], - availableAbilities, -}: CreateAppVersionAbilitiesFormProps) { - const [isModalOpen, setIsModalOpen] = useState(false); - - const handleAbilityAdd = async (ability: Ability) => { - await onAbilityAdd(ability); - setIsModalOpen(false); - }; - - return ( -
-
-

- Add Abilities to App Version -

-

- Clicking the package name will open the ability's npm page. Otherwise, abilities will be - added immediately when selected. -

-
-
-
- -
-
- - setIsModalOpen(false)} - onAbilityAdd={handleAbilityAdd} - existingAbilities={existingAbilities} - availableAbilities={availableAbilities} - /> -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionForm.tsx deleted file mode 100644 index f1321de2e..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/CreateAppVersionForm.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { z } from 'zod'; -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { AlertCircle, CheckCircle } from 'lucide-react'; -import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { Form } from '@/components/shared/ui/form'; -import { Button } from '@/components/shared/ui/button'; -import { LongTextField } from '@/components/developer-dashboard/form-fields'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; -import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; - -const { appVersionDoc } = docSchemas; - -const { changes } = appVersionDoc.shape; - -export const CreateAppVersionSchema = z.object({ changes }).strict(); - -export type CreateAppVersionFormData = z.infer; - -interface CreateAppVersionFormProps { - onSubmit: (data: CreateAppVersionFormData) => Promise; - isSubmitting?: boolean; -} - -export function CreateAppVersionForm({ - onSubmit, - isSubmitting = false, -}: CreateAppVersionFormProps) { - const [submitError, setSubmitError] = useState(null); - const [submitSuccess, setSubmitSuccess] = useState(false); - - const form = useForm({ - resolver: zodResolver(CreateAppVersionSchema), - defaultValues: { - changes: '', - }, - }); - - const { - register, - handleSubmit, - formState: { errors }, - } = form; - - const handleFormSubmit = async (data: CreateAppVersionFormData) => { - setSubmitError(null); - setSubmitSuccess(false); - try { - await onSubmit(data); - setSubmitSuccess(true); - } catch (error) { - console.error('Failed to create app version:', error); - setSubmitError(extractErrorMessage(error, 'Failed to create app version')); - } - }; - - return ( -
- - - - {/* Status Messages */} - {submitError && ( -
- - {submitError} -
- )} - - {submitSuccess && ( -
- - - App version created successfully! - -
- )} - - - - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/DeleteAppForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/DeleteAppForm.tsx index c8e520842..f7ca42a11 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/DeleteAppForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/DeleteAppForm.tsx @@ -5,8 +5,9 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Form } from '@/components/shared/ui/form'; import { Button } from '@/components/shared/ui/button'; import { TextField } from '../../form-fields'; -import { AlertCircle, CheckCircle } from 'lucide-react'; +import { AlertCircle, CheckCircle, AlertTriangle } from 'lucide-react'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; +import { theme } from '@/lib/themeClasses'; function buildConfirmationString(appName: string): string { return `I want to delete app ${appName}`; @@ -102,6 +103,29 @@ export function DeleteAppForm({ appName, onSubmit, isSubmitting = false }: Delet
)} + {/* Gas Warning */} +
+ +
+

+ This action requires a blockchain transaction +

+

+ Deleting this app will cost gas fees. This action can be undone, but undeleting will + also require gas. Please consider carefully before proceeding. +

+
+
+ - - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppForm.tsx index 6c56b35ed..226e48e34 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppForm.tsx @@ -10,40 +10,27 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField, LongTextField, - ArrayField, DeploymentStatusSelectField, - NumberSelectField, ImageUploadField, } from '@/components/developer-dashboard/form-fields'; -import { App, AppVersion } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { App } from '@/types/developer-dashboard/appTypes'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { appDoc } = docSchemas; -const { - name, - description, - contactEmail, - appUserUrl, - logo, - redirectUris, - deploymentStatus, - activeVersion, - delegateeAddresses, -} = appDoc.shape; +const { name, description, contactEmail, appUrl, logo, deploymentStatus, activeVersion } = + appDoc.shape; export const EditAppSchema = z .object({ name, description, contactEmail, - appUserUrl, + appUrl, logo, - redirectUris, deploymentStatus, activeVersion, - delegateeAddresses, }) .required() .partial({ logo: true, activeVersion: true }) @@ -53,19 +40,11 @@ export type EditAppFormData = z.infer; interface EditAppFormProps { appData: App; - appVersions: AppVersion[]; onSubmit: (data: EditAppFormData) => Promise; isSubmitting?: boolean; - isPublished?: boolean; } -export function EditAppForm({ - appData, - appVersions, - onSubmit, - isSubmitting = false, - isPublished = false, -}: EditAppFormProps) { +export function EditAppForm({ appData, onSubmit, isSubmitting = false }: EditAppFormProps) { const [submitError, setSubmitError] = useState(null); const [submitSuccess, setSubmitSuccess] = useState(false); @@ -75,12 +54,10 @@ export function EditAppForm({ name: appData.name, description: appData.description, contactEmail: appData.contactEmail, - appUserUrl: appData.appUserUrl, + appUrl: appData.appUrl, logo: appData.logo, - redirectUris: appData.redirectUris, deploymentStatus: appData.deploymentStatus, activeVersion: appData.activeVersion, - delegateeAddresses: appData.delegateeAddresses, }, }); @@ -107,12 +84,6 @@ export function EditAppForm({ } }; - // Create version options from appVersions, showing enabled/disabled status for all versions - const versionOptions = appVersions.map((version) => ({ - value: version.version, - label: `Version ${version.version} (${version.enabled ? 'Enabled' : 'Disabled'})`, - })); - return (
@@ -142,10 +113,10 @@ export function EditAppForm({ /> @@ -159,40 +130,8 @@ export function EditAppForm({ label="Logo" /> - - - {isPublished && ( - - )} - - - {/* Status Messages */} {submitError && } {submitSuccess && } diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionAbilityForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionAbilityForm.tsx deleted file mode 100644 index cdd4d8e2c..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionAbilityForm.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { z } from 'zod'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { Form } from '@/components/shared/ui/form'; -import { Button } from '@/components/shared/ui/button'; -import { AppVersionAbility, Policy } from '@/types/developer-dashboard/appTypes'; -import { PolicyWithVersion } from '@/utils/developer-dashboard/sortSupportedPolicies'; -import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; -import { PolicyCheckboxField } from '../../form-fields'; - -const { appVersionAbilityDoc } = docSchemas; - -const { hiddenSupportedPolicies } = appVersionAbilityDoc.shape; - -export const EditAppVersionAbilitySchema = z - .object({ - hiddenSupportedPolicies, - }) - .required() - .strict(); - -export type EditAppVersionAbilityFormData = z.infer; - -interface EditAppVersionAbilityFormProps { - ability: AppVersionAbility; - policies: (Policy | PolicyWithVersion)[]; - onSubmit: (data: EditAppVersionAbilityFormData) => Promise; - onCancel: () => void; - isSubmitting?: boolean; -} - -export function EditAppVersionAbilityForm({ - ability, - policies, - onSubmit, - onCancel, - isSubmitting = false, -}: EditAppVersionAbilityFormProps) { - const form = useForm({ - resolver: zodResolver(EditAppVersionAbilitySchema), - defaultValues: { - hiddenSupportedPolicies: ability.hiddenSupportedPolicies || [], - }, - }); - - const { - handleSubmit, - watch, - setValue, - formState: { errors }, - } = form; - - return ( - - - - -
- - -
- - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionForm.tsx deleted file mode 100644 index e273c2007..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditAppVersionForm.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; - -import { Form } from '@/components/shared/ui/form'; -import { Button } from '@/components/shared/ui/button'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { AppVersion } from '@/types/developer-dashboard/appTypes'; -import { LongTextField } from '@/components/developer-dashboard/form-fields'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; -import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; - -const { appVersionDoc } = docSchemas; - -const { changes } = appVersionDoc.shape; - -export const EditAppVersionSchema = z - .object({ - changes, - }) - .required() - .strict(); - -export type EditAppVersionFormData = z.infer; - -interface EditAppVersionFormProps { - versionData: AppVersion; - onSubmit: (data: EditAppVersionFormData) => Promise; - isSubmitting?: boolean; -} - -export function EditAppVersionForm({ - versionData, - onSubmit, - isSubmitting = false, -}: EditAppVersionFormProps) { - const [submitError, setSubmitError] = useState(null); - const [submitSuccess, setSubmitSuccess] = useState(false); - - const form = useForm({ - resolver: zodResolver(EditAppVersionSchema), - defaultValues: { - changes: versionData.changes || '', - }, - }); - - const { - register, - handleSubmit, - formState: { errors }, - } = form; - - const handleFormSubmit = async (data: EditAppVersionFormData) => { - setSubmitError(null); - setSubmitSuccess(false); - try { - await onSubmit(data); - setSubmitSuccess(true); - } catch (error) { - console.error('Failed to update app version:', error); - setSubmitError(extractErrorMessage(error, 'Failed to update app version')); - } - }; - - return ( -
- - - - {/* Status Messages */} - {submitError && } - {submitSuccess && ( - - )} - - - - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditPublishedAppForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditPublishedAppForm.tsx deleted file mode 100644 index 41304fe07..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/EditPublishedAppForm.tsx +++ /dev/null @@ -1,159 +0,0 @@ -import { useState } from 'react'; -import { z } from 'zod'; -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { Form } from '@/components/shared/ui/form'; -import { Button } from '@/components/shared/ui/button'; -import { TextField, LongTextField, ArrayField, ImageUploadField } from '../../form-fields'; -import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; -import { App } from '@/types/developer-dashboard/appTypes'; -import { DeploymentStatusSelectField } from '../../form-fields/array/DeploymentStatusSelectField'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; - -const { appDoc } = docSchemas; - -const { name, description, contactEmail, appUserUrl, logo, redirectUris, deploymentStatus } = - appDoc.shape; - -export const EditPublishedAppSchema = z - .object({ - name, - description, - contactEmail, - appUserUrl, - logo, - redirectUris, - deploymentStatus, - }) - .required() - .partial({ logo: true }) - .strict(); - -export type EditPublishedAppFormData = z.infer; - -interface EditPublishedAppFormProps { - appData: App; - onSubmit: (data: EditPublishedAppFormData) => Promise; - isSubmitting?: boolean; -} - -export function EditPublishedAppForm({ - appData, - onSubmit, - isSubmitting = false, -}: EditPublishedAppFormProps) { - const [submitError, setSubmitError] = useState(null); - const [submitSuccess, setSubmitSuccess] = useState(false); - - const form = useForm({ - resolver: zodResolver(EditPublishedAppSchema), - defaultValues: { - name: appData.name, - description: appData.description, - contactEmail: appData.contactEmail, - appUserUrl: appData.appUserUrl, - logo: appData.logo, - redirectUris: appData.redirectUris, - deploymentStatus: appData.deploymentStatus, - }, - }); - - const { - register, - handleSubmit, - watch, - setValue, - control, - setError, - clearErrors, - formState: { errors }, - } = form; - - const handleFormSubmit = async (data: EditPublishedAppFormData) => { - setSubmitError(null); - setSubmitSuccess(false); - try { - await onSubmit(data); - setSubmitSuccess(true); - } catch (error) { - console.error('Failed to update app:', error); - setSubmitError(extractErrorMessage(error, 'Failed to update app')); - } - }; - - return ( -
- - - - - - - - - - - - - - - - {/* Status Messages */} - {submitError && } - {submitSuccess && } - - - - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/ManageDelegateesForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/ManageDelegateesForm.tsx index 108d4d2e1..4b1c02771 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/ManageDelegateesForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/ManageDelegateesForm.tsx @@ -18,10 +18,9 @@ import { Plus, Trash2, Dices, Check } from 'lucide-react'; import { TextField } from '../../form-fields'; import { CopyButton } from '@/components/shared/ui/CopyButton'; import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import { addPayee } from '@/utils/user-dashboard/addPayee'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { theme, fonts } from '@/lib/themeClasses'; const AddDelegateeSchema = z.object({ address: z.string().refine((val) => ethers.utils.isAddress(val), { @@ -33,15 +32,18 @@ type AddDelegateeFormData = z.infer; interface ManageDelegateesFormProps { existingDelegatees: string[]; - refetchBlockchainData: () => void; + onDelegateeAdded?: (address: string) => void; + onDelegateeRemoved?: (address: string) => void; } export function ManageDelegateesForm({ existingDelegatees, - refetchBlockchainData, + onDelegateeAdded, + onDelegateeRemoved, }: ManageDelegateesFormProps) { const { appId } = useParams<{ appId: string }>(); - const { authInfo, sessionSigs } = useReadAuthInfo(); + const { getSigner } = useWagmiSigner(); + const { ensureChain, infoMessage } = useEnsureChain(); const [error, setError] = useState(''); const [removingDelegatee, setRemovingDelegatee] = useState(null); const [generatedWallet, setGeneratedWallet] = useState<{ @@ -51,6 +53,14 @@ export function ManageDelegateesForm({ const [addingGeneratedWallet, setAddingGeneratedWallet] = useState(false); const [isGenerateDialogOpen, setIsGenerateDialogOpen] = useState(false); + // Local state for optimistic updates (RPC caching can cause stale data) + const [localDelegatees, setLocalDelegatees] = useState(existingDelegatees); + + // Sync local state when prop changes (e.g., after page refresh) + useEffect(() => { + setLocalDelegatees(existingDelegatees); + }, [existingDelegatees]); + const form = useForm({ resolver: zodResolver(AddDelegateeSchema), defaultValues: { @@ -76,37 +86,43 @@ export function ManageDelegateesForm({ }, [error]); const handleAddDelegatee = async (data: AddDelegateeFormData) => { - try { - await addDelegateeAddress(data.address); - // Success - form will remain for adding more delegatees - } catch (error) { - console.error('Failed to add delegatee:', error); - const message = error instanceof Error ? error.message : String(error); - if (message.includes('user rejected')) { - setError('Transaction rejected.'); - } else if (message.includes('DelegateeAlreadyRegistered')) { - // Error already set in addDelegateeAddress - } else { - setError(`Failed to add delegatee: ${message}`); - } + const success = await addDelegateeAddress(data.address); + if (success) { + form.reset(); // Clear the form on success } }; const handleRemoveDelegatee = async (addressToRemove: string) => { if (!appId) return; + // Ensure user is on Base Sepolia + try { + const canProceed = await ensureChain('Remove Delegatee'); + if (!canProceed) return; // Chain was switched, user needs to click again + } catch (error) { + setError(error instanceof Error ? error.message : 'Failed to switch network'); + return; + } + setRemovingDelegatee(addressToRemove); try { - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); + const signer = await getSigner(); + const client = getClient({ signer }); - await client.removeDelegatee({ + const result = await client.removeDelegatee({ appId: Number(appId), delegateeAddress: addressToRemove, }); - refetchBlockchainData(); + // Wait for transaction to be confirmed on-chain + if (result.txHash && signer.provider) { + await signer.provider.waitForTransaction(result.txHash); + } + + // Update local state immediately (RPC caching makes refetch unreliable) + setLocalDelegatees((prev) => prev.filter((addr) => addr !== addressToRemove)); + onDelegateeRemoved?.(addressToRemove); } catch (error) { console.error('Failed to remove delegatee:', error); const message = error instanceof Error ? error.message : String(error); @@ -115,9 +131,9 @@ export function ManageDelegateesForm({ } else { setError(`Failed to remove delegatee: ${message}`); } - } finally { - setRemovingDelegatee(null); } + + setRemovingDelegatee(null); }; const handleOpenGenerateDialog = () => { @@ -129,27 +145,54 @@ export function ManageDelegateesForm({ setIsGenerateDialogOpen(true); }; - const addDelegateeAddress = async (address: string) => { - if (!appId) return; + const addDelegateeAddress = async (address: string): Promise => { + if (!appId) return false; // Check if delegatee is already in the current app's delegatee list - if (existingDelegatees.includes(address)) { + if (localDelegatees.includes(address)) { setError(`Delegatee ${address} is already registered to app ${appId}`); - throw new Error('DelegateeAlreadyRegistered'); + return false; } - // Add the specific delegatee address as a payee - await addPayee(address); + // Ensure user is on Base Sepolia + try { + const canProceed = await ensureChain('Add Delegatee'); + if (!canProceed) return false; // Chain was switched, user needs to click again + } catch (error) { + setError(error instanceof Error ? error.message : 'Failed to switch network'); + return false; + } - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); + // TODO: Add delegatees batch endpoint in the contracts, make sure to addPayees as well - await client.addDelegatee({ - appId: Number(appId), - delegateeAddress: address, - }); + try { + const signer = await getSigner(); + const client = getClient({ signer }); - refetchBlockchainData(); + const result = await client.addDelegatee({ + appId: Number(appId), + delegateeAddress: address, + }); + + // Wait for transaction to be confirmed on-chain + if (result.txHash && signer.provider) { + await signer.provider.waitForTransaction(result.txHash); + } + + // Update local state immediately (RPC caching makes refetch unreliable) + setLocalDelegatees((prev) => [...prev, address]); + onDelegateeAdded?.(address); + return true; + } catch (error) { + console.error('Failed to add delegatee:', error); + const message = error instanceof Error ? error.message : String(error); + if (message.includes('user rejected')) { + setError('Transaction rejected.'); + } else { + setError(`Failed to add delegatee: ${message}`); + } + return false; + } }; const handleConfirmAndAddWallet = async () => { @@ -157,25 +200,16 @@ export function ManageDelegateesForm({ setAddingGeneratedWallet(true); - try { - await addDelegateeAddress(generatedWallet.address); + const success = await addDelegateeAddress(generatedWallet.address); + setAddingGeneratedWallet(false); + + if (success) { // Success - close dialog and clear wallet setGeneratedWallet(null); setIsGenerateDialogOpen(false); - } catch (error) { - console.error('Failed to add generated delegatee:', error); - const message = error instanceof Error ? error.message : String(error); - if (message.includes('user rejected')) { - setError('Transaction rejected.'); - } else if (message.includes('DelegateeAlreadyRegistered')) { - // Error already set in addDelegateeAddress - } else { - setError(`Failed to add delegatee: ${message}`); - } - } finally { - setAddingGeneratedWallet(false); } + // If !success, error is already set by addDelegateeAddress, dialog stays open }; const handleCancelGeneration = () => { @@ -185,6 +219,9 @@ export function ManageDelegateesForm({ return (
+ {/* Info Message */} + {infoMessage && } + {/* Error Message */} {error && } @@ -267,14 +304,14 @@ export function ManageDelegateesForm({ Current Delegatees

- {existingDelegatees.length === 0 + {localDelegatees.length === 0 ? 'No delegatees configured yet.' - : `${existingDelegatees.length} delegatee${existingDelegatees.length === 1 ? '' : 's'} configured`} + : `${localDelegatees.length} delegatee${localDelegatees.length === 1 ? '' : 's'} configured`}

- {existingDelegatees.map((delegatee, index) => ( + {localDelegatees.map((delegatee, index) => (
))} - {existingDelegatees.length === 0 && ( + {localDelegatees.length === 0 && (

No delegatees configured yet.

diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/SetActiveVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/SetActiveVersionForm.tsx new file mode 100644 index 000000000..2ca1d94e8 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/forms/SetActiveVersionForm.tsx @@ -0,0 +1,150 @@ +import { useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; +import { useBlockchainAppVersions } from '@/hooks/useBlockchainAppVersions'; +import { Button } from '@/components/shared/ui/button'; +import { StatusMessage } from '@/components/shared/ui/statusMessage'; +import Loading from '@/components/shared/ui/Loading'; +import { theme, fonts } from '@/lib/themeClasses'; +import { CheckCircle } from 'lucide-react'; + +interface SetActiveVersionFormProps { + currentActiveVersion?: number | null; + onSuccess: () => void; +} + +export function SetActiveVersionForm({ + currentActiveVersion, + onSuccess, +}: SetActiveVersionFormProps) { + const { appId } = useParams<{ appId: string }>(); + const [selectedVersion, setSelectedVersion] = useState( + currentActiveVersion ?? null, + ); + const [error, setError] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + + const { + data: versionData, + isLoading, + error: fetchError, + } = useBlockchainAppVersions(Number(appId)); + const [setAppActiveVersion] = vincentApiClient.useSetAppActiveVersionMutation(); + + const handleSubmit = async () => { + if (!selectedVersion || !appId) return; + + setIsSubmitting(true); + setError(null); + + try { + await setAppActiveVersion({ + appId: Number(appId), + appSetActiveVersion: { activeVersion: selectedVersion }, + }).unwrap(); + + onSuccess(); + } catch (err: any) { + console.error('Failed to set active version:', err); + setError(err.data?.message || err.message || 'Failed to set active version'); + } finally { + setIsSubmitting(false); + } + }; + + if (isLoading) { + return ; + } + + if (fetchError) { + return ; + } + + // Filter to only show enabled versions (published on-chain) + const enabledVersions = versionData?.versions.filter((v) => v.enabled) || []; + + if (enabledVersions.length === 0) { + return ( +

+

+ No published versions available. You need to publish at least one version before setting + it as active. +

+
+ ); + } + + return ( +
+ {error && } + +
+ +

+ Choose which version users will see when they install your app. Only published (enabled) + versions can be set as active. +

+
+ {enabledVersions.map((version) => { + const versionNumber = versionData?.versions.indexOf(version) ?? 0; + const actualVersion = versionNumber + 1; + const isCurrentActive = actualVersion === currentActiveVersion; + const isSelected = actualVersion === selectedVersion; + + return ( + + ); + })} +
+
+ +
+ +
+
+ ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppDetailsView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppDetailsView.tsx index e1faba48b..ea62b3192 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppDetailsView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppDetailsView.tsx @@ -4,12 +4,9 @@ import { App as ContractApp } from '@lit-protocol/vincent-contracts-sdk'; import { AppDetail } from '@/components/developer-dashboard/ui/AppDetail'; import { Logo } from '@/components/shared/ui/Logo'; import { AppPublishedButtons } from '../wrappers/ui/AppPublishedButtons'; -import { AppUnpublishedButtons } from '../wrappers/ui/AppUnpublishedButtons'; -import { UndeleteAppButton } from '../wrappers/ui/UndeleteAppButton'; -import { Share, Copy, AlertCircle } from 'lucide-react'; -import { ConnectPageModal } from '../../ui/ConnectPageModal'; +import { Copy, AlertCircle } from 'lucide-react'; import { Link } from 'react-router-dom'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { motion } from 'framer-motion'; interface AppDetailsViewProps { @@ -27,11 +24,9 @@ export function AppDetailsView({ blockchainAppLoading, refetchBlockchainData, }: AppDetailsViewProps) { - const [isConnectModalOpen, setIsConnectModalOpen] = useState(false); const [copySuccess, setCopySuccess] = useState(false); const [showContent, setShowContent] = useState(false); const isPublished = blockchainAppData !== null; - const isAppDeletedRegistry = selectedApp.isDeleted; useEffect(() => { setShowContent(true); @@ -47,9 +42,7 @@ export function AppDetailsView({ } }; - const delegateeAddresses = isPublished - ? blockchainAppData.delegateeAddresses - : selectedApp.delegateeAddresses; + const delegateeAddresses = blockchainAppData?.delegateeAddresses || []; return ( <> @@ -103,14 +96,6 @@ export function AppDetailsView({

{selectedApp.name}

-
{/* App ID with Copy Button */} @@ -180,17 +165,13 @@ export function AppDetailsView({

- {isPublished ? ( + {blockchainAppData && ( - ) : isAppDeletedRegistry ? ( - - ) : ( - )}
@@ -217,7 +198,7 @@ export function AppDetailsView({ - {selectedApp.activeVersion || 'Unpublished'} + {selectedApp.activeVersion || 'Unregistered'} @@ -229,37 +210,20 @@ export function AppDetailsView({ )} - {selectedApp.appUserUrl && ( - + {selectedApp.appUrl && ( + - {selectedApp.appUserUrl} + {selectedApp.appUrl} )} - {selectedApp.redirectUris && selectedApp.redirectUris.length > 0 && ( - -
- {selectedApp.redirectUris.map((uri) => ( -
- - {uri} - -
- ))} -
-
- )} - {delegateeAddresses && delegateeAddresses.length > 0 && (
@@ -309,13 +273,6 @@ export function AppDetailsView({
- - setIsConnectModalOpen(false)} - appId={selectedApp.appId} - redirectUris={selectedApp.redirectUris || []} - /> ); } diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionAbilitiesDisplay.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionAbilitiesDisplay.tsx deleted file mode 100644 index 589fa4639..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionAbilitiesDisplay.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Wrench, Package } from 'lucide-react'; - -import { AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -interface AppVersionAbilitiesDisplayProps { - abilities: AppVersionAbility[]; -} - -export function AppVersionAbilitiesDisplay({ abilities }: AppVersionAbilitiesDisplayProps) { - // Filter out deleted abilities - const activeAbilities = abilities.filter((ability) => !ability.isDeleted); - - if (activeAbilities.length === 0) { - return ( -
- -

- No abilities assigned to this app version yet. -

-
- ); - } - - return ( -
- {activeAbilities.map((ability: AppVersionAbility) => { - const npmUrl = `https://www.npmjs.com/package/${ability.abilityPackageName}/v/${ability.abilityVersion}`; - - return ( -
-
-
- -
-
-
-

- {ability.abilityPackageName} -

- - NPM - -
-

- Version {ability.abilityVersion} -

-

- Added: {new Date(ability.createdAt).toLocaleDateString()} -

-
-
-
- ); - })} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetailView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetailView.tsx deleted file mode 100644 index 3d300c5ae..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetailView.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useState, useEffect } from 'react'; -import { Power, PowerOff, AlertCircle } from 'lucide-react'; -import { motion } from 'framer-motion'; -import { - App as ContractApp, - AppVersion as ContractAppVersion, -} from '@lit-protocol/vincent-contracts-sdk'; - -import { VersionDetails } from '@/components/developer-dashboard/app/views/AppVersionDetails'; -import { AppVersionPublishedButtons } from '../wrappers/ui/AppVersionPublishedButtons'; -import { AppVersionUnpublishedButtons } from '../wrappers/ui/AppVersionUnpublishedButtons'; -import { AppVersionDeletedButtons } from '../wrappers/ui/AppVersionDeletedButtons'; -import { App, AppVersion, AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; -interface AppVersionDetailViewProps { - app: App; - versionData: AppVersion; - versionAbilities: AppVersionAbility[]; - blockchainAppVersion: ContractAppVersion | null; - blockchainAppData: ContractApp | null; - refetchBlockchainAppVersionData: () => void; - onOpenMutation: (mutationType: string) => void; -} - -export function AppVersionDetailView({ - app, - versionData, - versionAbilities, - blockchainAppVersion, - blockchainAppData, - refetchBlockchainAppVersionData, - onOpenMutation, -}: AppVersionDetailViewProps) { - const [showContent, setShowContent] = useState(false); - const isAppPublished = blockchainAppData !== null; - const isVersionPublished = blockchainAppVersion !== null; - const isAppDeletedOnChain = blockchainAppData?.isDeleted; - const isAppVersionDeletedRegistry = versionData.isDeleted; - const isVersionEnabledRegistry = versionData.enabled; - - useEffect(() => { - setShowContent(true); - }, []); - - return ( - - {/* Header Card */} -
- {/* Status Banner */} - {!isVersionPublished && app.activeVersion === versionData.version && ( -
- - - This is your app's active version but it's not published on-chain. Users cannot grant - permissions until you publish this version. - -
- )} - {!app.activeVersion && versionData.version === 1 && ( -
- - - App version 1 needs to be published. Until it is, users cannot grant permissions. - -
- )} - -
-
-
-
-

- Version {versionData.version} -

- {app.activeVersion === versionData.version && ( - - Active - - )} -
-

- View and manage this version of your application -

-
- - {/* Registry Status Badge */} -
- - {isVersionEnabledRegistry ? ( - <> - - Enabled - - ) : ( - <> - - Disabled - - )} - -
-
-
-
- - {/* Actions Card */} -
-
-

- Actions -

-

- Manage this version -

-
-
- {isVersionPublished && !isAppDeletedOnChain && !isAppVersionDeletedRegistry ? ( - - ) : isVersionPublished && isAppDeletedOnChain ? ( -
-
- -

- This app is deleted on-chain. Please undelete the app to enable version - modification. -

-
-
- ) : isAppVersionDeletedRegistry ? ( - - ) : ( - - )} -
-
- - {/* Version Details */} - -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetails.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetails.tsx deleted file mode 100644 index f3a79376b..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionDetails.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/components/shared/ui/card'; -import { AppVersion, AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { AppVersionAbilitiesDisplay } from '@/components/developer-dashboard/app/views/AppVersionAbilitiesDisplay.tsx'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -interface VersionDetailsProps { - version: number; - appName?: string; - versionData: AppVersion; - abilities: AppVersionAbility[]; -} - -export function VersionDetails({ version, versionData, abilities }: VersionDetailsProps) { - if (!versionData) { - return ( -
- - - Version Not Found - - -

Version {version} not found for this app.

-
-
-
- ); - } - - return ( -
- {versionData.changes && ( - - - Version Changes - - What's new in this version - - - -
-

- {versionData.changes} -

-
-
-
- )} - - - - Associated Abilities - - Abilities included in this version - - - - - - -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionsListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionsListView.tsx deleted file mode 100644 index 05f293746..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppVersionsListView.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useState, useEffect, useMemo } from 'react'; -import { Package, Plus, ArrowUpDown } from 'lucide-react'; -import { motion } from 'framer-motion'; -import { AppVersion as ContractAppVersion } from '@lit-protocol/vincent-contracts-sdk'; - -import { AppVersion } from '@/types/developer-dashboard/appTypes'; -import { UndeleteAppVersionButton } from '../wrappers'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; -import { VersionCard } from '@/components/developer-dashboard/ui/VersionCard'; - -type SortOption = 'published' | 'enabled'; - -interface AppVersionsListViewProps { - appName: string; - versions: AppVersion[]; - deletedVersions?: AppVersion[]; - activeVersion?: number; - onVersionClick: (version: number) => void; - onCreateVersion?: () => void; - blockchainVersions: Record; - sortBy: SortOption; - onSortChange: (sortBy: SortOption) => void; -} - -export function AppVersionsListView({ - appName, - versions, - deletedVersions, - activeVersion, - onVersionClick, - onCreateVersion, - blockchainVersions, - sortBy, - onSortChange, -}: AppVersionsListViewProps) { - const [showContent, setShowContent] = useState(false); - - useEffect(() => { - setShowContent(true); - }, []); - - // Separate versions based on current sort option - const { primaryVersions, secondaryVersions, primaryLabel, secondaryLabel } = useMemo(() => { - if (sortBy === 'published') { - const publishedVersions = versions.filter((v) => blockchainVersions[v.version] !== null); - const unpublishedVersions = versions.filter((v) => blockchainVersions[v.version] === null); - return { - primaryVersions: publishedVersions, - secondaryVersions: unpublishedVersions, - primaryLabel: 'Published Versions', - secondaryLabel: 'Unpublished Versions', - }; - } else { - const enabledVersions = versions.filter((v) => v.enabled); - const disabledVersions = versions.filter((v) => !v.enabled); - return { - primaryVersions: enabledVersions, - secondaryVersions: disabledVersions, - primaryLabel: 'Enabled Versions', - secondaryLabel: 'Disabled Versions', - }; - } - }, [versions, blockchainVersions, sortBy]); - - const renderVersionCard = (version: AppVersion) => ( - onVersionClick(version.version)} - /> - ); - - return ( - - {/* Empty State */} - {versions.length === 0 ? ( -
-
-
-
-
- -
-

- No Versions Yet -

-

- Create your first version for {appName} to get started. -

-
-
-
-
- ) : ( - <> - {/* Primary Versions Section */} - {primaryVersions.length > 0 && ( -
-
-

- {primaryLabel} -

- -
-
- {primaryVersions.map(renderVersionCard)} -
-
- )} - - {/* Secondary Versions Section */} - {secondaryVersions.length > 0 && ( -
-

- {secondaryLabel} -

-
- {secondaryVersions.map(renderVersionCard)} -
-
- )} - - {/* Create Version CTA */} - {onCreateVersion && ( -
-
-

- Ready to create another version? -

- -
-
- )} - - )} - - {/* Deleted Versions Section */} - {deletedVersions && deletedVersions.length > 0 && ( -
-

- Deleted Versions -

-
- {deletedVersions.map((version) => ( -
-
-
-
-

- Version {version.version} -

- - DELETED - - {version.version === activeVersion && ( - - Active - - )} -
-
- -
-
-

- Created: {new Date(version.createdAt).toLocaleDateString()} -

-
-
- ))} -
-
- )} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppsListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppsListView.tsx index 25fded8c9..91b586742 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppsListView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/AppsListView.tsx @@ -5,11 +5,17 @@ import { motion } from 'framer-motion'; import { App } from '@/types/developer-dashboard/appTypes'; import { AppCard } from '@/components/developer-dashboard/ui/AppCard'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; + +interface AppWithOwnership extends App { + isOwnedOnChain?: boolean; + isInRegistry?: boolean; + onChainOwner?: string; +} interface AppsListViewProps { - apps: App[]; - deletedApps: App[]; + apps: AppWithOwnership[]; + deletedApps: AppWithOwnership[]; } export function AppsListView({ apps, deletedApps }: AppsListViewProps) { @@ -20,9 +26,9 @@ export function AppsListView({ apps, deletedApps }: AppsListViewProps) { setShowContent(true); }, []); - // Separate apps into published and unpublished - const publishedApps = apps.filter((app) => app.activeVersion); - const unpublishedApps = apps.filter((app) => !app.activeVersion); + // Separate apps into registered (on-chain + in registry) and unregistered (on-chain only) + const registeredApps = apps.filter((app) => app.isInRegistry); + const unregisteredApps = apps.filter((app) => !app.isInRegistry); const renderAppCard = (app: App) => ( ) : ( <> - {/* Published Apps Section */} - {publishedApps.length > 0 && ( + {/* Registered Apps Section */} + {registeredApps.length > 0 && (

- Published Apps + Registered Apps

- {publishedApps.map(renderAppCard)} + {registeredApps.map(renderAppCard)}
)} - {/* Unpublished Apps Section */} - {unpublishedApps.length > 0 && ( + {/* Unregistered Apps Section */} + {unregisteredApps.length > 0 && (

- Unpublished Apps + Unregistered Apps

- {unpublishedApps.map(renderAppCard)} + {unregisteredApps.map(renderAppCard)}
)} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/ManageAppVersionAbilities.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/ManageAppVersionAbilities.tsx deleted file mode 100644 index 089942f53..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/views/ManageAppVersionAbilities.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import { useState } from 'react'; -import { Edit, X, Package } from 'lucide-react'; - -import { Button } from '@/components/shared/ui/button'; -import { - EditAppVersionAbilityButton, - DeleteAppVersionAbilityButton, - UndeleteAppVersionAbilityButton, -} from '../wrappers'; -import { AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -interface ManageAppVersionAbilitiesProps { - abilities: AppVersionAbility[]; - deletedAbilities: AppVersionAbility[]; - appId: number; - versionId: number; -} - -export function ManageAppVersionAbilities({ - abilities, - deletedAbilities, - appId, - versionId, -}: ManageAppVersionAbilitiesProps) { - const [editingAbility, setEditingAbility] = useState(null); - - const handleEditAbility = (abilityPackageName: string) => { - setEditingAbility(abilityPackageName); - }; - - const handleCancelEdit = () => { - setEditingAbility(null); - }; - - const handleEditSuccess = () => { - setEditingAbility(null); - }; - - if (abilities.length === 0 && (!deletedAbilities || deletedAbilities.length === 0)) { - return ( -
- -

- No abilities assigned to this app version yet. -

-
- ); - } - - return ( -
- {/* Active Abilities Section */} - {abilities.length === 0 ? ( -
- -

- No active abilities assigned to this app version yet. -

-
- ) : ( -
- {abilities.map((ability) => ( -
- {editingAbility === ability.abilityPackageName ? ( - // Edit mode - render wrapper -
-
-

- Edit {ability.abilityPackageName} -

- -
- -
- ) : ( - // Normal display mode -
-
-

- {ability.abilityPackageName} -

-

- Version: {ability.abilityVersion} -

- {ability.hiddenSupportedPolicies && - ability.hiddenSupportedPolicies.length > 0 && ( -

- Hidden policies: {ability.hiddenSupportedPolicies.join(', ')} -

- )} -

- Added: {new Date(ability.createdAt).toLocaleDateString()} -

-
-
- - -
-
- )} -
- ))} -
- )} - - {/* Deleted Abilities Section */} - {deletedAbilities && deletedAbilities.length > 0 && ( -
-
-

- Deleted Abilities -

-
- {deletedAbilities.map((ability) => ( -
-
-
-
-

- {ability.abilityPackageName} -

- - DELETED - -
-

- Version: {ability.abilityVersion} -

- {ability.hiddenSupportedPolicies && - ability.hiddenSupportedPolicies.length > 0 && ( -

- Hidden policies: {ability.hiddenSupportedPolicies.join(', ')} -

- )} -

- Added: {new Date(ability.createdAt).toLocaleDateString()} -

-
-
- -
-
-
- ))} -
-
-
- )} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppOverviewWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppOverviewWrapper.tsx index ad0fa6794..f0acc11a0 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppOverviewWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppOverviewWrapper.tsx @@ -2,40 +2,30 @@ import { useState, useEffect } from 'react'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import * as Sentry from '@sentry/react'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; -import { addPayee } from '@/utils/user-dashboard/addPayee'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { useOnChainAppOwnership } from '@/hooks/developer-dashboard/app/useOnChainAppOwnership'; import { AppDetailsView } from '../views/AppDetailsView'; import { EditAppForm } from '../forms/EditAppForm'; -import { EditPublishedAppForm } from '../forms/EditPublishedAppForm'; -import { CreateAppVersionForm } from '../forms/CreateAppVersionForm'; import { DeleteAppForm } from '../forms/DeleteAppForm'; import { ManageDelegateesForm } from '../forms/ManageDelegateesForm'; +import { SetActiveVersionForm } from '../forms/SetActiveVersionForm'; import Loading from '@/components/shared/ui/Loading'; import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { useBlockchainAppData } from '@/hooks/useBlockchainAppData'; import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; import { EditAppFormData } from '../forms/EditAppForm'; -import { EditPublishedAppFormData } from '../forms/EditPublishedAppForm'; -import { CreateAppVersionFormData } from '../forms/CreateAppVersionForm'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/shared/ui/dialog'; +import { theme, fonts } from '@/lib/themeClasses'; type ViewType = | 'details' | 'edit-app' | 'edit-published-app' - | 'create-app-version' | 'delete-app' - | 'manage-delegatees'; + | 'manage-delegatees' + | 'set-active-version'; export function AppOverviewWrapper() { const { appId } = useParams<{ appId: string }>(); @@ -43,19 +33,26 @@ export function AppOverviewWrapper() { const [searchParams, setSearchParams] = useSearchParams(); const [currentView, setCurrentView] = useState('details'); const [isSubmitting, setIsSubmitting] = useState(false); - const { authInfo, sessionSigs } = useReadAuthInfo(); + const { getSigner } = useWagmiSigner(); + const { ensureChain, infoMessage } = useEnsureChain(); - const { - data: app, - isLoading: appLoading, - isError: appError, - } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }); + // Define handlers early so they're available in early returns + const handleCloseModal = () => { + setCurrentView('details'); + }; + // Check on-chain ownership first (source of truth) const { - data: appVersions, - isLoading: appVersionsLoading, - isError: appVersionsError, - } = vincentApiClient.useGetAppVersionsQuery({ appId: Number(appId) }); + isOwner, + existsOnChain, + isChecking: ownershipChecking, + error: ownershipError, + } = useOnChainAppOwnership(Number(appId)); + + const { data: app, isLoading: appLoading } = vincentApiClient.useGetAppQuery( + { appId: Number(appId) }, + { skip: !existsOnChain }, + ); // Fetching on-chain data const { @@ -67,12 +64,10 @@ export function AppOverviewWrapper() { // Mutations const [editApp] = vincentApiClient.useEditAppMutation(); - const [createAppVersion] = vincentApiClient.useCreateAppVersionMutation(); - const [deleteApp] = vincentApiClient.useDeleteAppMutation(); // Check for action query param when data is ready useEffect(() => { - if (!app || appLoading || appVersionsLoading || blockchainAppLoading) { + if (!app || appLoading || blockchainAppLoading) { return; } @@ -86,41 +81,130 @@ export function AppOverviewWrapper() { newParams.delete('action'); setSearchParams(newParams, { replace: true }); } - }, [searchParams, setSearchParams, app, appLoading, appVersionsLoading, blockchainAppLoading]); - - const isLoadingEssentialData = - appLoading || appVersionsLoading || blockchainAppLoading || !app || !appVersions; - - if (isLoadingEssentialData) return ; - - // Combined error states - if (appError || blockchainAppError) - return ; - if (appVersionsError) return ; + }, [searchParams, setSearchParams, app, appLoading, blockchainAppLoading]); + + // Check on-chain ownership first + if (ownershipChecking) return ; + + if (ownershipError) { + return ; + } + + // App doesn't exist on-chain + if (existsOnChain === false) { + return ( + <> + navigate('/developer/apps') }]} /> + + + ); + } + + // User doesn't own the app on-chain + if (isOwner === false) { + return ( + <> + navigate('/developer/apps') }]} /> + + + ); + } + + // App exists on-chain and user owns it, but not in registry yet + // This indicates an error occurred during the registration process + if (existsOnChain && isOwner && !app && !appLoading) { + return ( + <> + navigate('/developer/apps') }, + { label: `App ${appId}` }, + ]} + /> +
+
+ {/* Error Icon */} +
+
+ + + +
+
+ + {/* Title */} +

+ App Registration Error +

+ + {/* Description */} +

+ An error occurred during the app registration process for App ID {appId}. Your app + exists on-chain but was not properly synced to the Vincent Registry. Please contact + our team for assistance in resolving this issue. +

+ + {/* Contact Button */} + { + e.currentTarget.style.backgroundColor = theme.brandOrangeDarker; + }} + onMouseLeave={(e) => { + e.currentTarget.style.backgroundColor = theme.brandOrange; + }} + > + Contact Support + +
+
+ + ); + } + + // If app data is still loading, show loading + if (appLoading || blockchainAppLoading) { + return ; + } + + // Handle errors only if app should exist but failed to load (not 404) + // 404 is handled above in the "create-in-registry" flow + if (app && blockchainAppError) { + return ; + } + + // If we reach here without app data, something unexpected happened + if (!app) { + return ; + } const handleOpenMutation = (mutationType: string) => { if (mutationType === 'versions') { navigate(`/developer/apps/appId/${appId}/versions`); + } else if (mutationType === 'create-app-version') { + navigate(`/developer/apps/appId/${appId}/new-version`); } else { setCurrentView(mutationType as ViewType); } }; - const handleEditAppSubmit = async (data: EditAppFormData | EditPublishedAppFormData) => { + const handleEditAppSubmit = async (data: EditAppFormData) => { setIsSubmitting(true); try { - // identify NEW delegatee addresses that weren't in the original app - const originalDelegateeAddresses = app?.delegateeAddresses || []; - const newDelegateeAddresses = ( - 'delegateeAddresses' in data ? data.delegateeAddresses || [] : [] - ).filter((address: string) => !originalDelegateeAddresses.includes(address)); - - await Promise.all( - newDelegateeAddresses.map(async (address: string) => { - await addPayee(address); - }), - ); - await editApp({ appId: Number(appId), appEdit: data, @@ -139,72 +223,30 @@ export function AppOverviewWrapper() { } }; - const handleCreateAppVersionSubmit = async (data: CreateAppVersionFormData) => { - setIsSubmitting(true); - try { - const result = await createAppVersion({ - appId: Number(appId), - appVersionCreate: data, - }).unwrap(); - - // Success - wait a moment to show success message, then navigate to abilities page - setTimeout(() => { - navigate(`/developer/apps/appId/${appId}/version/${result.version}/abilities`); - setIsSubmitting(false); - }, 1500); - } catch (error) { - console.error('Failed to create app version:', error); - setIsSubmitting(false); - throw error; - } - }; - const handleDeleteAppSubmit = async () => { + // Ensure user is on Base Sepolia before starting + const canProceed = await ensureChain('Delete App'); + if (!canProceed) return; // Chain was switched, user needs to click again + setIsSubmitting(true); try { - const isPublished = blockchainAppData !== null; - - // Step 1: Delete in registry (always do this) - await deleteApp({ appId: Number(appId) }).unwrap(); - - // Step 2: If published on-chain, also delete on-chain - if (isPublished) { - try { - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); - - await client.deleteApp({ - appId: Number(appId), - }); - } catch (onChainError) { - console.error('Failed to delete app on-chain:', onChainError); - // If on-chain deletion fails, log to Sentry but still navigate away - // The mismatch resolution UI will handle fixing the inconsistent state - Sentry.captureException(onChainError, { - extra: { - context: 'AppOverviewWrapper.handleDeleteAppSubmit', - appId: appId, - registryDeleted: true, - onChainDeleted: false, - userPkp: authInfo?.userPKP?.ethAddress, - }, - }); - } - } + // Delete on-chain only - on-chain is the source of truth + const signer = await getSigner(); + const client = getClient({ signer }); + + await client.deleteApp({ + appId: Number(appId), + }); // Success - navigate back to apps list navigate('/developer/apps'); - } catch (error) { + } catch (error: any) { console.error('Failed to delete app:', error); setIsSubmitting(false); throw error; } }; - const handleCloseModal = () => { - setCurrentView('details'); - }; - return ( <> - {blockchainAppData !== null ? ( - - ) : ( - - )} - - - - {/* Create App Version Modal */} - - - - - New Version - - Create a new version of your app - - + @@ -273,6 +286,11 @@ export function AppOverviewWrapper() { Delete App + {infoMessage && ( +
+ +
+ )} - + + + + {/* Set Active Version Modal */} + + + + + Set Active Version + + + diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionAbilitiesWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionAbilitiesWrapper.tsx deleted file mode 100644 index e0d905377..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionAbilitiesWrapper.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import { useEffect, useState, useMemo } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { CheckCircle2 } from 'lucide-react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; - -import { AppVersionAbility, Ability } from '@/types/developer-dashboard/appTypes'; -import { ManageAppVersionAbilities } from '../views/ManageAppVersionAbilities.tsx'; -import { CreateAppVersionAbilitiesForm } from '../forms/CreateAppVersionAbilitiesForm.tsx'; -import { PublishAppVersionWrapper } from './PublishAppVersionWrapper'; -import Loading from '@/components/shared/ui/Loading'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; -import { useBlockchainAppData } from '@/hooks/useBlockchainAppData'; - -export function AppVersionAbilitiesWrapper() { - const { appId, versionId } = useParams<{ appId: string; versionId: string }>(); - const navigate = useNavigate(); - const [showSuccess, setShowSuccess] = useState(false); - - // Fetching - const { - data: app, - isLoading: appLoading, - isError: appError, - } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }); - - const { blockchainAppData, blockchainAppLoading } = useBlockchainAppData(Number(appId)); - - const { - data: versionData, - isLoading: versionLoading, - isError: versionError, - } = vincentApiClient.useGetAppVersionQuery({ appId: Number(appId), version: Number(versionId) }); - - const { - data: versionAbilities, - isLoading: versionAbilitiesLoading, - isError: versionAbilitiesError, - } = vincentApiClient.useListAppVersionAbilitiesQuery({ - appId: Number(appId), - version: Number(versionId), - }); - - // Separate active and deleted abilities - const { activeAbilities, deletedAbilities } = useMemo(() => { - if (!versionAbilities?.length) return { activeAbilities: [], deletedAbilities: [] }; - const activeAbilities = versionAbilities.filter( - (ability: AppVersionAbility) => !ability.isDeleted, - ); - const deletedAbilities = versionAbilities.filter( - (ability: AppVersionAbility) => ability.isDeleted, - ); - return { activeAbilities, deletedAbilities }; - }, [versionAbilities]); - - const { - data: allAbilities = [], - isLoading: abilitiesLoading, - isError: abilitiesError, - } = vincentApiClient.useListAllAbilitiesQuery(); - - // Mutation - const [createAppVersionAbility, { isLoading, isSuccess, isError, data }] = - vincentApiClient.useCreateAppVersionAbilityMutation(); - - // Effect - useEffect(() => { - if (!isSuccess || !data) return; - setShowSuccess(true); - - const timer = setTimeout(() => { - setShowSuccess(false); - }, 2000); - - return () => clearTimeout(timer); - }, [isSuccess, data]); - - // Show loading while data is loading OR while checking authorization - if ( - appLoading || - versionLoading || - versionAbilitiesLoading || - abilitiesLoading || - blockchainAppLoading - ) - return ; - - // Handle errors - if (appError) return ; - if (versionError) return ; - if (versionAbilitiesError) - return ; - if (abilitiesError) return ; - if (!app) return ; - if (!versionData) - return ; - - const existingAbilityNames = - versionAbilities?.map((ability: AppVersionAbility) => ability.abilityPackageName) || []; - - const handleAbilityAdd = async (ability: Ability) => { - await createAppVersionAbility({ - appId: Number(appId), - appVersion: Number(versionId), - abilityPackageName: ability.packageName, - appVersionAbilityCreate: { - abilityVersion: ability.activeVersion, - hiddenSupportedPolicies: [], - }, - }); - }; - - return ( - <> - navigate('/developer/apps') }, - { label: app.name, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, - { - label: `Version ${versionData.version}`, - onClick: () => navigate(`/developer/apps/appId/${appId}/version/${versionId}`), - }, - { label: 'Abilities' }, - ]} - /> - -
- {/* Add Abilities Form */} - - - {/* Status messages */} - {isLoading && } - {showSuccess && } - {isError && } - - {/* Current Abilities Section */} -
-
-

- Current Abilities -

-

- Abilities currently associated with this version. After adding and editing your - abilities, you can publish your app version to be accessible by users. -

-
-
- -
-
- - {activeAbilities.length > 0 && ( -
-
-
- -
-

- Ready to Publish -

-

- Your app version has {activeAbilities.length} abilit - {activeAbilities.length === 1 ? 'y' : 'ies'} configured and is ready to publish. -

-
-
- -
-
- )} -
- - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailWrapper.tsx deleted file mode 100644 index 3397822f2..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailWrapper.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; - -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import Loading from '@/components/shared/ui/Loading'; -import { AppVersionDetailView } from '@/components/developer-dashboard/app/views/AppVersionDetailView'; -import { useBlockchainAppData } from '@/hooks/useBlockchainAppData'; -import { useBlockchainAppVersionData } from '@/hooks/useBlockchainAppVersionData'; -import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; -import { EditAppVersionForm, type EditAppVersionFormData } from '../forms/EditAppVersionForm'; -import { DeleteAppVersionForm, type DeleteAppVersionFormData } from '../forms/DeleteAppVersionForm'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; - -type ViewType = 'details' | 'edit-version' | 'delete-version'; - -export function AppVersionDetailWrapper() { - const { appId, versionId } = useParams<{ appId: string; versionId: string }>(); - const navigate = useNavigate(); - const [currentView, setCurrentView] = useState('details'); - const [isSubmitting, setIsSubmitting] = useState(false); - - // Fetch app data from API - const { - data: app, - isLoading: appLoading, - isError: appError, - } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }); - - // Fetch app versions from API - const { - data: appVersions, - isLoading: versionsLoading, - isError: versionsError, - } = vincentApiClient.useGetAppVersionsQuery({ appId: Number(appId) }); - - // Fetch specific version data from API - const { - data: versionData, - isLoading: versionLoading, - isError: versionError, - } = vincentApiClient.useGetAppVersionQuery({ appId: Number(appId), version: Number(versionId) }); - - // Fetch version abilities from API - const { - data: versionAbilities, - isLoading: versionAbilitiesLoading, - isError: versionAbilitiesError, - } = vincentApiClient.useListAppVersionAbilitiesQuery({ - appId: Number(appId), - version: Number(versionId), - }); - - const { blockchainAppData, blockchainAppError, blockchainAppLoading } = useBlockchainAppData( - Number(appId), - ); - - // Fetch blockchain app and version data - const { - blockchainAppVersion, - blockchainAppVersionError, - blockchainAppVersionLoading, - refetch: refetchBlockchainAppVersionData, - } = useBlockchainAppVersionData(Number(appId), Number(versionId)); - - // Mutations - const [editAppVersion] = vincentApiClient.useEditAppVersionMutation(); - const [deleteAppVersion] = vincentApiClient.useDeleteAppVersionMutation(); - const [editApp] = vincentApiClient.useEditAppMutation(); - - // Loading states first - if ( - appLoading || - versionsLoading || - versionLoading || - versionAbilitiesLoading || - blockchainAppLoading || - blockchainAppVersionLoading - ) - return ; - - // Combined error states - if (appError) return ; - if (versionsError) return ; - if (blockchainAppError) - return ; - if (blockchainAppVersionError) - return ; - if (versionError) return ; - if (versionAbilitiesError) - return ; - if (!app) return ; - if (!versionData) - return ; - - const handleOpenMutation = (mutationType: string) => { - setCurrentView(mutationType as ViewType); - }; - - const handleEditAppVersionSubmit = async (data: EditAppVersionFormData) => { - setIsSubmitting(true); - try { - await editAppVersion({ - appId: Number(appId), - version: Number(versionId), - appVersionEdit: data, - }).unwrap(); - - // Success - wait a moment to show success message, then close modal - setTimeout(() => { - setCurrentView('details'); - setIsSubmitting(false); - }, 1500); - } catch (error) { - console.error('Failed to update app version:', error); - setIsSubmitting(false); - throw error; - } - }; - - const handleDeleteAppVersionSubmit = async (formData: DeleteAppVersionFormData) => { - setIsSubmitting(true); - try { - if (app.activeVersion === Number(versionId) && formData.activeVersion) { - await editApp({ - appId: app.appId, - appEdit: { - activeVersion: formData.activeVersion, - }, - }).unwrap(); - } - await deleteAppVersion({ appId: app.appId, version: Number(versionId) }).unwrap(); - - // Success - navigate back to versions list - navigate(`/developer/apps/appId/${appId}/versions`); - } catch (error) { - console.error('Failed to delete app version:', error); - setIsSubmitting(false); - throw error; - } - }; - - const handleCloseModal = () => { - setCurrentView('details'); - }; - - return ( - <> - navigate('/developer/apps') }, - { label: app.name, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, - { label: 'Versions', onClick: () => navigate(`/developer/apps/appId/${appId}/versions`) }, - { label: `Version ${versionId}` }, - ]} - /> - - - {/* Edit Version Modal */} - - e.preventDefault()} - > - - - Edit Version - - Update the changes description for this version - - - - - - {/* Delete Version Modal */} - - - - - Delete Version - - - - - - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailsWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailsWrapper.tsx new file mode 100644 index 000000000..9396ee560 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionDetailsWrapper.tsx @@ -0,0 +1,427 @@ +import { useState, useEffect } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; +import { getClient, AppVersion } from '@lit-protocol/vincent-contracts-sdk'; +import { readOnlySigner } from '@/utils/developer-dashboard/readOnlySigner'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { registryUrl } from '@/config/registry'; +import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; +import { Button } from '@/components/shared/ui/button'; +import Loading from '@/components/shared/ui/Loading'; +import { StatusMessage } from '@/components/shared/ui/statusMessage'; +import { theme, fonts } from '@/lib/themeClasses'; +import { CheckCircle, XCircle, ExternalLink, Loader2 } from 'lucide-react'; +import { PackageInstallCommand } from './ui/PackageInstallCommand'; + +interface AbilityDisplayData { + title: string; + packageName: string; + version: string; + ipfsCid: string; + policyIpfsCids: string[]; +} + +export function AppVersionDetailsWrapper() { + const { appId, version } = useParams<{ appId: string; version: string }>(); + const navigate = useNavigate(); + const { getSigner } = useWagmiSigner(); + const { ensureChain, infoMessage } = useEnsureChain(); + + // On-chain state (for enabled status) + const [onChainVersion, setOnChainVersion] = useState(null); + const [onChainLoading, setOnChainLoading] = useState(true); + const [onChainError, setOnChainError] = useState(null); + + // Combined ability data + const [abilities, setAbilities] = useState([]); + const [abilitiesLoading, setAbilitiesLoading] = useState(true); + + // Toggle state + const [isToggling, setIsToggling] = useState(false); + const [toggleError, setToggleError] = useState(null); + + // Fetch app from registry for name/metadata + const { data: app } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }, { skip: !appId }); + + // Fetch AppVersionAbilities from registry (has packageName, version directly) + const { + data: versionAbilities, + isLoading: versionAbilitiesLoading, + error: versionAbilitiesError, + } = vincentApiClient.useListAppVersionAbilitiesQuery( + { appId: Number(appId), version: Number(version) }, + { skip: !appId || !version }, + ); + + // Fetch on-chain version for enabled status and policy CIDs + useEffect(() => { + async function fetchOnChainVersion() { + if (!appId || !version) { + setOnChainError('App ID and version are required'); + setOnChainLoading(false); + return; + } + + setOnChainLoading(true); + setOnChainError(null); + + try { + const client = getClient({ signer: readOnlySigner }); + const result = await client.getAppVersion({ + appId: Number(appId), + version: Number(version), + }); + + if (result === null) { + setOnChainError('Version not found on-chain'); + setOnChainVersion(null); + } else { + setOnChainVersion(result.appVersion); + } + } catch (err: any) { + setOnChainError(`Failed to fetch version: ${err.message}`); + setOnChainVersion(null); + } finally { + setOnChainLoading(false); + } + } + + fetchOnChainVersion(); + }, [appId, version]); + + // Build ability display data by combining registry and on-chain data + useEffect(() => { + async function buildAbilityData() { + if (!versionAbilities || !onChainVersion) { + setAbilitiesLoading(false); + return; + } + + setAbilitiesLoading(true); + + try { + // Build a map of ipfsCid -> policyIpfsCids from on-chain data + const policyMap = new Map(); + for (const onChainAbility of onChainVersion.abilities) { + policyMap.set(onChainAbility.abilityIpfsCid, onChainAbility.policyIpfsCids); + } + + // For each registry ability, fetch additional metadata + const abilityDataPromises = versionAbilities.map(async (va) => { + try { + // Fetch Ability for title + const abilityResponse = await fetch( + `${registryUrl}/ability/${encodeURIComponent(va.abilityPackageName)}`, + ); + const ability = abilityResponse.ok ? await abilityResponse.json() : null; + + // Fetch AbilityVersion for ipfsCid + const versionResponse = await fetch( + `${registryUrl}/ability/${encodeURIComponent(va.abilityPackageName)}/version/${encodeURIComponent(va.abilityVersion)}`, + ); + const abilityVersion = versionResponse.ok ? await versionResponse.json() : null; + + const ipfsCid = abilityVersion?.ipfsCid || ''; + + return { + title: ability?.title || va.abilityPackageName, + packageName: va.abilityPackageName, + version: va.abilityVersion, + ipfsCid, + policyIpfsCids: policyMap.get(ipfsCid) || [], + }; + } catch { + return { + title: va.abilityPackageName, + packageName: va.abilityPackageName, + version: va.abilityVersion, + ipfsCid: '', + policyIpfsCids: [], + }; + } + }); + + const abilityData = await Promise.all(abilityDataPromises); + setAbilities(abilityData); + } catch (err) { + console.error('Failed to build ability data:', err); + } finally { + setAbilitiesLoading(false); + } + } + + if (!versionAbilitiesLoading && !onChainLoading) { + buildAbilityData(); + } + }, [versionAbilities, onChainVersion, versionAbilitiesLoading, onChainLoading]); + + const handleToggleEnabled = async () => { + if (!onChainVersion || !appId || !version) return; + + // Ensure user is on Base Sepolia before starting + const action = onChainVersion.enabled ? 'Disable Version' : 'Enable Version'; + try { + const canProceed = await ensureChain(action); + if (!canProceed) return; + } catch (error) { + setToggleError(error instanceof Error ? error.message : 'Failed to switch network'); + return; + } + + setIsToggling(true); + setToggleError(null); + + try { + const signer = await getSigner(); + const client = getClient({ signer }); + + await client.enableAppVersion({ + appId: Number(appId), + appVersion: Number(version), + enabled: !onChainVersion.enabled, + }); + + // Update local state + setOnChainVersion({ + ...onChainVersion, + enabled: !onChainVersion.enabled, + }); + } catch (err: any) { + console.error('Failed to toggle version enabled state:', err); + + let errorMessage = 'Failed to update version'; + if (err.code === 'ACTION_REJECTED' || err.message?.includes('user rejected')) { + errorMessage = 'Transaction was rejected'; + } else if (err.message) { + errorMessage = err.message.substring(0, 100); + } + setToggleError(errorMessage); + } finally { + setIsToggling(false); + } + }; + + const isLoading = onChainLoading || versionAbilitiesLoading || abilitiesLoading; + + if (isLoading) { + return ; + } + + if (onChainError || versionAbilitiesError) { + const errorMessage = onChainError || 'Failed to load version abilities'; + return ( + <> + navigate('/developer/apps') }, + { label: `App ${appId}`, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, + { + label: 'Versions', + onClick: () => navigate(`/developer/apps/appId/${appId}/versions`), + }, + ]} + /> + + + ); + } + + if (!onChainVersion) { + return ( + <> + navigate('/developer/apps') }, + { + label: app?.name || `App ${appId}`, + onClick: () => navigate(`/developer/apps/appId/${appId}`), + }, + { + label: 'Versions', + onClick: () => navigate(`/developer/apps/appId/${appId}/versions`), + }, + ]} + /> + + + ); + } + + return ( + <> + navigate('/developer/apps') }, + { + label: app?.name || `App ${appId}`, + onClick: () => navigate(`/developer/apps/appId/${appId}`), + }, + { label: 'Versions', onClick: () => navigate(`/developer/apps/appId/${appId}/versions`) }, + { label: `Version ${version}` }, + ]} + /> + +
+
+
+

+ Version {version} +

+
+ {onChainVersion.enabled ? ( + <> + + Enabled + + ) : ( + <> + + Disabled + + )} +
+
+ +
+ {infoMessage && ( +
+ +
+ )} + {toggleError && ( +
+ +
+ )} +
+ + {/* Abilities Section */} +
+

+ Abilities ({abilities.length}) +

+ + {abilities.length === 0 ? ( +

+ No abilities configured for this version. +

+ ) : ( +
+ {abilities.map((ability) => ( +
+
+ {/* Title */} +
+ {ability.title} +
+ + {/* Package Name */} + + + {/* IPFS CID */} + {ability.ipfsCid && ( +
+ + {ability.ipfsCid} + + + + +
+ )} + + {/* Policies */} + {ability.policyIpfsCids.length > 0 && ( +
+
+ Policies ({ability.policyIpfsCids.length}) +
+
+ {ability.policyIpfsCids.map((policyCid) => ( +
+ + {policyCid} + + + + +
+ ))} +
+
+ )} +
+
+ ))} +
+ )} + + {/* Package Install Command */} + {abilities.length > 0 && ( +
+ ({ + abilityPackageName: ability.packageName, + abilityVersion: ability.version, + isDeleted: false, + }))} + abilityVersionsData={{}} + /> +
+ )} +
+ + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionsWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionsWrapper.tsx index 3fa328736..b099200a4 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionsWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/AppVersionsWrapper.tsx @@ -1,85 +1,232 @@ -import { useMemo, useState } from 'react'; -import { useNavigate, useParams } from 'react-router'; +import { useState } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { motion } from 'framer-motion'; import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; - -import { AppVersionsListView } from '../views/AppVersionsListView'; +import { useBlockchainAppVersions } from '@/hooks/useBlockchainAppVersions'; +import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; import Loading from '@/components/shared/ui/Loading'; import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { AppVersion } from '@/types/developer-dashboard/appTypes'; -import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; -import { useBlockchainAppVersionsData } from '@/hooks/useBlockchainAppVersionsData'; +import { theme, fonts } from '@/lib/themeClasses'; +import { CheckCircle, XCircle, Plus, Layers } from 'lucide-react'; +import { AppVersion } from '@lit-protocol/vincent-contracts-sdk'; + +interface VersionCardProps { + version: AppVersion; + onClick: () => void; +} -type SortOption = 'published' | 'enabled'; +function VersionCard({ version, onClick }: VersionCardProps) { + return ( + + ); +} export function AppVersionsWrapper() { const { appId } = useParams<{ appId: string }>(); const navigate = useNavigate(); - const [sortBy, setSortBy] = useState('published'); + const [showContent] = useState(true); - // Fetch app - const { - data: app, - isLoading: appLoading, - isError: appError, - } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }); + // Fetch app from registry for name/metadata + const { data: app, isLoading: appLoading } = vincentApiClient.useGetAppQuery( + { appId: Number(appId) }, + { skip: !appId }, + ); - // Fetch app versions + // Fetch versions from on-chain const { - data: versions, - isLoading: versionsLoading, - isError: versionsError, - } = vincentApiClient.useGetAppVersionsQuery({ appId: Number(appId) }); + data: chainData, + error: chainError, + isLoading: chainLoading, + } = useBlockchainAppVersions(Number(appId)); - // Fetch blockchain data for all versions - const { blockchainVersions, blockchainVersionsLoading, blockchainVersionsError } = - useBlockchainAppVersionsData(Number(appId), versions); + const isLoading = appLoading || chainLoading; - // Separate active and deleted versions - const { activeVersions, deletedVersions } = useMemo(() => { - if (!versions?.length) return { activeVersions: [], deletedVersions: [] }; - const activeVersions = versions.filter((version: AppVersion) => !version.isDeleted); - const deletedVersions = versions.filter((version: AppVersion) => version.isDeleted); - return { activeVersions, deletedVersions }; - }, [versions]); + if (isLoading) { + return ; + } - // Loading states first - if (appLoading || versionsLoading || blockchainVersionsLoading) return ; - - // Combined error states - if (appError) return ; - if (versionsError) return ; - if (blockchainVersionsError) - return ; - if (!app) return ; + if (chainError) { + return ( + <> + navigate('/developer/apps') }, + { label: `App ${appId}` }, + ]} + /> + + + ); + } const handleVersionClick = (version: number) => { navigate(`/developer/apps/appId/${appId}/version/${version}`); }; const handleCreateVersion = () => { - navigate(`/developer/apps/appId/${appId}?action=create-app-version`); + navigate(`/developer/apps/appId/${appId}/new-version`); }; + // Separate enabled and disabled versions + const enabledVersions = chainData?.versions.filter((v) => v.enabled) || []; + const disabledVersions = chainData?.versions.filter((v) => !v.enabled) || []; + return ( <> navigate('/developer/apps') }, - { label: app.name, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, + { + label: app?.name || `App ${appId}`, + onClick: () => navigate(`/developer/apps/appId/${appId}`), + }, { label: 'Versions' }, ]} /> - + + + {!chainData || chainData.versions.length === 0 ? ( +
+
+
+ +
+

+ No Versions Yet +

+

+ Create your first version to get started. +

+ +
+
+ ) : ( + <> + {/* Enabled Versions Section */} + {enabledVersions.length > 0 && ( +
+

+ Enabled Versions +

+
+ {enabledVersions + .slice() + .sort((a, b) => b.version - a.version) + .map((version) => ( + handleVersionClick(version.version)} + /> + ))} +
+
+ )} + + {/* Disabled Versions Section */} + {disabledVersions.length > 0 && ( +
+

+ Disabled Versions +

+
+ {disabledVersions + .slice() + .sort((a, b) => b.version - a.version) + .map((version) => ( + handleVersionClick(version.version)} + /> + ))} +
+
+ )} + + {/* Divider and Create Version CTA */} +
+
+

+ Ready to create another version? +

+ +
+
+ + )} +
); } diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppVersionWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppVersionWrapper.tsx new file mode 100644 index 000000000..e27dd36f9 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppVersionWrapper.tsx @@ -0,0 +1,554 @@ +import { useState } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; +import { getClient } from '@lit-protocol/vincent-contracts-sdk'; +import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; +import { StatusMessage } from '@/components/shared/ui/statusMessage'; +import Loading from '@/components/shared/ui/Loading'; +import { Button } from '@/components/shared/ui/button'; +import { Trash2 } from 'lucide-react'; +import { theme, fonts } from '@/lib/themeClasses'; +import { registryUrl } from '@/config/registry'; +import { getSiweAuthToken } from '@/hooks/developer-dashboard/useAuth'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { AbilitySelectorModal } from '@/components/developer-dashboard/ui/AbilitySelectorModal'; +import { Ability } from '@/types/developer-dashboard/appTypes'; + +type CreateVersionStep = 'abilities' | 'review' | 'submitting'; + +interface SelectedAbility { + ability: Ability; + ipfsCid: string; + policies: string[]; // Policy IPFS CIDs +} + +export function CreateAppVersionWrapper() { + const { appId } = useParams<{ appId: string }>(); + const navigate = useNavigate(); + const { getSigner } = useWagmiSigner(); + const { ensureChain, infoMessage } = useEnsureChain(); + + const [currentStep, setCurrentStep] = useState('abilities'); + const [selectedAbilities, setSelectedAbilities] = useState>( + new Map(), + ); + const [isAbilitySelectorOpen, setIsAbilitySelectorOpen] = useState(false); + const [error, setError] = useState(null); + const [submissionStatus, setSubmissionStatus] = useState< + 'signing' | 'confirming' | 'creating-registry' | null + >(null); + + // RTK Query mutations + const [createAppVersionAbility] = vincentApiClient.useCreateAppVersionAbilityMutation(); + + // Fetch app data + const { + data: app, + isLoading: appLoading, + error: appError, + } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }, { skip: !appId }); + + // Fetch app versions to determine next version number + const { + data: versions, + isLoading: versionsLoading, + error: versionsError, + } = vincentApiClient.useGetAppVersionsQuery({ appId: Number(appId) }, { skip: !appId }); + + // Fetch available abilities + const { + data: abilities, + isLoading: abilitiesLoading, + error: abilitiesError, + } = vincentApiClient.useListAllAbilitiesQuery(); + + const isLoading = appLoading || versionsLoading; + + // Calculate next version number + const currentMaxVersion = versions?.length ? Math.max(...versions.map((v: any) => v.version)) : 0; + const nextVersion = currentMaxVersion + 1; + + const handleAbilityAdd = async (ability: Ability) => { + try { + // Get the active version's IPFS CID + const activeVersion = ability.activeVersion; + if (!activeVersion) { + console.error('Ability has no active version:', ability); + setError('Selected ability has no active version'); + return; + } + + // Fetch ability version to get IPFS CID + const encodedPackageName = encodeURIComponent(ability.packageName); + const encodedVersion = encodeURIComponent(activeVersion); + const response = await fetch( + `${registryUrl}/ability/${encodedPackageName}/version/${encodedVersion}`, + ); + + if (!response.ok) { + throw new Error( + `Failed to fetch ability version: ${response.status} ${response.statusText}`, + ); + } + + const abilityVersion = await response.json(); + + if (!abilityVersion.ipfsCid) { + throw new Error('Ability version is missing IPFS CID'); + } + + setSelectedAbilities((prev) => { + const next = new Map(prev); + next.set(ability.packageName, { + ability, + ipfsCid: abilityVersion.ipfsCid, + policies: [], // TODO: Add policy selection + }); + return next; + }); + setError(null); + } catch (err) { + console.error('Failed to add ability:', err); + setError(err instanceof Error ? err.message : 'Failed to add ability'); + } + }; + + const handleAbilityRemove = (packageName: string) => { + setSelectedAbilities((prev) => { + const next = new Map(prev); + next.delete(packageName); + return next; + }); + }; + + const handleNext = () => { + setError(null); + if (currentStep === 'abilities') { + if (selectedAbilities.size === 0) { + setError('Please select at least one ability'); + return; + } + setCurrentStep('review'); + } + }; + + const handleBack = () => { + setError(null); + if (currentStep === 'review') { + setCurrentStep('abilities'); + } + }; + + const handleFinalSubmit = async () => { + // Step 1: Ensure user is on Base Sepolia (before starting submission) + try { + const canProceed = await ensureChain('Register Version On-Chain'); + if (!canProceed) return; // Chain was switched, user needs to click again + } catch (error) { + setError(error instanceof Error ? error.message : 'Failed to switch network'); + return; + } + + setCurrentStep('submitting'); + setSubmissionStatus('signing'); + setError(null); + + try { + // Step 2: Register version on-chain + console.log('[CreateVersion] Getting signer...'); + const signer = await getSigner(); + const client = getClient({ signer }); + + const abilityIpfsCids = Array.from(selectedAbilities.values()).map((sa) => sa.ipfsCid); + const abilityPolicies = Array.from(selectedAbilities.values()).map((sa) => sa.policies); + + console.log('[CreateVersion] Registering next version with abilities:', abilityIpfsCids); + + setSubmissionStatus('confirming'); + const result = await client.registerNextVersion({ + appId: Number(appId), + versionAbilities: { + abilityIpfsCids, + abilityPolicies, + }, + }); + + console.log('[CreateVersion] Version registered, tx hash:', result.txHash); + + // Step 3: Wait for indexing + console.log('[CreateVersion] Waiting 5 seconds for indexing...'); + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Step 4: Create AppVersion in registry + setSubmissionStatus('creating-registry'); + console.log('[CreateVersion] Creating AppVersion in registry...'); + + const siweToken = getSiweAuthToken(); + if (!siweToken) { + throw new Error('Authentication required. Please sign in again.'); + } + + const versionResponse = await fetch(`${registryUrl}/app/${appId}/version`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + authorization: `SIWE ${siweToken}`, + }, + }); + + if (!versionResponse.ok) { + const errorData = await versionResponse.json(); + throw new Error(errorData.message || 'Failed to create app version in registry'); + } + + const newVersionData = await versionResponse.json(); + const newVersionNumber = newVersionData.version; + console.log('[CreateVersion] AppVersion created:', newVersionNumber); + + // Step 5: Create AppVersionAbility records in registry + console.log('[CreateVersion] Creating AppVersionAbility records...'); + try { + const abilityCreationPromises = Array.from(selectedAbilities.values()).map( + ({ ability }) => { + console.log( + '[CreateVersion] Creating ability:', + ability.packageName, + 'v', + ability.activeVersion, + ); + return createAppVersionAbility({ + appId: Number(appId), + appVersion: newVersionNumber, + abilityPackageName: ability.packageName, + appVersionAbilityCreate: { + abilityVersion: ability.activeVersion, + hiddenSupportedPolicies: [], + }, + }).unwrap(); + }, + ); + + await Promise.all(abilityCreationPromises); + console.log('[CreateVersion] All AppVersionAbility records created'); + } catch (abilityError: any) { + console.error('[CreateVersion] Failed to create abilities in registry:', abilityError); + // Navigate to app overview - user can add abilities manually if needed + } + + // Step 6: Navigate to app detail page + console.log('[CreateVersion] Success! Navigating to app detail page'); + navigate(`/developer/apps/appId/${appId}`); + } catch (err: any) { + console.error('Failed to create app version:', err); + + let errorMessage = 'Failed to create app version'; + if (err.code === 'ACTION_REJECTED' || err.message?.includes('user rejected')) { + errorMessage = + 'Transaction was rejected. Please try again and approve the transaction in your wallet.'; + } else if (err.code === 'INSUFFICIENT_FUNDS') { + errorMessage = + 'Insufficient funds to complete the transaction. Please add more ETH to your wallet.'; + } else if (err.message) { + const match = err.message.match(/^([^(]+)/); + errorMessage = match ? match[1].trim() : err.message.substring(0, 100); + } + + setError(errorMessage); + setCurrentStep('review'); + setSubmissionStatus(null); + } + }; + + if (isLoading) { + return ; + } + + if (appError || versionsError || abilitiesError || !app) { + return ; + } + + // Progress bar data + const steps = [ + { name: 'Select Abilities', step: 'abilities' }, + { name: 'Review & Submit', step: 'review' }, + { name: 'Register', step: 'submitting' }, + ]; + + const currentStepIndex = steps.findIndex((s) => s.step === currentStep); + + // Submitting state + if (currentStep === 'submitting') { + return ( + <> + navigate('/developer/apps') }, + { label: app.name, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, + { label: `New Version (v${nextVersion})` }, + ]} + /> + +
+ {submissionStatus === 'signing' ? ( + <> +

+ Please sign the transaction +

+

+ Check your wallet to approve the version registration transaction +

+ + ) : submissionStatus === 'confirming' ? ( + <> +

+ Waiting for confirmation +

+

+ Your transaction has been submitted and is being confirmed on-chain +

+

+ This typically takes 15-30 seconds +

+ + ) : submissionStatus === 'creating-registry' ? ( + <> +

+ Creating registry records +

+

+ Adding version and abilities to the Vincent Registry +

+

+ Almost done... +

+ + ) : null} +
+ + ); + } + + return ( + <> + navigate('/developer/apps') }, + { label: app.name, onClick: () => navigate(`/developer/apps/appId/${appId}`) }, + { label: `New Version (v${nextVersion})` }, + ]} + /> + +
+

Create Version {nextVersion}

+

+ Follow the steps below to register a new version for {app.name}. +

+
+ + {/* Progress Bar */} +
+
+ {steps.map((step, index) => ( +
+ {/* Step Circle and Label */} +
+
currentStepIndex + ? `${theme.mainCard} border-2 ${theme.cardBorder} ${theme.textMuted}` + : '' + }`} + style={index <= currentStepIndex ? { backgroundColor: theme.brandOrange } : {}} + > + {index + 1} +
+ + {step.name} + +
+ + {/* Connecting Bar */} + {index < steps.length - 1 && ( +
+
+
+ )} +
+ ))} +
+
+ + {infoMessage && ( +
+ +
+ )} + + {error && ( +
+ +
+ )} + + {/* Step 1: Select Abilities */} + {currentStep === 'abilities' && ( + <> + {abilitiesLoading ? ( + + ) : !abilities || abilities.length === 0 ? ( + + ) : ( + <> + + + {/* Display Selected Abilities */} + {selectedAbilities.size > 0 && ( +
+

+ Selected Abilities ({selectedAbilities.size}) +

+
+ {Array.from(selectedAbilities.values()).map(({ ability }) => ( +
+ + +
+ ))} +
+
+ )} + +
+ +
+ + )} + + )} + + {/* Step 2: Review */} + {currentStep === 'review' && ( + <> +
+

+ Review Version {nextVersion} +

+ + {/* Selected Abilities */} +
+

+ Selected Abilities ({selectedAbilities.size}) +

+
+ {Array.from(selectedAbilities.values()).map(({ ability }) => ( +
+
+ {ability.title} +
+
+ {ability.packageName} v{ability.activeVersion} +
+
+ ))} +
+
+
+ +
+ + +
+ + )} + + {/* Ability Selector Modal */} + setIsAbilitySelectorOpen(false)} + onAbilityAdd={handleAbilityAdd} + existingAbilities={Array.from(selectedAbilities.keys())} + availableAbilities={abilities || []} + /> + + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppWrapper.tsx index 080b88efa..cc17ca748 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/CreateAppWrapper.tsx @@ -1,37 +1,77 @@ -import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { CreateAppForm, type CreateAppFormData } from '../forms/CreateAppForm'; -import { navigateWithDelay } from '@/utils/developer-dashboard/app-forms'; import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; -import { addPayee } from '@/utils/user-dashboard/addPayee'; +import { StatusMessage } from '@/components/shared/ui/statusMessage'; +import Loading from '@/components/shared/ui/Loading'; +import { theme } from '@/lib/themeClasses'; +import { + useCreateAppFormState, + CreateAppProgressBar, + CreateAppStepAbilities, + CreateAppStepDelegatees, + CreateAppStepMetadata, + CreateAppStepReview, + CreateAppSubmitting, +} from './create-app'; export function CreateAppWrapper() { - // Mutation - const [createApp, { isLoading, isSuccess, data }] = vincentApiClient.useCreateAppMutation(); - - // Navigation const navigate = useNavigate(); - // Effect - useEffect(() => { - if (isSuccess && data) { - navigateWithDelay(navigate, `/developer/apps/appId/${data.appId}/version/1/abilities`); - } - }, [isSuccess, data, navigate]); - - const handleSubmit = async (data: CreateAppFormData) => { - // Add all delegatee addresses as payees before creating the app - await Promise.all( - (data.delegateeAddresses || []).map(async (address) => { - await addPayee(address); - }), - ); + const { + // Step state + currentStep, + + // Abilities + selectedAbilities, + isAbilitySelectorOpen, + setIsAbilitySelectorOpen, + handleAbilityAdd, + handleAbilityRemove, + abilities, + abilitiesLoading, + + // Delegatees + delegateeAddresses, + delegateeInput, + delegateeError, + handleDelegateeInputChange, + handleRemoveDelegatee, + + // Metadata + metadataForm, + metadataFormData, - await createApp({ - appCreate: { ...data }, - }).unwrap(); - }; + // Submission + error, + submissionStatus, + infoMessage, + + // Navigation + handleNextFromAbilities, + handleNextFromDelegatees, + handleNextFromMetadata, + handleBack, + handleFinalSubmit, + } = useCreateAppFormState(); + + // Show submitting state + if (currentStep === 'submitting') { + return ; + } + + // Show error if abilities failed to load + if (!abilities && !abilitiesLoading) { + return ( + <> + navigate('/developer/apps') }, + { label: 'Create App' }, + ]} + /> + + + ); + } return ( <> @@ -41,7 +81,77 @@ export function CreateAppWrapper() { { label: 'Create App' }, ]} /> - + +
+

Create New App

+

+ Follow the steps below to register your app on-chain with abilities and delegatees. +

+
+ + + + {infoMessage && ( +
+ +
+ )} + + {error && ( +
+ +
+ )} + + {abilitiesLoading ? ( + + ) : !abilities || abilities.length === 0 ? ( + + ) : ( + <> + {currentStep === 'abilities' && ( + + )} + + {currentStep === 'delegatees' && ( + + )} + + {currentStep === 'metadata' && ( + + )} + + {currentStep === 'review' && ( + + )} + + )} ); } diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/PublishAppVersionWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/PublishAppVersionWrapper.tsx deleted file mode 100644 index 981740d06..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/PublishAppVersionWrapper.tsx +++ /dev/null @@ -1,346 +0,0 @@ -import { useEffect, useState, useMemo } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { ethers } from 'ethers'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { AbilityVersion, PolicyVersion } from '@/types/developer-dashboard/appTypes'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import { PublishAppVersionButton } from './ui/PublishAppVersionButton'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; - -export function PublishAppVersionWrapper({ isAppPublished }: { isAppPublished: boolean }) { - const { appId, versionId } = useParams<{ appId: string; versionId: string }>(); - const { authInfo, sessionSigs } = useReadAuthInfo(); - const navigate = useNavigate(); - - // Fetching - const { - data: app, - isLoading: appLoading, - isError: appError, - } = vincentApiClient.useGetAppQuery({ appId: Number(appId) }); - - const { - data: versionData, - isLoading: versionLoading, - isError: versionError, - } = vincentApiClient.useGetAppVersionQuery({ appId: Number(appId), version: Number(versionId) }); - - const { - data: versionAbilities, - isLoading: versionAbilitiesLoading, - isError: versionAbilitiesError, - } = vincentApiClient.useListAppVersionAbilitiesQuery({ - appId: Number(appId), - version: Number(versionId), - }); - - const [setActiveVersion] = vincentApiClient.useSetAppActiveVersionMutation(); - - // Lazy queries for fetching ability and policy versions - const [ - triggerGetAbilityVersion, - { isLoading: abilityVersionsLoading, isError: abilityVersionsError }, - ] = vincentApiClient.useLazyGetAbilityVersionQuery(); - const [triggerGetPolicyVersion, { isLoading: policiesLoading, isError: policiesError }] = - vincentApiClient.useLazyGetPolicyVersionQuery(); - - // State for storing fetched data - const [abilityVersionsData, setAbilityVersionsData] = useState>( - {}, - ); - const [policyVersionsData, setPolicyVersionsData] = useState>({}); - - // State for publish status - const [publishResult, setPublishResult] = useState<{ - success: boolean; - message?: string; - } | null>(null); - const [isPublishing, setIsPublishing] = useState(false); - - // Fetch ability versions and policy versions when activeAbilities changes - useEffect(() => { - if (!versionAbilities || versionAbilities.length === 0) { - setAbilityVersionsData({}); - setPolicyVersionsData({}); - return; - } - - const fetchAbilityAndPolicyVersions = async () => { - const abilityVersions: Record = {}; - const policyVersions: Record = {}; - - // Fetch ability versions - for (const ability of versionAbilities) { - const abilityVersionResult = await triggerGetAbilityVersion({ - packageName: ability.abilityPackageName, - version: ability.abilityVersion, - }); - - const abilityKey = `${ability.abilityPackageName}-${ability.abilityVersion}`; - if (abilityVersionResult.data) { - abilityVersions[abilityKey] = abilityVersionResult.data; - - // Fetch supported policies for this ability - if (abilityVersionResult.data.supportedPolicies) { - for (const [policyPackageName, policyVersion] of Object.entries( - abilityVersionResult.data.supportedPolicies, - )) { - const policyVersionResult = await triggerGetPolicyVersion({ - packageName: policyPackageName, - version: policyVersion, - }); - - const policyKey = `${policyPackageName}-${policyVersion}`; - if (policyVersionResult.data) { - policyVersions[policyKey] = policyVersionResult.data; - } - } - } - } - } - - setAbilityVersionsData(abilityVersions); - setPolicyVersionsData(policyVersions); - }; - - fetchAbilityAndPolicyVersions(); - }, [versionAbilities, triggerGetAbilityVersion, triggerGetPolicyVersion]); - - // Extract IPFS CIDs from the fetched data - const { abilityIpfsCids, abilityPolicies } = useMemo(() => { - const abilityIpfsCids: string[] = []; - const abilityPolicies: string[][] = []; - - // Get ability IPFS CIDs and their corresponding policies - Object.values(abilityVersionsData).forEach((abilityVersion) => { - if (abilityVersion.ipfsCid) { - abilityIpfsCids.push(abilityVersion.ipfsCid); - - // Get policies for this specific ability (or empty array if none) - const abilityPolicyCids: string[] = []; - if (abilityVersion.supportedPolicies) { - Object.entries(abilityVersion.supportedPolicies).forEach( - ([policyPackageName, policyVersion]) => { - const policyKey = `${policyPackageName}-${policyVersion}`; - const policyVersionData = policyVersionsData[policyKey]; - if (policyVersionData?.ipfsCid) { - abilityPolicyCids.push(policyVersionData.ipfsCid); - } - }, - ); - } - // Always push a policy array for each ability, even if empty - abilityPolicies.push(abilityPolicyCids); - } - }); - - return { - abilityIpfsCids, // Keep all ability CIDs to match policy array length - abilityPolicies, // Array of policy arrays, one per ability (same length as abilityIpfsCids) - }; - }, [abilityVersionsData, policyVersionsData]); - - // Clear error message after 3 seconds - useEffect(() => { - if (!publishResult || publishResult.success) return; - - const timer = setTimeout(() => { - setPublishResult(null); - }, 3000); - - return () => clearTimeout(timer); - }, [publishResult]); - - // Loading states with skeleton matching PublishAppVersionButton layout - if ( - appLoading || - versionLoading || - versionAbilitiesLoading || - abilityVersionsLoading || - policiesLoading - ) { - return ( -
-
-
-
-
-
-
-
-
-
-
- ); - } - - // Error states - if (appError) return ; - if (versionError) return ; - if (versionAbilitiesError) - return ; - if (abilityVersionsError) - return ; - if (policiesError) return ; - if (!app) return ; - if (!versionData) - return ; - - const publishAppVersion = async () => { - if (!appId) { - return; - } - - setPublishResult(null); - setIsPublishing(true); - - try { - // Check if we have any abilities at all - if (abilityIpfsCids.length === 0) { - if (versionAbilities && versionAbilities.length > 0) { - setPublishResult({ - success: false, - message: - 'Abilities found but missing IPFS CIDs. Please ensure all abilities are properly uploaded to IPFS.', - }); - } else { - setPublishResult({ - success: false, - message: - 'Cannot publish version without abilities. Please add at least one ability to this version.', - }); - } - setIsPublishing(false); - return; - } - - // Check if we have any active (non-deleted) abilities - const activeAbilities = versionAbilities?.filter((ability) => !ability.isDeleted) || []; - - if (activeAbilities.length === 0) { - setPublishResult({ - success: false, - message: - 'Cannot publish version without active abilities. Please add at least one ability or undelete existing abilities.', - }); - setIsPublishing(false); - return; - } - - // Check if any delegatees are already registered to other apps - const delegatees = app.delegateeAddresses; - - if (!delegatees) { - setPublishResult({ - success: false, - message: 'Cannot publish app without delegatee addresses.', - }); - setIsPublishing(false); - return; - } - - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); - - for (const delegatee of delegatees) { - // Validate that delegatee is a proper Ethereum address - if (!ethers.utils.isAddress(delegatee)) { - setPublishResult({ - success: false, - message: `Invalid delegatee address: ${delegatee}. Please ensure all delegatee addresses are valid Ethereum addresses.`, - }); - setIsPublishing(false); - return; - } - - try { - const existingApp = await client.getAppByDelegateeAddress({ - delegateeAddress: delegatee, - }); - - if (existingApp && existingApp?.id !== Number(appId)) { - setPublishResult({ - success: false, - message: `Delegatee ${delegatee} is already registered to app ${existingApp?.id}`, - }); - setIsPublishing(false); - return; - } - } catch (error) { - // If DelegateeNotRegistered, that's fine - continue - if (!(error instanceof Error) || !error.message?.includes('DelegateeNotRegistered')) { - throw error; - } - } - } - - if (!isAppPublished) { - // App not registered - use registerApp (first-time registration) - await client.registerApp({ - appId: Number(appId), - delegateeAddresses: delegatees, - versionAbilities: { - abilityIpfsCids: abilityIpfsCids, - abilityPolicies: abilityPolicies, - }, - }); - - // Set active version to 1 for the first version - await setActiveVersion({ - appId: Number(appId), - appSetActiveVersion: { - activeVersion: 1, - }, - }); - } else { - // App is registered - use registerNextVersion - await client.registerNextVersion({ - appId: Number(appId), - versionAbilities: { - abilityIpfsCids: abilityIpfsCids, - abilityPolicies: abilityPolicies, - }, - }); - } - - setPublishResult({ - success: true, - message: 'App version published successfully!', - }); - setIsPublishing(false); - - // Navigate to version page after a delay - setTimeout(() => { - navigate(`/developer/apps/appId/${appId}/version/${versionId}`); - }, 2000); - } catch (error) { - console.error('Error publishing app version:', error); - const errorMessage = error instanceof Error ? error.message : 'Please try again.'; - console.error('Error details:', { - message: errorMessage, - ...(error instanceof Error && { stack: error.stack, name: error.name }), - }); - - setPublishResult({ - success: false, - message: `Failed to publish app version: ${errorMessage}`, - }); - setIsPublishing(false); - } - }; - - return ( - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppProgressBar.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppProgressBar.tsx new file mode 100644 index 000000000..f8e9da6b1 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppProgressBar.tsx @@ -0,0 +1,60 @@ +import { theme, fonts } from '@/lib/themeClasses'; +import { CreateAppStep, STEPS } from './types'; + +interface CreateAppProgressBarProps { + currentStep: CreateAppStep; +} + +export function CreateAppProgressBar({ currentStep }: CreateAppProgressBarProps) { + const currentStepIndex = STEPS.findIndex((s) => s.step === currentStep); + + return ( +
+
+ {STEPS.map((step, index) => ( +
+ {/* Step Circle and Label */} +
+
currentStepIndex + ? `${theme.mainCard} border-2 ${theme.cardBorder} ${theme.textMuted}` + : '' + }`} + style={index <= currentStepIndex ? { backgroundColor: theme.brandOrange } : {}} + > + {index + 1} +
+ + {step.name} + +
+ + {/* Connecting Bar */} + {index < STEPS.length - 1 && ( +
+
+
+ )} +
+ ))} +
+
+ ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepAbilities.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepAbilities.tsx new file mode 100644 index 000000000..0dfde8907 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepAbilities.tsx @@ -0,0 +1,106 @@ +import { Trash2 } from 'lucide-react'; +import { Button } from '@/components/shared/ui/button'; +import { theme, fonts } from '@/lib/themeClasses'; +import { Ability } from '@/types/developer-dashboard/appTypes'; +import { AbilitySelectorModal } from '@/components/developer-dashboard/ui/AbilitySelectorModal'; +import { SelectedAbility } from './types'; + +interface CreateAppStepAbilitiesProps { + selectedAbilities: Map; + isAbilitySelectorOpen: boolean; + setIsAbilitySelectorOpen: (open: boolean) => void; + onAbilityAdd: (ability: Ability) => Promise; + onAbilityRemove: (packageName: string) => void; + onNext: () => void; + availableAbilities: Ability[]; +} + +export function CreateAppStepAbilities({ + selectedAbilities, + isAbilitySelectorOpen, + setIsAbilitySelectorOpen, + onAbilityAdd, + onAbilityRemove, + onNext, + availableAbilities, +}: CreateAppStepAbilitiesProps) { + return ( + <> + + + {/* Display Selected Abilities */} + {selectedAbilities.size > 0 && ( +
+

+ Selected Abilities ({selectedAbilities.size}) +

+
+ {Array.from(selectedAbilities.values()).map(({ ability }) => ( +
+ + +
+ ))} +
+
+ )} + +
+ +
+ + {/* Ability Selector Modal */} + setIsAbilitySelectorOpen(false)} + onAbilityAdd={onAbilityAdd} + existingAbilities={Array.from(selectedAbilities.keys())} + availableAbilities={availableAbilities} + /> + + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepDelegatees.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepDelegatees.tsx new file mode 100644 index 000000000..a0f723ee2 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepDelegatees.tsx @@ -0,0 +1,103 @@ +import { Trash2 } from 'lucide-react'; +import { Button } from '@/components/shared/ui/button'; +import { Input } from '@/components/shared/ui/input'; +import { theme, fonts } from '@/lib/themeClasses'; + +interface CreateAppStepDelegateesProps { + delegateeAddresses: string[]; + delegateeInput: string; + delegateeError: string | null; + onDelegateeInputChange: (value: string) => void; + onRemoveDelegatee: (address: string) => void; + onNext: () => void; + onBack: () => void; +} + +export function CreateAppStepDelegatees({ + delegateeAddresses, + delegateeInput, + delegateeError, + onDelegateeInputChange, + onRemoveDelegatee, + onNext, + onBack, +}: CreateAppStepDelegateesProps) { + return ( + <> +
+

+ Delegatee Addresses + (Optional) +

+

+ Add Ethereum addresses that can manage this app on your behalf. +

+
+

+ Note: Adding delegatees later will require a separate blockchain + transaction and gas fees. Consider adding them now to save on gas costs. +

+
+ + {/* Add Delegatee Input */} +
+ onDelegateeInputChange(e.target.value)} + className={delegateeError ? 'border-red-500 dark:border-red-400' : ''} + /> +
+ + {delegateeError && ( +

{delegateeError}

+ )} + + {/* List of Added Delegatees */} + {delegateeAddresses.length > 0 && ( +
+ {delegateeAddresses.map((address, index) => ( +
+ + {address} + + +
+ ))} +
+ )} +
+ +
+ + +
+ + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepMetadata.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepMetadata.tsx new file mode 100644 index 000000000..e0c30728f --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepMetadata.tsx @@ -0,0 +1,136 @@ +import { UseFormReturn } from 'react-hook-form'; +import { Button } from '@/components/shared/ui/button'; +import { Form } from '@/components/shared/ui/form'; +import { theme, fonts } from '@/lib/themeClasses'; +import { + TextField, + LongTextField, + ImageUploadField, + DeploymentStatusSelectField, +} from '@/components/developer-dashboard/form-fields'; +import { AppMetadataFormData } from './useCreateAppFormState'; + +interface CreateAppStepMetadataProps { + metadataForm: UseFormReturn; + onNext: (data: AppMetadataFormData) => void; + onBack: () => void; +} + +export function CreateAppStepMetadata({ + metadataForm, + onNext, + onBack, +}: CreateAppStepMetadataProps) { + const { + register, + handleSubmit, + watch, + setValue, + control, + setError, + clearErrors, + formState: { errors }, + } = metadataForm; + + return ( +
+

+ Provide metadata for your app and version 1. This information will be stored in the Vincent + Registry. +

+ +
+ +
+ {/* Left Column - Basic Information */} +
+

+ Basic Information +

+
+ + + + + + + +
+
+ + {/* Right Column - Configuration */} +
+

+ Configuration +

+
+ + + +
+
+
+ +
+ + +
+
+ +
+ ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepReview.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepReview.tsx new file mode 100644 index 000000000..8c93060cb --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppStepReview.tsx @@ -0,0 +1,159 @@ +import { Button } from '@/components/shared/ui/button'; +import { theme, fonts } from '@/lib/themeClasses'; +import { SelectedAbility } from './types'; +import { AppMetadataFormData } from './useCreateAppFormState'; + +interface CreateAppStepReviewProps { + metadataFormData: AppMetadataFormData | null; + selectedAbilities: Map; + delegateeAddresses: string[]; + onSubmit: () => void; + onBack: () => void; +} + +export function CreateAppStepReview({ + metadataFormData, + selectedAbilities, + delegateeAddresses, + onSubmit, + onBack, +}: CreateAppStepReviewProps) { + return ( + <> +
+

+ Review Your App +

+ + {/* App Details */} + {metadataFormData && ( +
+

+ App Details +

+
+
+ + Name: + +
+ {metadataFormData.name} +
+
+
+ + Description: + +
+ {metadataFormData.description} +
+
+
+ + Contact Email: + +
+ {metadataFormData.contactEmail} +
+
+
+ + App URL: + +
+ {metadataFormData.appUrl} +
+
+ {metadataFormData.logo && ( +
+ + Logo: + +
+ {metadataFormData.logo} +
+
+ )} +
+ + Deployment Status: + +
+ {metadataFormData.deploymentStatus} +
+
+
+
+ )} + + {/* Selected Abilities */} +
+

+ Selected Abilities ({selectedAbilities.size}) +

+
+ {Array.from(selectedAbilities.values()).map(({ ability }) => ( +
+
+ {ability.title} +
+
+ {ability.packageName} v{ability.activeVersion} +
+
+ ))} +
+
+ + {/* Delegatee Addresses */} +
+

+ Delegatee Addresses ({delegateeAddresses.length}) +

+ {delegateeAddresses.length > 0 ? ( +
+ {delegateeAddresses.map((address, index) => ( +
+ + {address} + +
+ ))} +
+ ) : ( +
+ + No delegatees added. You can add them later from the app management page. + +
+ )} +
+
+ +
+ + +
+ + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppSubmitting.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppSubmitting.tsx new file mode 100644 index 000000000..b5fb53cc9 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/CreateAppSubmitting.tsx @@ -0,0 +1,60 @@ +import { useNavigate } from 'react-router-dom'; +import { Breadcrumb } from '@/components/shared/ui/Breadcrumb'; +import Loading from '@/components/shared/ui/Loading'; +import { theme, fonts } from '@/lib/themeClasses'; + +interface CreateAppSubmittingProps { + submissionStatus: 'signing' | 'confirming' | 'creating-registry' | null; +} + +export function CreateAppSubmitting({ submissionStatus }: CreateAppSubmittingProps) { + const navigate = useNavigate(); + + return ( + <> + navigate('/developer/apps') }, + { label: 'Create App' }, + ]} + /> + +
+ {submissionStatus === 'signing' ? ( + <> +

+ Please sign the transaction +

+

+ Check your wallet to approve the app registration transaction +

+ + ) : submissionStatus === 'confirming' ? ( + <> +

+ Waiting for confirmation +

+

+ Your transaction has been submitted and is being confirmed on-chain +

+

+ This typically takes 15-30 seconds +

+ + ) : submissionStatus === 'creating-registry' ? ( + <> +

+ Creating registry records +

+

+ Adding app, version, and abilities to the Vincent Registry +

+

+ Almost done... +

+ + ) : null} +
+ + ); +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/index.ts b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/index.ts new file mode 100644 index 000000000..69e63c712 --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/index.ts @@ -0,0 +1,8 @@ +export { useCreateAppFormState, type AppMetadataFormData } from './useCreateAppFormState'; +export { CreateAppProgressBar } from './CreateAppProgressBar'; +export { CreateAppStepAbilities } from './CreateAppStepAbilities'; +export { CreateAppStepDelegatees } from './CreateAppStepDelegatees'; +export { CreateAppStepMetadata } from './CreateAppStepMetadata'; +export { CreateAppStepReview } from './CreateAppStepReview'; +export { CreateAppSubmitting } from './CreateAppSubmitting'; +export * from './types'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/types.ts b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/types.ts new file mode 100644 index 000000000..e5a25d33f --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/types.ts @@ -0,0 +1,19 @@ +import { Ability } from '@/types/developer-dashboard/appTypes'; + +export type CreateAppStep = 'abilities' | 'delegatees' | 'metadata' | 'review' | 'submitting'; + +export interface SelectedAbility { + ability: Ability; + ipfsCid: string; + policies: string[]; +} + +export const CREATE_APP_STORAGE_KEY = 'vincentCreateAppDraft'; + +export const STEPS = [ + { name: 'Select Abilities', step: 'abilities' }, + { name: 'Add Delegatees', step: 'delegatees' }, + { name: 'App Details', step: 'metadata' }, + { name: 'Review', step: 'review' }, + { name: 'Register', step: 'submitting' }, +] as const; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/useCreateAppFormState.ts b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/useCreateAppFormState.ts new file mode 100644 index 000000000..53af4bd4e --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/create-app/useCreateAppFormState.ts @@ -0,0 +1,405 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { ethers } from 'ethers'; +import { z } from 'zod'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { reactClient as vincentApiClient, docSchemas } from '@lit-protocol/vincent-registry-sdk'; +import { getClient } from '@lit-protocol/vincent-contracts-sdk'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { Ability } from '@/types/developer-dashboard/appTypes'; +import { registryUrl } from '@/config/registry'; +import { CreateAppStep, SelectedAbility, CREATE_APP_STORAGE_KEY } from './types'; + +// Form validation schema using existing Zod schemas from registry +const { appDoc } = docSchemas; +const { name, description, contactEmail, appUrl, logo, deploymentStatus } = appDoc.shape; + +const AppMetadataSchema = z + .object({ + name, + description, + contactEmail, + appUrl, + logo, + deploymentStatus, + }) + .strict(); + +export type AppMetadataFormData = z.infer; + +interface SavedFormState { + currentStep: CreateAppStep; + selectedAbilities: Array<[string, SelectedAbility]>; + delegateeAddresses: string[]; + metadataFormValues: Partial; + savedAt: number; +} + +export function useCreateAppFormState() { + const navigate = useNavigate(); + const { getSigner } = useWagmiSigner(); + const { ensureChain, infoMessage } = useEnsureChain(); + + // Step state + const [currentStep, setCurrentStep] = useState('abilities'); + const [isRestoringState, setIsRestoringState] = useState(true); + + // Abilities state + const [selectedAbilities, setSelectedAbilities] = useState>( + new Map(), + ); + const [isAbilitySelectorOpen, setIsAbilitySelectorOpen] = useState(false); + + // Delegatees state + const [delegateeAddresses, setDelegateeAddresses] = useState([]); + const [delegateeInput, setDelegateeInput] = useState(''); + const [delegateeError, setDelegateeError] = useState(null); + + // Metadata state + const [metadataFormData, setMetadataFormData] = useState(null); + + // Submission state + const [error, setError] = useState(null); + const [submissionStatus, setSubmissionStatus] = useState< + 'signing' | 'confirming' | 'creating-registry' | null + >(null); + + // Form setup + const metadataForm = useForm({ + resolver: zodResolver(AppMetadataSchema), + defaultValues: { + deploymentStatus: 'dev', + }, + }); + + // API hooks + const { data: abilities, isLoading: abilitiesLoading } = + vincentApiClient.useListAllAbilitiesQuery(); + const [createApp] = vincentApiClient.useCreateAppMutation(); + const [createAppVersionAbility] = vincentApiClient.useCreateAppVersionAbilityMutation(); + + // Watch form values for persistence + const watchedFormValues = metadataForm.watch(); + + // Clear saved form state + const clearSavedFormState = () => { + localStorage.removeItem(CREATE_APP_STORAGE_KEY); + }; + + // Load saved state on mount + useEffect(() => { + try { + const saved = localStorage.getItem(CREATE_APP_STORAGE_KEY); + if (saved) { + const state: SavedFormState = JSON.parse(saved); + // Only restore if saved within last 24 hours + if (Date.now() - state.savedAt < 24 * 60 * 60 * 1000) { + // Don't restore if we were in the middle of submitting + if (state.currentStep !== 'submitting') { + setCurrentStep(state.currentStep); + } + setSelectedAbilities(new Map(state.selectedAbilities)); + setDelegateeAddresses(state.delegateeAddresses); + // Restore form values + if (state.metadataFormValues) { + Object.entries(state.metadataFormValues).forEach(([key, value]) => { + if (value !== undefined) { + metadataForm.setValue(key as keyof AppMetadataFormData, value as any); + } + }); + // If we're past the metadata step, also set metadataFormData + if (state.currentStep === 'review' || state.currentStep === 'submitting') { + setMetadataFormData(state.metadataFormValues as AppMetadataFormData); + } + } + } else { + // Clear expired saved state + localStorage.removeItem(CREATE_APP_STORAGE_KEY); + } + } + } catch (e) { + // Invalid saved state, clear it + localStorage.removeItem(CREATE_APP_STORAGE_KEY); + } + setIsRestoringState(false); + }, []); + + // Save state on changes (debounced) + useEffect(() => { + if (isRestoringState) return; + + const timeoutId = setTimeout(() => { + const state: SavedFormState = { + currentStep, + selectedAbilities: Array.from(selectedAbilities.entries()), + delegateeAddresses, + metadataFormValues: watchedFormValues, + savedAt: Date.now(), + }; + localStorage.setItem(CREATE_APP_STORAGE_KEY, JSON.stringify(state)); + }, 500); + + return () => clearTimeout(timeoutId); + }, [currentStep, selectedAbilities, delegateeAddresses, watchedFormValues, isRestoringState]); + + // Ability handlers + const handleAbilityAdd = async (ability: Ability) => { + try { + const activeVersion = ability.activeVersion; + if (!activeVersion) { + console.error('Ability has no active version:', ability); + setError('Selected ability has no active version'); + return; + } + + const encodedPackageName = encodeURIComponent(ability.packageName); + const encodedVersion = encodeURIComponent(activeVersion); + const response = await fetch( + `${registryUrl}/ability/${encodedPackageName}/version/${encodedVersion}`, + ); + + if (!response.ok) { + throw new Error( + `Failed to fetch ability version: ${response.status} ${response.statusText}`, + ); + } + + const abilityVersion = await response.json(); + + if (!abilityVersion.ipfsCid) { + throw new Error('Ability version is missing IPFS CID'); + } + + setSelectedAbilities((prevSelected) => { + const newSelected = new Map(prevSelected); + newSelected.set(ability.packageName, { + ability, + ipfsCid: abilityVersion.ipfsCid, + policies: [], + }); + return newSelected; + }); + setError(null); + } catch (err) { + console.error('Failed to add ability:', err); + setError(err instanceof Error ? err.message : 'Failed to add ability'); + } + }; + + const handleAbilityRemove = (packageName: string) => { + const newSelected = new Map(selectedAbilities); + newSelected.delete(packageName); + setSelectedAbilities(newSelected); + setError(null); + }; + + // Delegatee handlers + const handleDelegateeInputChange = (value: string) => { + setDelegateeInput(value); + setDelegateeError(null); + + const address = value.trim(); + + if (ethers.utils.isAddress(address)) { + if (delegateeAddresses.includes(address)) { + setDelegateeError('Address already added'); + return; + } + + setDelegateeAddresses([...delegateeAddresses, address]); + setDelegateeInput(''); + setDelegateeError(null); + } + }; + + const handleRemoveDelegatee = (address: string) => { + setDelegateeAddresses(delegateeAddresses.filter((a) => a !== address)); + setDelegateeError(null); + }; + + // Navigation handlers + const handleNextFromAbilities = () => { + if (selectedAbilities.size === 0) { + setError('Please select at least one ability before continuing'); + return; + } + setError(null); + setCurrentStep('delegatees'); + }; + + const handleNextFromDelegatees = () => { + setError(null); + setCurrentStep('metadata'); + }; + + const handleNextFromMetadata = (data: AppMetadataFormData) => { + setMetadataFormData(data); + setError(null); + setCurrentStep('review'); + }; + + const handleBack = () => { + setError(null); + if (currentStep === 'delegatees') { + setCurrentStep('abilities'); + } else if (currentStep === 'metadata') { + setCurrentStep('delegatees'); + } else if (currentStep === 'review') { + setCurrentStep('metadata'); + } + }; + + // Final submission + const handleFinalSubmit = async () => { + if (!metadataFormData) { + setError('Missing app metadata'); + return; + } + + // Step 1: Ensure user is on Base Sepolia + try { + const canProceed = await ensureChain('Register App On-Chain'); + if (!canProceed) return; + } catch (error) { + setError(error instanceof Error ? error.message : 'Failed to switch network'); + return; + } + + setCurrentStep('submitting'); + setSubmissionStatus('signing'); + setError(null); + + try { + // Step 2: Register app on-chain with abilities + const signer = await getSigner(); + const client = getClient({ signer }); + + const abilityIpfsCids = Array.from(selectedAbilities.values()).map((sa) => sa.ipfsCid); + const abilityPolicies = Array.from(selectedAbilities.values()).map((sa) => sa.policies); + + const result = await client.registerApp({ + delegateeAddresses, + versionAbilities: { + abilityIpfsCids, + abilityPolicies, + }, + }); + + const appId = result.appId; + + // Step 3: Wait a bit for indexing + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Step 4: Create app in registry + setSubmissionStatus('creating-registry'); + try { + await createApp({ + appCreate: { + appId, + name: metadataFormData.name, + description: metadataFormData.description, + contactEmail: metadataFormData.contactEmail, + appUrl: metadataFormData.appUrl, + logo: metadataFormData.logo, + deploymentStatus: metadataFormData.deploymentStatus, + }, + }).unwrap(); + } catch (registryError: any) { + clearSavedFormState(); + navigate(`/developer/apps/appId/${appId}?action=create-in-registry`); + return; + } + + // Step 5: Create all AppVersionAbility records in registry + try { + const abilityCreationPromises = Array.from(selectedAbilities.values()).map( + ({ ability }) => { + console.log( + '[CreateApp] Creating ability:', + ability.packageName, + 'v', + ability.activeVersion, + ); + return createAppVersionAbility({ + appId, + appVersion: 1, + abilityPackageName: ability.packageName, + appVersionAbilityCreate: { + abilityVersion: ability.activeVersion, + hiddenSupportedPolicies: [], + }, + }).unwrap(); + }, + ); + + await Promise.all(abilityCreationPromises); + } catch (abilityError: any) { + clearSavedFormState(); + navigate(`/developer/apps/appId/${appId}/version/1/abilities`); + return; + } + + // Step 6: Navigate to the app detail page + clearSavedFormState(); + navigate(`/developer/apps/appId/${appId}`); + } catch (err: any) { + console.error('Failed to create app on-chain:', err); + + let errorMessage = 'Failed to create app on-chain'; + if (err.code === 'ACTION_REJECTED' || err.message?.includes('user rejected')) { + errorMessage = + 'Transaction was rejected. Please try again and approve the transaction in your wallet.'; + } else if (err.code === 'INSUFFICIENT_FUNDS') { + errorMessage = + 'Insufficient funds to complete the transaction. Please add more ETH to your wallet.'; + } else if (err.message) { + const match = err.message.match(/^([^(]+)/); + errorMessage = match ? match[1].trim() : err.message.substring(0, 100); + } + + setError(errorMessage); + setCurrentStep('review'); + setSubmissionStatus(null); + } + }; + + return { + // Step state + currentStep, + isRestoringState, + + // Abilities + selectedAbilities, + isAbilitySelectorOpen, + setIsAbilitySelectorOpen, + handleAbilityAdd, + handleAbilityRemove, + abilities, + abilitiesLoading, + + // Delegatees + delegateeAddresses, + delegateeInput, + delegateeError, + handleDelegateeInputChange, + handleRemoveDelegatee, + + // Metadata + metadataForm, + metadataFormData, + + // Submission + error, + submissionStatus, + infoMessage, + + // Navigation + handleNextFromAbilities, + handleNextFromDelegatees, + handleNextFromMetadata, + handleBack, + handleFinalSubmit, + }; +} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/index.ts b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/index.ts index 392ee8d3c..af457df40 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/index.ts +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/index.ts @@ -1,27 +1,17 @@ import { AppOverviewWrapper } from './AppOverviewWrapper'; -import { AppVersionDetailWrapper } from './AppVersionDetailWrapper'; import { AppVersionsWrapper } from './AppVersionsWrapper'; -import { AppVersionAbilitiesWrapper } from './AppVersionAbilitiesWrapper'; +import { AppVersionDetailsWrapper } from './AppVersionDetailsWrapper'; import { CreateAppWrapper } from './CreateAppWrapper'; -import { EditAppVersionAbilityButton } from './ui/EditAppVersionAbilityButton.tsx'; -import { UndeleteAppButton } from './ui/UndeleteAppButton'; -import { UndeleteAppVersionButton } from './ui/UndeleteAppVersionButton'; +import { CreateAppVersionWrapper } from './CreateAppVersionWrapper'; import { DeleteAppVersionAbilityButton } from './ui/DeleteAppAbilityVersionButton'; -import { UndeleteAppVersionAbilityButton } from './ui/UndeleteAppVersionAbilityButton'; import { AppsWrapper } from './AppsWrapper'; -import { PublishAppVersionButton } from './ui/PublishAppVersionButton'; export { AppsWrapper, AppOverviewWrapper, - AppVersionDetailWrapper, AppVersionsWrapper, - AppVersionAbilitiesWrapper, + AppVersionDetailsWrapper, CreateAppWrapper, + CreateAppVersionWrapper, DeleteAppVersionAbilityButton, - EditAppVersionAbilityButton, - UndeleteAppButton, - UndeleteAppVersionButton, - UndeleteAppVersionAbilityButton, - PublishAppVersionButton, }; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppMismatchResolution.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppMismatchResolution.tsx deleted file mode 100644 index 1937b03bd..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppMismatchResolution.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Upload, Undo2 } from 'lucide-react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import MutationButtonStates, { SkeletonButton } from '@/components/shared/ui/MutationButtonStates'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; - -type AppMismatchResolutionProps = { - appId: number; - registryDeleted: boolean; - onChainDeleted: boolean; - refetchBlockchainData: () => void; -}; - -export function AppMismatchResolution({ - appId, - registryDeleted, - onChainDeleted, - refetchBlockchainData, -}: AppMismatchResolutionProps) { - const [isProcessing, setIsProcessing] = useState(false); - const [error, setError] = useState(null); - const [success, setSuccess] = useState(null); - const { authInfo, sessionSigs } = useReadAuthInfo(); - - // Registry mutations - const [deleteAppInRegistry, { isLoading: isDeletingInRegistry, error: deleteAppError }] = - vincentApiClient.useDeleteAppMutation(); - const [undeleteAppInRegistry, { isLoading: isUndeletingInRegistry, error: undeleteAppError }] = - vincentApiClient.useUndeleteAppMutation(); - - // Handler for mismatch resolution - commit registry state to on-chain - const handleCommitToOnChain = async () => { - setError(null); - setIsProcessing(true); - - try { - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); - - if (registryDeleted) { - await client.deleteApp({ - appId: Number(appId), - }); - } else { - await client.undeleteApp({ - appId: Number(appId), - }); - } - - const action = registryDeleted ? 'deleted' : 'undeleted'; - setSuccess(`App ${action} on-chain successfully!`); - - setTimeout(() => { - refetchBlockchainData(); - }, 3000); - } catch (error: any) { - if (error?.message?.includes('user rejected')) { - setError('Transaction rejected.'); - } else { - setError(error.message || 'Failed to update on-chain state. Please try again.'); - } - } finally { - setIsProcessing(false); - } - }; - - // Handler for mismatch resolution - revert registry to match on-chain - const handleRevertRegistry = async () => { - setError(null); - setIsProcessing(true); - - try { - if (onChainDeleted) { - await deleteAppInRegistry({ - appId: appId, - }); - } else { - await undeleteAppInRegistry({ - appId: appId, - }); - } - - const action = onChainDeleted ? 'deleted' : 'undeleted'; - setSuccess(`Registry reverted - app is now ${action} to match on-chain!`); - - setTimeout(() => { - refetchBlockchainData(); - }, 1000); - } catch (error: any) { - setError(error.message || 'Failed to revert registry changes. Please try again.'); - } finally { - setIsProcessing(false); - } - }; - - useEffect(() => { - if (!error) return; - - const timer = setTimeout(() => { - setError(null); - }, 3000); - return () => clearTimeout(timer); - }, [error]); - - const isLoading = isProcessing || isDeletingInRegistry || isUndeletingInRegistry; - - if (error || deleteAppError || undeleteAppError) { - const errorMessage = - error || - (deleteAppError as any)?.message || - (undeleteAppError as any)?.message || - 'Failed to update app.'; - return ; - } - - if (success) { - return ; - } - - return ( -
- - -
-

Choose how to resolve this mismatch:

-

- On-chain: {onChainDeleted ? 'Deleted' : 'Active'} • - Registry: {registryDeleted ? 'Deleted' : 'Active'} -

-
- -
- - - -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppPublishedButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppPublishedButtons.tsx index 184131c6e..8a8753e7e 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppPublishedButtons.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppPublishedButtons.tsx @@ -1,15 +1,12 @@ import { useState, useEffect } from 'react'; -import * as Sentry from '@sentry/react'; -import { Plus, Users, Trash2, Edit, RotateCcw, List } from 'lucide-react'; +import { Plus, Users, Trash2, Edit, RotateCcw, List, CheckCircle } from 'lucide-react'; import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; import { App } from '@/types/developer-dashboard/appTypes'; import { App as ContractApp } from '@lit-protocol/vincent-contracts-sdk'; import MutationButtonStates from '@/components/shared/ui/MutationButtonStates'; -import { AppMismatchResolution } from './AppMismatchResolution'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { useWagmiSigner } from '@/hooks/developer-dashboard/useWagmiSigner'; +import { useEnsureChain } from '@/hooks/developer-dashboard/useEnsureChain'; +import { theme, fonts } from '@/lib/themeClasses'; import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; interface AppPublishedButtonsProps { @@ -28,37 +25,34 @@ export function AppPublishedButtons({ const [isProcessing, setIsProcessing] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); - const { authInfo, sessionSigs } = useReadAuthInfo(); + const { getSigner } = useWagmiSigner(); + const { ensureChain } = useEnsureChain(); - // Registry mutations - const [undeleteAppInRegistry, { isLoading: isUndeletingInRegistry, error: undeleteAppError }] = - vincentApiClient.useUndeleteAppMutation(); + // On-chain is the source of truth for deleted state + const isDeleted = appBlockchainData.isDeleted; - const registryDeleted = appData.isDeleted ?? false; - const onChainDeleted = appBlockchainData.isDeleted; - - // Determine if there's a mismatch (only when not processing) - const hasMismatch = !isProcessing && registryDeleted !== onChainDeleted; - - // Handler for app undelete + // Handler for app undelete (on-chain only) const handleUndelete = async () => { + // Ensure user is on Base Sepolia before starting + try { + const canProceed = await ensureChain('Undelete App'); + if (!canProceed) return; // Chain was switched, user needs to click again + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to switch network'); + return; + } + setError(null); setIsProcessing(true); try { - // Step 1: Update on-chain first (if published) - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); + const signer = await getSigner(); + const client = getClient({ signer }); await client.undeleteApp({ appId: appData.appId, }); - // Step 2: Update registry (only after on-chain succeeds) - await undeleteAppInRegistry({ - appId: appData.appId, - }); - setSuccess(`App undeleted successfully!`); setTimeout(() => { @@ -71,12 +65,6 @@ export function AppPublishedButtons({ setError('Transaction rejected.'); } else { setError(`Failed to undelete app. Please try again.`); - Sentry.captureException(error, { - extra: { - context: 'AppPublishedButtons.undeleteApp', - userPkp: authInfo?.userPKP?.ethAddress, - }, - }); } } finally { setIsProcessing(false); @@ -92,122 +80,111 @@ export function AppPublishedButtons({ return () => clearTimeout(timer); }, [error]); - const isLoading = isProcessing || isUndeletingInRegistry; - - if (error || undeleteAppError) { - const errorMessage = - error || - (undeleteAppError && typeof undeleteAppError === 'object' && 'message' in undeleteAppError - ? String(undeleteAppError.message) - : 'Failed to update app.'); - return ; + if (error) { + return ; } if (success) { return ; } - // Show mismatch resolution component if there's a mismatch - if (hasMismatch) { + // Show regular buttons when not deleted + if (!isDeleted) { return ( - +
+ {/* App Management Section */} +
+

+ App Management +

+
+ onOpenMutation('edit-published-app')} + variant="orange" + iconBg={`${theme.brandOrange}1A`} + iconColor={theme.brandOrange} + hoverBorderColor={theme.brandOrange} + /> + + onOpenMutation('manage-delegatees')} + variant="orange" + iconBg={`${theme.brandOrange}1A`} + iconColor={theme.brandOrange} + hoverBorderColor={theme.brandOrange} + /> + + onOpenMutation('delete-app')} + variant="danger" + borderColor="rgb(254 202 202 / 0.5)" + hoverBorderColor="rgb(239 68 68)" + /> +
+
+ + {/* Version Management Section */} +
+

+ Version Management +

+
+ onOpenMutation('versions')} + variant="orange" + iconBg={`${theme.brandOrange}1A`} + iconColor={theme.brandOrange} + hoverBorderColor={theme.brandOrange} + /> + + onOpenMutation('create-app-version')} + variant="orange" + iconBg={`${theme.brandOrange}1A`} + iconColor={theme.brandOrange} + hoverBorderColor={theme.brandOrange} + /> + + onOpenMutation('set-active-version')} + variant="orange" + iconBg={`${theme.brandOrange}1A`} + iconColor={theme.brandOrange} + hoverBorderColor={theme.brandOrange} + /> +
+
+
); } - // Show regular delete/undelete buttons when states are in sync + // Show undelete button when deleted return ( -
- {/* Regular buttons when not deleted */} - {!registryDeleted && ( - <> - {/* App Management Section */} -
-

- App Management -

-
- onOpenMutation('edit-published-app')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - onOpenMutation('manage-delegatees')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - onOpenMutation('delete-app')} - variant="danger" - borderColor="rgb(254 202 202 / 0.5)" - hoverBorderColor="rgb(239 68 68)" - /> -
-
- - {/* Version Management Section */} -
-

- Version Management -

-
- onOpenMutation('versions')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - onOpenMutation('create-app-version')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> -
-
- - )} - - {/* Undelete button when deleted */} - {registryDeleted && ( - - )} -
+ ); } diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppUnpublishedButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppUnpublishedButtons.tsx deleted file mode 100644 index 86d8a0a61..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppUnpublishedButtons.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Edit, Trash2, Plus, List } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; -import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; - -interface AppUnpublishedButtonsProps { - onOpenMutation: (mutationType: string) => void; -} - -export function AppUnpublishedButtons({ onOpenMutation }: AppUnpublishedButtonsProps) { - return ( -
- {/* App Management Section */} -
-

- App Management -

-
- onOpenMutation('edit-app')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - onOpenMutation('delete-app')} - variant="danger" - borderColor="rgb(254 202 202 / 0.5)" - hoverBorderColor="rgb(239 68 68)" - /> -
-
- - {/* Version Management Section */} -
-

- Version Management -

-
- onOpenMutation('versions')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - onOpenMutation('create-app-version')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> -
-
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionDeletedButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionDeletedButtons.tsx deleted file mode 100644 index 179c681a9..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionDeletedButtons.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { UndeleteAppVersionButton } from './UndeleteAppVersionButton'; -import { AppVersion } from '@/types/developer-dashboard/appTypes'; - -interface AppVersionDeletedButtonsProps { - appVersion: AppVersion; -} - -export function AppVersionDeletedButtons({ appVersion }: AppVersionDeletedButtonsProps) { - return ( -
-
- -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionMismatchResolution.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionMismatchResolution.tsx deleted file mode 100644 index 1557541fb..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionMismatchResolution.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Upload, Undo2 } from 'lucide-react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { getClient } from '@lit-protocol/vincent-contracts-sdk'; -import MutationButtonStates, { SkeletonButton } from '@/components/shared/ui/MutationButtonStates'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; - -type AppVersionMismatchResolutionProps = { - appId: number; - versionId: number; - registryEnabled: boolean; - onChainEnabled: boolean; - refetchBlockchainAppVersionData: () => void; -}; - -export function AppVersionMismatchResolution({ - appId, - versionId, - registryEnabled, - onChainEnabled, - refetchBlockchainAppVersionData, -}: AppVersionMismatchResolutionProps) { - const [isProcessing, setIsProcessing] = useState(false); - const [error, setError] = useState(null); - const [success, setSuccess] = useState(null); - const { authInfo, sessionSigs } = useReadAuthInfo(); - - // Mutations for enable/disable - const [enableAppVersion, { isLoading: isEnabling, error: enableAppVersionError }] = - vincentApiClient.useEnableAppVersionMutation(); - const [disableAppVersion, { isLoading: isDisabling, error: disableAppVersionError }] = - vincentApiClient.useDisableAppVersionMutation(); - - // Handler for mismatch resolution - commit registry state to on-chain - const handleCommitToOnChain = async () => { - setError(null); - setIsProcessing(true); - - try { - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); - - await client.enableAppVersion({ - appId: Number(appId), - appVersion: Number(versionId), - enabled: registryEnabled, - }); - - const action = registryEnabled ? 'enabled' : 'disabled'; - setSuccess(`App version ${action} on-chain successfully!`); - - setTimeout(() => { - refetchBlockchainAppVersionData(); - }, 3000); - } catch (error: any) { - if (error?.message?.includes('user rejected')) { - setError('Transaction rejected.'); - } else { - setError(error.message || 'Failed to update on-chain state. Please try again.'); - } - } finally { - setIsProcessing(false); - } - }; - - // Handler for mismatch resolution - revert registry to match on-chain - const handleRevertRegistry = async () => { - setError(null); - setIsProcessing(true); - - try { - if (onChainEnabled) { - await enableAppVersion({ - appId: Number(appId), - version: Number(versionId), - }); - } else { - await disableAppVersion({ - appId: Number(appId), - version: Number(versionId), - }); - } - - const action = onChainEnabled ? 'enabled' : 'disabled'; - setSuccess(`Registry reverted - app version is now ${action} to match on-chain!`); - - setTimeout(() => { - refetchBlockchainAppVersionData(); - }, 1000); - } catch (error: any) { - setError(error.message || 'Failed to revert registry changes. Please try again.'); - } finally { - setIsProcessing(false); - } - }; - - useEffect(() => { - if (!error) return; - - const timer = setTimeout(() => { - setError(null); - }, 3000); - return () => clearTimeout(timer); - }, [error]); - - const isLoading = isProcessing || isEnabling || isDisabling; - - if (error || enableAppVersionError || disableAppVersionError) { - const errorMessage = - error || - (enableAppVersionError as any)?.message || - (disableAppVersionError as any)?.message || - 'Failed to update app version.'; - return ; - } - - if (success) { - return ; - } - - return ( -
- - -
-

Choose how to resolve this mismatch:

-

- On-chain: {onChainEnabled ? 'Enabled' : 'Disabled'} • - Registry: {registryEnabled ? 'Enabled' : 'Disabled'} -

-
- -
- - - -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionPublishedButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionPublishedButtons.tsx deleted file mode 100644 index 4712e5ecb..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionPublishedButtons.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import { useState } from 'react'; -import { Power, PowerOff, CheckCircle } from 'lucide-react'; -import { AppVersion } from '@/types/developer-dashboard/appTypes'; -import { AppVersion as ContractAppVersion, getClient } from '@lit-protocol/vincent-contracts-sdk'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { AppVersionMismatchResolution } from './AppVersionMismatchResolution'; -import { initPkpSigner } from '@/utils/developer-dashboard/initPkpSigner'; -import useReadAuthInfo from '@/hooks/user-dashboard/useAuthInfo'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; -import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; - -interface AppVersionPublishedButtonsProps { - appId: number; - versionId: number; - appVersionData: AppVersion; - appVersionBlockchainData: ContractAppVersion; - refetchBlockchainAppVersionData: () => void; - appActiveVersion?: number | null; -} - -export function AppVersionPublishedButtons({ - appId, - versionId, - appVersionData, - appVersionBlockchainData, - refetchBlockchainAppVersionData, - appActiveVersion, -}: AppVersionPublishedButtonsProps) { - const [isProcessing, setIsProcessing] = useState(false); - const [actionSuccess, setActionSuccess] = useState<'enable' | 'disable' | 'setActive' | null>( - null, - ); - const [actionError, setActionError] = useState(null); - const { authInfo, sessionSigs } = useReadAuthInfo(); - - // Mutations for enable/disable - const [enableAppVersion, { isLoading: isEnabling }] = - vincentApiClient.useEnableAppVersionMutation(); - const [disableAppVersion, { isLoading: isDisabling }] = - vincentApiClient.useDisableAppVersionMutation(); - const [setActiveVersion, { isLoading: isSettingActive }] = - vincentApiClient.useSetAppActiveVersionMutation(); - - const registryEnabled = appVersionData.enabled; - const onChainEnabled = appVersionBlockchainData.enabled; - - // Determine if there's a mismatch (only when not processing) - const hasMismatch = !isProcessing && registryEnabled !== onChainEnabled; - - // Unified handler for enable/disable operations - const handleVersionToggle = async (targetState: boolean) => { - setActionSuccess(null); - setActionError(null); - setIsProcessing(true); - - try { - // Step 1: Update registry - if (targetState) { - await enableAppVersion({ - appId: Number(appId), - version: Number(versionId), - }).unwrap(); - } else { - await disableAppVersion({ - appId: Number(appId), - version: Number(versionId), - }).unwrap(); - } - - // Step 2: Update on-chain - const pkpSigner = await initPkpSigner({ authInfo, sessionSigs }); - const client = getClient({ signer: pkpSigner }); - - await client.enableAppVersion({ - appId: Number(appId), - appVersion: Number(versionId), - enabled: targetState, - }); - - refetchBlockchainAppVersionData(); - - setActionSuccess(targetState ? 'enable' : 'disable'); - - // Clear success state after 3 seconds - setTimeout(() => { - setActionSuccess(null); - }, 3000); - } catch (error) { - console.error('Failed to toggle app version:', error); - const message = error instanceof Error ? error.message : String(error); - if (message.includes('user rejected')) { - setActionError('Transaction rejected'); - } else { - const action = targetState ? 'enable' : 'disable'; - setActionError(`Failed to ${action} version`); - } - - // Clear error after 5 seconds - setTimeout(() => { - setActionError(null); - }, 5000); - } finally { - setIsProcessing(false); - } - }; - - // Handler for setting active version - const handleSetActiveVersion = async () => { - setActionSuccess(null); - setActionError(null); - setIsProcessing(true); - - try { - await setActiveVersion({ - appId: Number(appId), - appSetActiveVersion: { - activeVersion: Number(versionId), - }, - }).unwrap(); - - refetchBlockchainAppVersionData(); - - setActionSuccess('setActive'); - - // Clear success state after 3 seconds - setTimeout(() => { - setActionSuccess(null); - }, 3000); - } catch (error) { - console.error('Failed to set active version:', error); - const message = error instanceof Error ? error.message : String(error); - setActionError(`Failed to set active version: ${message}`); - - // Clear error after 5 seconds - setTimeout(() => { - setActionError(null); - }, 5000); - } finally { - setIsProcessing(false); - } - }; - - const isLoading = isProcessing || isEnabling || isDisabling || isSettingActive; - const isActiveVersion = appActiveVersion === versionId; - - // Show mismatch resolution component if there's a mismatch - if (hasMismatch) { - return ( - - ); - } - - // Show regular enable/disable buttons when states are in sync - return ( -
- {/* Error Message */} - {actionError && } - -
- {/* Set as Active Version Button - Only show when not active and enabled */} - {!isActiveVersion && registryEnabled && ( - - )} - - {/* Enable Button - Only show when disabled */} - {!registryEnabled && ( - handleVersionToggle(true)} - isLoading={isLoading} - disabled={actionSuccess === 'enable'} - variant="success" - borderColor="rgb(134 239 172 / 0.3)" - hoverBorderColor={theme.brandOrange} - /> - )} - - {/* Disable Button - Only show when enabled */} - {registryEnabled && ( - handleVersionToggle(false)} - isLoading={isLoading} - disabled={actionSuccess === 'disable'} - variant={actionSuccess === 'disable' ? 'success' : 'danger'} - borderColor="rgb(252 165 165 / 0.3)" - hoverBorderColor={theme.brandOrange} - /> - )} -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionUnpublishedButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionUnpublishedButtons.tsx deleted file mode 100644 index 7ae0ddc0a..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/AppVersionUnpublishedButtons.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import { Edit, Plus, Trash2 } from 'lucide-react'; -import { PublishAppVersionWrapper } from '../PublishAppVersionWrapper'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; -import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; - -interface AppVersionUnpublishedButtonsProps { - appId: number; - versionId: number; - isVersionEnabled: boolean; - isAppPublished: boolean; - onOpenMutation: (mutationType: string) => void; -} - -export function AppVersionUnpublishedButtons({ - appId, - versionId, - isVersionEnabled, - isAppPublished, - onOpenMutation, -}: AppVersionUnpublishedButtonsProps) { - const navigate = useNavigate(); - - return ( -
- {/* Publish App Version - First row, full width */} - {isVersionEnabled && } - - {/* Other actions - Second row, 3 columns */} -
- {isVersionEnabled && ( - <> - {/* Edit Version Action */} - onOpenMutation('edit-version')} - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - {/* Manage Abilities Action */} - - navigate(`/developer/apps/appId/${appId}/version/${versionId}/abilities`) - } - variant="orange" - iconBg={`${theme.brandOrange}1A`} - iconColor={theme.brandOrange} - hoverBorderColor={theme.brandOrange} - /> - - )} - - {/* Delete Version Action */} - onOpenMutation('delete-version')} - variant="danger" - borderColor="rgb(254 202 202 / 0.3)" - hoverBorderColor="rgb(248 113 113)" - /> -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/EditAppVersionAbilityButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/EditAppVersionAbilityButton.tsx deleted file mode 100644 index 4b5058477..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/EditAppVersionAbilityButton.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useEffect } from 'react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { - EditAppVersionAbilityForm, - type EditAppVersionAbilityFormData, -} from '../../forms/EditAppVersionAbilityForm.tsx'; -import { AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { sortedSupportedPolicies } from '@/utils/developer-dashboard/sortSupportedPolicies'; -import LoadingLock from '@/components/shared/ui/LoadingLock'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; - -interface EditAppVersionAbilityButtonProps { - appId: number; - versionId: number; - ability: AppVersionAbility; - onSuccess: () => void; - onCancel: () => void; -} - -export function EditAppVersionAbilityButton({ - appId, - versionId, - ability, - onSuccess, - onCancel, -}: EditAppVersionAbilityButtonProps) { - // Fetching - const { - data: allPolicies, - isLoading: isLoadingPolicies, - isError: isErrorPolicies, - } = vincentApiClient.useListAllPoliciesQuery(); - - const { - data: abilityVersionData, - isLoading: isLoadingAbilityVersion, - isError: isErrorAbilityVersion, - } = vincentApiClient.useGetAbilityVersionQuery({ - packageName: ability.abilityPackageName, - version: ability.abilityVersion, - }); - - // Mutation - const [editAppVersionAbility, { isLoading, isSuccess, isError, data, error }] = - vincentApiClient.useEditAppVersionAbilityMutation(); - - // Effect - useEffect(() => { - if (!isSuccess || !data) return; - const timer = setTimeout(() => { - onSuccess(); - }, 1500); - - return () => clearTimeout(timer); - }, [isSuccess, data, onSuccess]); - - // Loading states - if (isLoadingPolicies || isLoadingAbilityVersion) return ; - if (isErrorPolicies) return ; - if (isErrorAbilityVersion) - return ; - - // Early return if data is not available - if (!abilityVersionData) { - return ; - } - - const supportedPolicies = sortedSupportedPolicies(allPolicies || [], abilityVersionData); - - // Mutation states - if (isLoading) return ; - - if (isSuccess && data) { - return ; - } - - if (isError && error) { - const errorMessage = getErrorMessage(error, 'Failed to update ability'); - return ; - } - - const handleSubmit = async (data: EditAppVersionAbilityFormData) => { - await editAppVersionAbility({ - appId, - appVersion: versionId, - abilityPackageName: ability.abilityPackageName, - appVersionAbilityEdit: data, - }); - }; - - return ( - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PackageInstallCommand.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PackageInstallCommand.tsx index 035a54871..c801d17c6 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PackageInstallCommand.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PackageInstallCommand.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { Copy, Check } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface AbilityInfo { abilityPackageName: string; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PublishAppVersionButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PublishAppVersionButton.tsx deleted file mode 100644 index 352ab2532..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/PublishAppVersionButton.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Upload, CheckCircle, XCircle } from 'lucide-react'; -import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; -import { PackageInstallCommand } from './PackageInstallCommand'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; - -interface AbilityInfo { - abilityPackageName: string; - abilityVersion: string; - isDeleted?: boolean; - hiddenSupportedPolicies?: string[]; -} - -interface AbilityVersionData { - supportedPolicies?: Record; -} - -interface PublishAppVersionButtonProps { - isSubmitting?: boolean; - onSubmit: () => Promise; - publishResult?: { - success: boolean; - message?: string; - } | null; - versionAbilities?: AbilityInfo[]; - abilityVersionsData?: Record; -} - -export function PublishAppVersionButton({ - isSubmitting = false, - onSubmit, - publishResult = null, - versionAbilities = [], - abilityVersionsData = {}, -}: PublishAppVersionButtonProps) { - const hasError = publishResult && !publishResult.success; - const hasSuccess = publishResult && publishResult.success; - - // Determine state-based props - const getIcon = () => { - if (hasSuccess) return CheckCircle; - if (hasError) return XCircle; - return Upload; - }; - - const getTitle = () => { - if (isSubmitting) return 'Publishing App Version...'; - if (hasSuccess) return 'Published Successfully!'; - if (hasError) return 'Publication Failed'; - return 'Publish App Version'; - }; - - const getDescription = () => { - if (isSubmitting) return 'Publishing your app version to the blockchain...'; - if (hasSuccess) return publishResult?.message || 'App version published successfully!'; - if (hasError) return publishResult?.message || 'Failed to publish app version'; - return 'Make this version available to users'; - }; - - const getVariant = () => { - if (hasSuccess) return 'success' as const; - if (hasError) return 'danger' as const; - return 'orange' as const; - }; - - const showInstallCommand = !hasSuccess && !hasError; - - return ( -
- - - {showInstallCommand && ( - - )} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppButton.tsx deleted file mode 100644 index 8e74c9eb3..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppButton.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { ArchiveRestore } from 'lucide-react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; - -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { App } from '@/types/developer-dashboard/appTypes'; -import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; - -interface UndeleteAppWrapperProps { - app: App; -} - -export function UndeleteAppButton({ app }: UndeleteAppWrapperProps) { - // Mutation - const [undeleteApp, { isLoading, isSuccess, isError, data, error }] = - vincentApiClient.useUndeleteAppMutation(); - - // Navigation - const navigate = useNavigate(); - - // Effect - useEffect(() => { - if (isSuccess && data && app) { - navigate(`/developer/apps/appId/${app.appId}`); - } - }, [isSuccess, data, app]); - - // Error states - if (!app) return ; - - // Success state - if (isSuccess && data) { - return ; - } - - // Error state - if (isError && error) { - const errorMessage = getErrorMessage(error, 'Failed to undelete app'); - return ; - } - - const handleSubmit = async () => { - await undeleteApp({ - appId: app.appId, - }); - }; - - return ( - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionAbilityButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionAbilityButton.tsx deleted file mode 100644 index cd53a60d7..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionAbilityButton.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import Loading from '@/components/shared/ui/Loading'; -import { ArchiveRestore } from 'lucide-react'; -import { AppVersionAbility } from '@/types/developer-dashboard/appTypes'; - -interface UndeleteAppVersionAbilityWrapperProps { - appVersionAbility: AppVersionAbility; -} - -export function UndeleteAppVersionAbilityButton({ - appVersionAbility, -}: UndeleteAppVersionAbilityWrapperProps) { - // Mutation - const [undeleteAppVersionAbility, { isLoading, isSuccess, isError, data, error }] = - vincentApiClient.useUndeleteAppVersionAbilityMutation(); - - // Loading states - if (isLoading) return ; - - // Error states - if (!appVersionAbility) - return ; - - // Mutation states - if (isLoading) { - return ; - } - - if (isSuccess && data) { - return ; - } - - if (isError && error) { - const errorMessage = getErrorMessage(error, 'Failed to undelete app version ability'); - return ; - } - - const handleSubmit = async () => { - await undeleteAppVersionAbility({ - appId: appVersionAbility.appId, - appVersion: appVersionAbility.appVersion, - abilityPackageName: appVersionAbility.abilityPackageName, - }); - }; - - return ( - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionButton.tsx deleted file mode 100644 index da2d3d108..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/app/wrappers/ui/UndeleteAppVersionButton.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { StatusMessage } from '@/components/shared/ui/statusMessage'; -import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { ArchiveRestore } from 'lucide-react'; -import { AppVersion } from '@/types/developer-dashboard/appTypes'; - -interface UndeleteAppVersionWrapperProps { - appVersion: AppVersion; -} - -export function UndeleteAppVersionButton({ appVersion }: UndeleteAppVersionWrapperProps) { - // Mutation - const [undeleteAppVersion, { isLoading, isSuccess, isError, data, error }] = - vincentApiClient.useUndeleteAppVersionMutation(); - - // Navigation - const navigate = useNavigate(); - - // Effect - useEffect(() => { - if (isSuccess && data && appVersion) { - navigate(`/developer/apps/appId/${appVersion.appId}`); - } - }, [isSuccess, data, appVersion]); - - // Loading states - if (isLoading) return ; - - // Error states - if (!appVersion) return ; - - // Mutation states - if (isLoading) { - return ; - } - - if (isSuccess && data) { - return ; - } - - if (isError && error) { - const errorMessage = getErrorMessage(error, 'Failed to undelete app version'); - return ; - } - - const handleSubmit = async () => { - await undeleteAppVersion({ - appId: appVersion.appId, - version: appVersion.version, - }); - }; - - return ( - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/auth/SignInScreen.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/auth/SignInScreen.tsx new file mode 100644 index 000000000..709af464c --- /dev/null +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/auth/SignInScreen.tsx @@ -0,0 +1,291 @@ +import { useState, useEffect } from 'react'; +import { useConnect, useDisconnect } from 'wagmi'; +import { useNavigate } from 'react-router-dom'; +import { Wallet, AlertCircle, Loader2, ChevronDown, ChevronUp, Check, LogOut } from 'lucide-react'; +import { Button } from '@/components/shared/ui/button'; +import { useAuth } from '@/hooks/developer-dashboard/useAuth'; +import { theme, fonts } from '@/lib/themeClasses'; +import { cn } from '@/lib/utils'; +import { useTheme } from '@/hooks/useTheme'; +import { ExplorerNav } from '@/components/explorer/ui/ExplorerNav'; +import { Footer } from '@/components/shared/Footer'; + +export function SignInScreen() { + const { connectors, connect, isPending: isConnecting } = useConnect(); + const { disconnect } = useDisconnect(); + const { isWalletConnected, isAuthenticated, isSigningIn, error, signIn, walletAddress } = + useAuth(); + const isDark = useTheme(); + const navigate = useNavigate(); + + // Track which steps are expanded + const [step1Expanded, setStep1Expanded] = useState(!isWalletConnected); + const [step2Expanded, setStep2Expanded] = useState(isWalletConnected && !isAuthenticated); + + // Update expansion state when wallet connection changes + useEffect(() => { + if (isWalletConnected) { + setStep1Expanded(false); + setStep2Expanded(true); + } else { + setStep1Expanded(true); + setStep2Expanded(false); + } + }, [isWalletConnected]); + + const handleConnectWallet = (connectorId: string) => { + const connector = connectors.find((c) => c.id === connectorId); + if (connector) { + connect({ connector }); + } + }; + + const handleSignIn = () => { + signIn(); + }; + + // Step 1 is complete when wallet is connected + const step1Complete = isWalletConnected; + + return ( +
+ {/* Header */} + navigate(path)} /> + + {/* Main content */} +
+
+
+ {/* Logo */} +
+ Vincent +
+ + {/* Title */} +
+

+ Developer Dashboard +

+

+ Connect your wallet to access the developer dashboard +

+
+ + {/* Error Message */} + {error && ( +
+ +
+

+ Sign-in Failed +

+

+ {error} +

+
+
+ )} + + {/* Sign In Steps */} +
+ {/* Step 1: Connect Wallet */} +
+ + + {/* Step 1 Content */} + {(step1Expanded || !step1Complete) && ( +
+
+ {isConnecting ? ( + + ) : step1Complete ? ( +
+
+ +
+

+ Connected +

+

+ {walletAddress + ? `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}` + : ''} +

+
+
+ +
+ ) : ( +
+ {connectors.map((connector) => ( + + ))} +
+ )} +
+
+ )} +
+ + {/* Step 2: Sign Message */} +
+ + + {/* Step 2 Content */} + {step1Complete && step2Expanded && ( +
+
+ {isSigningIn ? ( + + ) : ( + + )} +
+
+ )} +
+
+
+
+
+ + {/* Footer */} +
+
+ ); +} + +export default SignInScreen; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/ImageUploadField.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/ImageUploadField.tsx index 85de19a1b..775624cb0 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/ImageUploadField.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/ImageUploadField.tsx @@ -15,7 +15,7 @@ import { UseFormClearErrors, } from 'react-hook-form'; import { processImageUpload, cleanupPreviewUrl } from '@/utils/developer-dashboard/imageUtils'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface ImageUploadFieldProps { name: string; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/PolicyCheckboxField.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/PolicyCheckboxField.tsx index f333f85bb..09883ca95 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/PolicyCheckboxField.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/form-fields/PolicyCheckboxField.tsx @@ -3,7 +3,7 @@ import { Label } from '@/components/shared/ui/label'; import { UseFormWatch, UseFormSetValue } from 'react-hook-form'; import { Policy } from '@/types/developer-dashboard/appTypes'; import { PolicyWithVersion } from '@/utils/developer-dashboard/sortSupportedPolicies'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { Info } from 'lucide-react'; import { useState } from 'react'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/ChangePolicyOwnerForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/ChangePolicyOwnerForm.tsx index ac869ae0f..e66b1565c 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/ChangePolicyOwnerForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/ChangePolicyOwnerForm.tsx @@ -7,7 +7,7 @@ import { Button } from '@/components/shared/ui/button'; import { Form } from '@/components/shared/ui/form'; import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField } from '../../form-fields'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; //const { policyOwnerDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyForm.tsx index aa3d97fcb..5d85935cc 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyForm.tsx @@ -8,7 +8,7 @@ import { Button } from '@/components/shared/ui/button'; import { TextField, LongTextField, ImageUploadField } from '../../form-fields'; import { DeploymentStatusSelectField } from '../../form-fields/array/DeploymentStatusSelectField'; import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { policyDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyVersionForm.tsx index 65bbf6d15..16fc72aab 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyVersionForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/CreatePolicyVersionForm.tsx @@ -8,7 +8,7 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { TextField, LongTextField } from '../../form-fields'; import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; import { Policy } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { policyVersionDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/DeletePolicyForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/DeletePolicyForm.tsx index eb4a2d538..2ad9c51d2 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/DeletePolicyForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/DeletePolicyForm.tsx @@ -7,7 +7,7 @@ import { Form } from '@/components/shared/ui/form'; import { Button } from '@/components/shared/ui/button'; import { TextField } from '../../form-fields'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; function buildConfirmationString(policyPackageName: string): string { return `I want to delete policy ${policyPackageName}`; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyForm.tsx index e20e1fdf2..4bc841312 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyForm.tsx @@ -9,7 +9,7 @@ import { TextField, LongTextField, SelectField, ImageUploadField } from '../../f import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; import { Policy, PolicyVersion } from '@/types/developer-dashboard/appTypes'; import { DeploymentStatusSelectField } from '../../form-fields/array/DeploymentStatusSelectField'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; const { policyDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyVersionForm.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyVersionForm.tsx index dbb712002..29bcf0c33 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyVersionForm.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/forms/EditPolicyVersionForm.tsx @@ -9,7 +9,7 @@ import { PolicyVersion } from '@/types/developer-dashboard/appTypes'; import { LongTextField } from '../../form-fields'; import { docSchemas } from '@lit-protocol/vincent-registry-sdk'; import { extractErrorMessage } from '@/utils/developer-dashboard/app-forms'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; const { policyVersionDoc } = docSchemas; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyDetailsView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyDetailsView.tsx index 6b6d8525f..dbe328092 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyDetailsView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyDetailsView.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { Policy, PolicyVersion } from '@/types/developer-dashboard/appTypes'; import { Badge } from '@/components/shared/ui/badge'; import { Logo } from '@/components/shared/ui/Logo'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { motion } from 'framer-motion'; import { AppDetail } from '@/components/developer-dashboard/ui/AppDetail'; import { PolicyActionButtons } from '../wrappers/ui/PolicyActionButtons'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyListView.tsx index 1fddd3150..3ca8dda8c 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyListView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyListView.tsx @@ -5,7 +5,7 @@ import { motion } from 'framer-motion'; import { formatDate } from '@/utils/developer-dashboard/formatDateAndTime'; import { UndeletePolicyButton } from '../wrappers'; import { Policy } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { PolicyCard } from '../../ui/PolicyCard'; import { Logo } from '@/components/shared/ui/Logo'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionDetailsView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionDetailsView.tsx index b7bfb531e..92b0a047f 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionDetailsView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionDetailsView.tsx @@ -3,7 +3,7 @@ import { ExternalLink } from 'lucide-react'; import { motion } from 'framer-motion'; import { Policy, PolicyVersion } from '@/types/developer-dashboard/appTypes'; import { Badge } from '@/components/shared/ui/badge'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { PolicyVersionActionButtons } from '../wrappers/ui/PolicyVersionActionButtons'; import { AppDetail } from '@/components/developer-dashboard/ui/AppDetail'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionsListView.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionsListView.tsx index 8752fa8c6..1108b9da5 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionsListView.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/views/PolicyVersionsListView.tsx @@ -3,7 +3,7 @@ import { PolicyVersion, Policy } from '@/types/developer-dashboard/appTypes'; import { Package, Plus } from 'lucide-react'; import { motion } from 'framer-motion'; import { UndeletePolicyVersionButton } from '../../policy/wrappers'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { VersionCard } from '@/components/developer-dashboard/ui/VersionCard'; interface PolicyVersionsListViewProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyOverviewWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyOverviewWrapper.tsx index 1f31d6d41..1cdbcb961 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyOverviewWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyOverviewWrapper.tsx @@ -16,7 +16,7 @@ import { DialogHeader, DialogTitle, } from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; type ViewType = | 'details' diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyVersionDetailsWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyVersionDetailsWrapper.tsx index 8f101cd67..df0c749a6 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyVersionDetailsWrapper.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/PolicyVersionDetailsWrapper.tsx @@ -14,7 +14,7 @@ import { DialogHeader, DialogTitle, } from '@/components/shared/ui/dialog'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; type ViewType = 'details' | 'edit-version' | 'delete-version'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyActionButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyActionButtons.tsx index 21fea244f..31c9014b1 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyActionButtons.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyActionButtons.tsx @@ -1,5 +1,5 @@ import { Edit, Trash2, Plus, List, ArrowLeftRight } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; interface PolicyActionButtonsProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyVersionActionButtons.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyVersionActionButtons.tsx index 1c82aaf13..1ef81e7d6 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyVersionActionButtons.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/PolicyVersionActionButtons.tsx @@ -1,5 +1,5 @@ import { Edit, Trash2 } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { ActionButton } from '@/components/developer-dashboard/ui/ActionButton'; interface PolicyVersionActionButtonsProps { diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/UndeletePolicyButton.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/UndeletePolicyButton.tsx index aa90cc690..f0ebbfe08 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/UndeletePolicyButton.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/policy/wrappers/ui/UndeletePolicyButton.tsx @@ -6,7 +6,7 @@ import { StatusMessage } from '@/components/shared/ui/statusMessage'; import { getErrorMessage } from '@/utils/developer-dashboard/app-forms'; import Loading from '@/components/shared/ui/Loading'; import { Policy } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface UndeletePolicyWrapperProps { policy: Policy; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AbilityList.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AbilityList.tsx deleted file mode 100644 index 3c0855c54..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AbilityList.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import { FileText, GitBranch } from 'lucide-react'; -import { useMemo } from 'react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { Ability, AbilityVersion } from '@/types/developer-dashboard/appTypes'; - -interface AbilityListProps { - abilities: Ability[]; - selectedAbility: Ability | null; - selectedAbilityView: string | null; - expandedMenus: Set; - onAbilitySelection: (ability: Ability) => void; - onAbilityViewSelection: (viewId: string) => void; - onToggleMenu: (menuId: string) => void; -} - -export function AbilityList({ - abilities, - selectedAbility, - selectedAbilityView, - expandedMenus, - onAbilitySelection, - onAbilityViewSelection, - onToggleMenu, -}: AbilityListProps) { - const { - data: abilityVersions, - isLoading: versionsLoading, - error: versionsError, - } = vincentApiClient.useGetAbilityVersionsQuery( - { packageName: selectedAbility?.packageName || '' }, - { skip: !selectedAbility?.packageName }, - ); // FIXME: Sidebar-related patch, we don't want to fetch versions if no ability is selected - - const sortedVersions = useMemo(() => { - if (!abilityVersions || abilityVersions.length === 0) return []; - // Filter out deleted versions from sidebar dropdown - const activeVersions = abilityVersions.filter((version: AbilityVersion) => !version.isDeleted); - return [...activeVersions].sort((a: AbilityVersion, b: AbilityVersion) => - b.version.localeCompare(a.version, undefined, { numeric: true }), - ); - }, [abilityVersions]); - - const abilityMenuItems = useMemo(() => { - if (!selectedAbility) return []; - - return [ - { id: 'ability-details', label: 'Ability Details', icon: FileText }, - { - id: 'ability-versions', - label: 'Ability Versions', - icon: GitBranch, - submenu: - sortedVersions.length > 0 - ? sortedVersions.map((version: AbilityVersion) => ({ - id: `version-${version.version}`, - label: `Version ${version.version}${version.version === selectedAbility.activeVersion ? ' (Active)' : ''}`, - })) - : [{ id: 'no-versions', label: 'No versions available', disabled: true }], - }, - ]; - }, [selectedAbility, sortedVersions]); - - const handleAbilityViewNavigation = (viewId: string) => { - onAbilityViewSelection(viewId); - if (viewId === 'ability-versions' && !expandedMenus.has('ability-versions')) { - onToggleMenu('ability-versions'); - } - }; - - const handleAbilitySubmenuNavigation = (submenuId: string) => { - if (submenuId.startsWith('version-')) { - const versionNumber = submenuId.replace('version-', ''); - onAbilityViewSelection(`version-${versionNumber}`); - } else { - onAbilityViewSelection(submenuId); - } - }; - - if (!abilities || abilities.length === 0) { - return
No abilities found
; - } - - return ( -
- {abilities.map((ability: Ability) => ( -
- - - {selectedAbility?.packageName === ability.packageName && ( -
- {/* Show menu items - always show basic items, handle versions separately */} - {abilityMenuItems.map((abilityItem) => { - const AbilityIcon = abilityItem.icon; - - if (abilityItem.submenu) { - const isAbilitySubmenuExpanded = expandedMenus.has(abilityItem.id); - const isVersionsMenu = abilityItem.id === 'ability-versions'; - const hasVersionsError = isVersionsMenu && !!versionsError; - const isVersionsLoading = isVersionsMenu && versionsLoading; - - return ( -
- - {isAbilitySubmenuExpanded && ( -
- {/* Show loading state for versions */} - {isVersionsLoading && ( -
- Loading versions... -
- )} - - {/* Show error state for versions */} - {hasVersionsError && ( -
- Error loading versions -
- )} - - {/* Show version items when loaded or fallback */} - {abilityItem.submenu.map((subMenuItem: any) => { - // Don't show version items if loading/error for ability-versions - if (isVersionsMenu && (isVersionsLoading || hasVersionsError)) { - return <>; - } - - return ( - - ); - })} -
- )} -
- ); - } - - return ( - - ); - })} -
- )} -
- ))} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AppList.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AppList.tsx deleted file mode 100644 index 0ea4703d2..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/AppList.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { FileText, GitBranch } from 'lucide-react'; -import { useMemo } from 'react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { App, AppVersion } from '@/types/developer-dashboard/appTypes'; - -interface AppListProps { - apps: App[]; - selectedApp: App | null; - selectedAppView: string | null; - expandedMenus: Set; - onAppSelection: (app: App) => void; - onAppViewSelection: (viewId: string) => void; - onToggleMenu: (menuId: string) => void; -} - -export function AppList({ - apps, - selectedApp, - selectedAppView, - expandedMenus, - onAppSelection, - onAppViewSelection, - onToggleMenu, -}: AppListProps) { - const { - data: appVersions, - isLoading: versionsLoading, - error: versionsError, - } = vincentApiClient.useGetAppVersionsQuery( - { appId: selectedApp?.appId || 0 }, - { skip: !selectedApp?.appId }, - ); // FIXME: Sidebar-related patch, we don't want to fetch versions if no app is selected - - const sortedVersions = useMemo(() => { - if (!appVersions || appVersions.length === 0) return []; - // Filter out deleted versions and sort by version number (descending) - return appVersions - .filter((version: AppVersion) => !version.isDeleted) - .sort((a: AppVersion, b: AppVersion) => b.version - a.version); - }, [appVersions]); - - const appMenuItems = useMemo(() => { - if (!selectedApp) return []; - - const baseMenuItems = [ - { id: 'app-details', label: 'App Details', icon: FileText }, - { - id: 'app-versions', - label: 'App Versions', - icon: GitBranch, - submenu: - sortedVersions.length > 0 - ? sortedVersions.map((version) => ({ - id: `version-${version.version}`, - label: `Version ${version.version}${version.version === selectedApp.activeVersion ? ' (Active)' : ''}`, - })) - : [{ id: 'no-versions', label: 'No versions available', disabled: true }], - }, - ]; - - return baseMenuItems; - }, [selectedApp, sortedVersions]); - - const handleAppViewNavigation = (viewId: string) => { - onAppViewSelection(viewId); - if (viewId === 'app-versions' && !expandedMenus.has('app-versions')) { - onToggleMenu('app-versions'); - } - }; - - const handleAppSubmenuNavigation = (submenuId: string) => { - if (submenuId.startsWith('version-')) { - // Handle version navigation (e.g., "version-1") - const versionNumber = submenuId.replace('version-', ''); - onAppViewSelection(`version-${versionNumber}`); - } else { - onAppViewSelection(submenuId); - } - }; - - if (!apps || apps.length === 0) { - return
No apps found
; - } - - return ( -
- {apps.map((app) => { - const isDeleted = app.isDeleted; - - return ( -
- - - {selectedApp?.appId === app.appId && ( -
- {/* Show menu items - always show basic items, handle versions separately */} - {appMenuItems.map((appItem) => { - const AppIcon = appItem.icon; - - if (appItem.submenu) { - const isAppSubmenuExpanded = expandedMenus.has(appItem.id); - const isVersionsMenu = appItem.id === 'app-versions'; - const hasVersionsError = isVersionsMenu && !!versionsError; - const isVersionsLoading = isVersionsMenu && versionsLoading; - - return ( -
- - - {isAppSubmenuExpanded && ( -
- {appItem.submenu.map((subMenuItem: any) => { - // Don't show version items if loading/error for app-versions - if (isVersionsMenu && (isVersionsLoading || hasVersionsError)) { - return <>; - } - - return ( - - ); - })} -
- )} -
- ); - } - - return ( - - ); - })} -
- )} -
- ); - })} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/DeveloperSidebarWrapper.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/DeveloperSidebarWrapper.tsx deleted file mode 100644 index a6c61aa84..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/DeveloperSidebarWrapper.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Sidebar } from './Sidebar'; - -export function DeveloperSidebarWrapper() { - return ; -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/PolicyList.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/PolicyList.tsx deleted file mode 100644 index c4c013e76..000000000 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/PolicyList.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import { FileText, GitBranch } from 'lucide-react'; -import { useMemo } from 'react'; -import { reactClient as vincentApiClient } from '@lit-protocol/vincent-registry-sdk'; -import { Policy, PolicyVersion } from '@/types/developer-dashboard/appTypes'; - -interface PolicyListProps { - policies: Policy[]; - selectedPolicy: Policy | null; - selectedPolicyView: string | null; - expandedMenus: Set; - onPolicySelection: (policy: Policy) => void; - onPolicyViewSelection: (viewId: string) => void; - onToggleMenu: (menuId: string) => void; -} - -export function PolicyList({ - policies, - selectedPolicy, - selectedPolicyView, - expandedMenus, - onPolicySelection, - onPolicyViewSelection, - onToggleMenu, -}: PolicyListProps) { - const { - data: policyVersions, - isLoading: versionsLoading, - error: versionsError, - } = vincentApiClient.useGetPolicyVersionsQuery( - { - packageName: selectedPolicy?.packageName || '', - }, - { skip: !selectedPolicy?.packageName }, - ); // FIXME: Sidebar-related patch, we don't want to fetch versions if no policy is selected - - const sortedVersions = useMemo(() => { - if (!policyVersions || policyVersions.length === 0) return []; - // Filter out deleted versions from sidebar dropdown - const activeVersions = policyVersions.filter((version: PolicyVersion) => !version.isDeleted); - return [...activeVersions].sort((a: PolicyVersion, b: PolicyVersion) => - b.version.localeCompare(a.version, undefined, { numeric: true }), - ); - }, [policyVersions]); - - const policyMenuItems = useMemo(() => { - if (!selectedPolicy) return []; - - return [ - { id: 'policy-details', label: 'Policy Details', icon: FileText }, - { - id: 'policy-versions', - label: 'Policy Versions', - icon: GitBranch, - submenu: - sortedVersions.length > 0 - ? sortedVersions.map((version: PolicyVersion) => ({ - id: `version-${version.version}`, - label: `Version ${version.version}${version.version === selectedPolicy.activeVersion ? ' (Active)' : ''}`, - })) - : [{ id: 'no-versions', label: 'No versions available', disabled: true }], - }, - ]; - }, [selectedPolicy, sortedVersions]); - - const handlePolicyViewNavigation = (viewId: string) => { - onPolicyViewSelection(viewId); - if (viewId === 'policy-versions' && !expandedMenus.has('policy-versions')) { - onToggleMenu('policy-versions'); - } - }; - - const handlePolicySubmenuNavigation = (submenuId: string) => { - if (submenuId.startsWith('version-')) { - const versionNumber = submenuId.replace('version-', ''); - onPolicyViewSelection(`version-${versionNumber}`); - } else { - onPolicyViewSelection(submenuId); - } - }; - - if (!policies || policies.length === 0) { - return
No policies found
; - } - - return ( -
- {policies.map((policy: Policy) => ( -
- - - {selectedPolicy?.packageName === policy.packageName && ( -
- {/* Show menu items - always show basic items, handle versions separately */} - {policyMenuItems.map((policyItem) => { - const PolicyIcon = policyItem.icon; - - if (policyItem.submenu) { - const isPolicySubmenuExpanded = expandedMenus.has(policyItem.id); - const isVersionsMenu = policyItem.id === 'policy-versions'; - const hasVersionsError = isVersionsMenu && !!versionsError; - const isVersionsLoading = isVersionsMenu && versionsLoading; - - return ( -
- - {isPolicySubmenuExpanded && ( -
- {/* Show loading state for versions */} - {isVersionsLoading && ( -
- Loading versions... -
- )} - - {/* Show error state for versions */} - {hasVersionsError && ( -
- Error loading versions -
- )} - - {/* Show version items when loaded or fallback */} - {policyItem.submenu.map((subMenuItem: any) => { - // Don't show version items if loading/error for policy-versions - if (isVersionsMenu && (isVersionsLoading || hasVersionsError)) { - return <>; - } - - return ( - - ); - })} -
- )} -
- ); - } - - return ( - - ); - })} -
- )} -
- ))} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/Sidebar.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/Sidebar.tsx index 2cace4fdc..a693b7084 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/Sidebar.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/sidebar/Sidebar.tsx @@ -1,9 +1,9 @@ import React from 'react'; import { useLocation, Link } from 'react-router-dom'; import { Home, Package, Wrench, Shield, LogOut, HelpCircle } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { useTheme } from '@/hooks/useTheme'; -import { useClearAuthInfo } from '@/hooks/user-dashboard/useAuthInfo'; +import { useAuth } from '@/hooks/developer-dashboard/useAuth'; import { AccountTooltip } from '@/components/shared/AccountTooltip'; import { Sidebar as SidebarComponent, @@ -27,15 +27,15 @@ interface MenuItem { export function Sidebar() { const location = useLocation(); - const { clearAuthInfo } = useClearAuthInfo(); + const { signOut } = useAuth(); const isDark = useTheme(); const isActiveRoute = (route: string) => { return location.pathname.startsWith(route); }; - const handleSignOut = async () => { - await clearAuthInfo(); + const handleSignOut = () => { + signOut(); }; const menuItems: MenuItem[] = [ diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilityCard.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilityCard.tsx index 05ae50c63..013cb1257 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilityCard.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilityCard.tsx @@ -1,5 +1,5 @@ import { ReactNode } from 'react'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; +import { theme } from '@/lib/themeClasses'; interface AbilityCardProps { onClick?: () => void; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/AbilitySelectorModal.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilitySelectorModal.tsx similarity index 98% rename from packages/apps/app-dashboard/src/components/developer-dashboard/AbilitySelectorModal.tsx rename to packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilitySelectorModal.tsx index 271969878..b652e784d 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/AbilitySelectorModal.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/AbilitySelectorModal.tsx @@ -19,7 +19,7 @@ import { DialogTitle, } from '@/components/shared/ui/dialog'; import { Button } from '@/components/shared/ui/button'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { AbilityVersionSelectorModal } from './AbilityVersionSelectorModal'; // Register AG Grid modules @@ -309,6 +309,8 @@ export function AbilitySelectorModal({ '--ag-header-foreground-color': theme.text, '--ag-foreground-color': theme.text, '--ag-data-color': theme.text, + '--ag-font-family': fonts.body.fontFamily, + '--ag-font-size': '14px', } as React.CSSProperties } > @@ -389,12 +391,12 @@ export function AbilitySelectorModal({
- - - )} -
- ) : ( -
- No redirect URIs configured. Please add redirect URIs to your app settings first. -
- )} -
-
- -
{/* Empty footer for spacing */}
- - - ); -} diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardCard.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardCard.tsx index e743a4110..563305ddc 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardCard.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardCard.tsx @@ -1,5 +1,5 @@ import { ReactNode } from 'react'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; +import { theme } from '@/lib/themeClasses'; interface DashboardCardProps { onClick?: () => void; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/DashboardContent.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardContent.tsx similarity index 99% rename from packages/apps/app-dashboard/src/components/developer-dashboard/DashboardContent.tsx rename to packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardContent.tsx index dc55f7dd3..8edfd7698 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/DashboardContent.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/DashboardContent.tsx @@ -4,7 +4,7 @@ import { motion } from 'framer-motion'; import { MenuId } from '@/types/developer-dashboard/menuId'; import { DashboardCard } from '@/components/developer-dashboard/ui/DashboardCard'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface DashboardContentProps { filteredAppsCount: number; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/PolicyCard.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/PolicyCard.tsx index 4a14b49ed..163ce1c92 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/PolicyCard.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/PolicyCard.tsx @@ -1,5 +1,5 @@ import { ReactNode } from 'react'; -import { theme } from '@/components/user-dashboard/connect/ui/theme'; +import { theme } from '@/lib/themeClasses'; interface PolicyCardProps { onClick?: () => void; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/ResourceNotOwnedError.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/ResourceNotOwnedError.tsx index 9dc59abb0..744a0448b 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/ResourceNotOwnedError.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/ResourceNotOwnedError.tsx @@ -1,6 +1,6 @@ import { ShieldAlert, Home, MessageCircle } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; type ResourceNotOwnedErrorProps = { resourceType: 'app' | 'ability' | 'policy'; diff --git a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/VersionCard.tsx b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/VersionCard.tsx index 48dc0aa65..ed1140899 100644 --- a/packages/apps/app-dashboard/src/components/developer-dashboard/ui/VersionCard.tsx +++ b/packages/apps/app-dashboard/src/components/developer-dashboard/ui/VersionCard.tsx @@ -1,4 +1,4 @@ -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface VersionCardProps { version: number | string; diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AbilityInfoView.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AbilityInfoView.tsx index 900024d81..6aeb7546f 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AbilityInfoView.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/AbilityInfoView.tsx @@ -3,7 +3,7 @@ import { AppVersionAbility, Ability } from '@/types/developer-dashboard/appTypes import { AbilityVersionPoliciesWrapper } from '../wrappers/ui/AbilityVersionPolicyWrapper'; import { Logo } from '@/components/shared/ui/Logo'; import { useState } from 'react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface AbilityInfoViewProps { appVersionAbility: AppVersionAbility; diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/ActiveAppVersion.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/ActiveAppVersion.tsx deleted file mode 100644 index 812e4cf59..000000000 --- a/packages/apps/app-dashboard/src/components/explorer/ui/ActiveAppVersion.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { TabsContent } from '@/components/shared/ui/tabs'; -import { Database, Layers } from 'lucide-react'; -import { App, AppVersion, AppVersionAbility } from '@/types/developer-dashboard/appTypes'; -import { AbilityInfoWrapper } from '../wrappers/ui/AbilityInfoWrapper'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -export function ActiveAppVersion({ - versions, - versionAbilities, - app, -}: { - versions: AppVersion[]; - versionAbilities: AppVersionAbility[]; - app: App; -}) { - const activeVersion = versions.find((v) => v.version === app.activeVersion); - - const activeVersionAbilities = versionAbilities.filter( - (ability) => ability.appVersion === app.activeVersion, - ); - - return ( - - {/* Active Version View */} - {activeVersion ? ( -
- {/* Version Abilities*/} -
-
- - - Integrated Abilities ({activeVersionAbilities.length}) - -
- - {activeVersionAbilities.length > 0 ? ( -
- {activeVersionAbilities.map((ability) => { - return ( - - ); - })} -
- ) : ( -
- -

- No abilities configured for this version -

-
- )} -
-
- ) : ( -
-

- No active version found -

-
- )} -
- ); -} diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AllAppVersions.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AllAppVersions.tsx deleted file mode 100644 index b5d795849..000000000 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AllAppVersions.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { TabsContent } from '@/components/shared/ui/tabs'; -import { App, AppVersion } from '@/types/developer-dashboard/appTypes'; -import { FileText } from 'lucide-react'; -import { Tag } from 'lucide-react'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; - -interface AllAppVersionsProps { - versions: AppVersion[]; - app: App; -} - -export function AllAppVersions({ versions, app }: AllAppVersionsProps) { - // Sort versions to show active version first, then others - const sortedVersions = [...versions].sort((a, b) => { - if (a.version === app.activeVersion) return -1; - if (b.version === app.activeVersion) return 1; - return 0; - }); - - return ( - - {/* All Versions View */} -
- {sortedVersions.map((version) => ( -
-
-
-
- - - v{version.version} - -
- {version.version === app.activeVersion && ( - - ACTIVE - - )} -
-
- - {version.changes && ( -
-
- - - Changes - -
-

- {version.changes} -

-
- )} -
- ))} -
-
- ); -} diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AppCard.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AppCard.tsx index 47b76fbf4..bd81a901d 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AppCard.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/AppCard.tsx @@ -49,13 +49,13 @@ export const AppCard = ({ app }: AppCardProps) => {
- {app.appUserUrl && ( + {app.appUrl && (
- - {/* Action Button */} - {app.redirectUris && app.redirectUris.length > 0 && ( - - )} diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AppHero.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AppHero.tsx index 80d79d6d0..1297179a3 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AppHero.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/AppHero.tsx @@ -1,5 +1,5 @@ import { App } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; export function AppHero({ apps }: { apps: App[] }) { return ( diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AppInfo.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AppInfo.tsx index 8567df3d7..6c382f06c 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AppInfo.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/AppInfo.tsx @@ -1,7 +1,7 @@ import { Mail, Copy, CheckCircle, ChevronDown, ChevronUp, Hash, Zap, Globe } from 'lucide-react'; import { useState } from 'react'; import { App } from '@/types/developer-dashboard/appTypes'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; export function AppInfo({ app }: { app: App }) { const [copiedField, setCopiedField] = useState(null); @@ -59,7 +59,7 @@ export function AppInfo({ app }: { app: App }) { )} - {app.appUserUrl && ( + {app.appUrl && (
@@ -73,20 +73,20 @@ export function AppInfo({ app }: { app: App }) { App URL

- {app.appUserUrl} + {app.appUrl}
)} - {!app.contactEmail && !app.appUserUrl && ( + {!app.contactEmail && !app.appUrl && (
diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/AppsDisplay.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/AppsDisplay.tsx index f1aff2c14..777c3fd2a 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/AppsDisplay.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/AppsDisplay.tsx @@ -2,7 +2,7 @@ import { Search } from 'lucide-react'; import { App } from '@/types/developer-dashboard/appTypes'; import { useNavigate } from 'react-router-dom'; import { Logo } from '@/components/shared/ui/Logo'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; import { useCallback } from 'react'; interface AppsDisplayProps { diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerErrorPage.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerErrorPage.tsx index a05ad4000..5bafb311e 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerErrorPage.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerErrorPage.tsx @@ -1,7 +1,7 @@ import { AlertTriangle } from 'lucide-react'; import { ExplorerNav } from './ExplorerNav'; import { useNavigate } from 'react-router-dom'; -import { fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { fonts } from '@/lib/themeClasses'; interface ExplorerErrorPageProps { title: string; diff --git a/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerNav.tsx b/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerNav.tsx index 63e3b0b2e..58d8a887a 100644 --- a/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerNav.tsx +++ b/packages/apps/app-dashboard/src/components/explorer/ui/ExplorerNav.tsx @@ -3,7 +3,7 @@ import { toggleTheme } from '@/lib/theme'; import { useCallback, useState } from 'react'; import { Menu, X } from 'lucide-react'; import { useLocation } from 'react-router-dom'; -import { theme, fonts } from '@/components/user-dashboard/connect/ui/theme'; +import { theme, fonts } from '@/lib/themeClasses'; interface ExplorerNavProps { onNavigate?: (path: string) => void; @@ -31,7 +31,6 @@ export function ExplorerNav({ onNavigate, sidebarTrigger }: ExplorerNavProps) { const isExplorer = location.pathname.startsWith('/explorer'); const isDeveloper = location.pathname.startsWith('/developer'); - const isUser = location.pathname.startsWith('/user'); return (