diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml index 0c78598d065..8d08b73452e 100644 --- a/.github/workflows/auto-assign.yml +++ b/.github/workflows/auto-assign.yml @@ -2,7 +2,7 @@ name: Auto Author Assign on: pull_request: - types: [ opened, reopened ] + types: [opened, reopened] permissions: pull-requests: write @@ -10,5 +10,9 @@ permissions: jobs: assign-author: runs-on: ubuntu-latest + if: | + github.event.pull_request.author_association == 'MEMBER' || + github.event.pull_request.author_association == 'OWNER' || + github.event.pull_request.author_association == 'COLLABORATOR' steps: - uses: toshimaru/auto-author-assign@v2.1.1 diff --git a/.github/workflows/issue.yml b/.github/workflows/issue.yml index 661d5e2ab4d..3e8dd3623fa 100644 --- a/.github/workflows/issue.yml +++ b/.github/workflows/issue.yml @@ -2,7 +2,7 @@ name: Linked Issue on: pull_request: - types: [opened, edited] + types: [opened, edited, ready_for_review] env: VALID_ISSUE_PREFIXES: "CNCT|DASH|PROT|INSIGHT|ENGINE|CS|DES|BIL|DEVX|SOLU|NEB" @@ -22,6 +22,17 @@ jobs: pull_number: context.issue.number }); + // Check if contributor is external + const isInternalContributor = ['MEMBER', 'OWNER', 'COLLABORATOR'].includes( + context.payload.pull_request.author_association + ); + + // Automatically pass for external contributors + if (!isInternalContributor) { + console.log('External contributor detected - automatically passing check'); + return; + } + const body = pr.data.body || ''; const branchName = pr.data.head.ref; const issueRegex = new RegExp(`(${process.env.VALID_ISSUE_PREFIXES})-\\d+`, 'i'); diff --git a/.github/workflows/typedoc.yml b/.github/workflows/typedoc.yml new file mode 100644 index 00000000000..9ef79f524fa --- /dev/null +++ b/.github/workflows/typedoc.yml @@ -0,0 +1,50 @@ +name: TypeDoc + +on: + push: + branches: + - main + +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment +concurrency: + group: "typedoc" + cancel-in-progress: true + +jobs: + build: + name: "Generate TypeDoc" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install + uses: ./.github/composite-actions/install + + - name: Run TypeDoc + run: pnpm typedoc + + - name: Update Gist + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GIST_TOKEN }} + script: | + const fs = require('fs'); + const content = fs.readFileSync('./packages/thirdweb/typedoc/parsed.json', 'utf8'); + const gistId = '678fe1f331a01270bb002fee660f285d'; + + await github.rest.gists.update({ + gist_id: gistId, + files: { + 'data.json': { + content: content + } + } + }); + + console.log(`Permalink: https://gist.githubusercontent.com/raw/${gistId}/data.json`); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx index af4c121a0ee..b05e7f9932c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-button.tsx @@ -8,8 +8,12 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; +import { TabButtons } from "@/components/ui/tabs"; import { ListerOnly } from "@3rdweb-sdk/react/components/roles/lister-only"; import type { Account } from "@3rdweb-sdk/react/hooks/useApi"; +import { isAlchemySupported } from "lib/wallet/nfts/alchemy"; +import { isMoralisSupported } from "lib/wallet/nfts/moralis"; +import { isSimpleHashSupported } from "lib/wallet/nfts/simpleHash"; import { PlusIcon } from "lucide-react"; import { useState } from "react"; import type { ThirdwebContract } from "thirdweb"; @@ -23,6 +27,8 @@ interface CreateListingButtonProps { twAccount: Account | undefined; } +const LISTING_MODES = ["Select NFT", "Manual"] as const; + export const CreateListingButton: React.FC = ({ createText = "Create", type, @@ -32,7 +38,13 @@ export const CreateListingButton: React.FC = ({ }) => { const address = useActiveAccount()?.address; const [open, setOpen] = useState(false); - + const [listingMode, setListingMode] = + useState<(typeof LISTING_MODES)[number]>("Select NFT"); + const isSupportedChain = + contract.chain.id && + (isSimpleHashSupported(contract.chain.id) || + isAlchemySupported(contract.chain.id) || + isMoralisSupported(contract.chain.id)); return ( @@ -43,15 +55,47 @@ export const CreateListingButton: React.FC = ({ - {createText} + {createText} - + {/* + If the chain is not supported by the indexer providers + we don't show the tabs, we only show the Manual input form. + Otherwise we show both */} + {isSupportedChain ? ( + <> + ({ + name: mode, + isActive: mode === listingMode, + onClick: () => setListingMode(mode), + isEnabled: true, + }))} + tabClassName="text-sm gap-2 !text-sm" + tabContainerClassName="gap-0.5" + /> +
+ +
+ + ) : ( +
+ +
+ )}
diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx index c82438cc496..c330fdc0bcf 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/(marketplace)/components/list-form.tsx @@ -37,10 +37,14 @@ import { import { decimals } from "thirdweb/extensions/erc20"; import { isApprovedForAll as isApprovedForAll721, + isERC721, + ownerOf, setApprovalForAll as setApprovalForAll721, } from "thirdweb/extensions/erc721"; import { + balanceOf, isApprovedForAll as isApprovedForAll1155, + isERC1155, setApprovalForAll as setApprovalForAll1155, } from "thirdweb/extensions/erc1155"; import { createAuction, createListing } from "thirdweb/extensions/marketplace"; @@ -49,6 +53,7 @@ import type { CreateListingParams, } from "thirdweb/extensions/marketplace"; import { useActiveAccount, useSendAndConfirmTransaction } from "thirdweb/react"; +import { shortenAddress } from "thirdweb/utils"; import { FormErrorMessage, FormHelperText, FormLabel } from "tw-components"; import { NFTMediaWithEmptyState } from "tw-components/nft-media"; import { shortenIfAddress } from "utils/usedapp-external"; @@ -78,6 +83,7 @@ type CreateListingsFormProps = { contract: ThirdwebContract; actionText: string; setOpen: Dispatch>; + mode: "automatic" | "manual"; type?: "direct-listings" | "english-auctions"; twAccount: Account | undefined; }; @@ -98,6 +104,7 @@ export const CreateListingsForm: React.FC = ({ actionText, setOpen, twAccount, + mode, }) => { const trackEvent = useTrack(); const chainId = contract.chain.id; @@ -162,7 +169,8 @@ export const CreateListingsForm: React.FC = ({ !selectedContract || isSupportedChain || isWalletNFTsLoading || - (walletNFTs?.result || []).length > 0, + (walletNFTs?.result || []).length > 0 || + mode === "manual", }); const isSelected = (nft: WalletNFT) => { @@ -208,34 +216,112 @@ export const CreateListingsForm: React.FC = ({ className="flex flex-col gap-6 pb-16" id={LIST_FORM_ID} onSubmit={form.handleSubmit(async (formData) => { - if (!formData.selected || !selectedContract) { - return; - } - if (!account) { return toast.error("No account detected"); } - setIsFormLoading(true); - + let nftType: "ERC1155" | "ERC721"; + let _selectedContract: ThirdwebContract; + let selectedTokenId: bigint; + const selectedQuantity = BigInt(formData.quantity); try { + if (mode === "manual") { + if (!formData.assetContractAddress) { + setIsFormLoading(false); + return toast.error("Enter a valid NFT contract address"); + } + _selectedContract = getContract({ + address: formData.assetContractAddress, + chain: contract.chain, + client: contract.client, + }); + /** + * In manual mode we need to detect the NFT type ourselves + * instead of relying on the third-party providers + */ + const [is721, is1155] = await Promise.all([ + isERC721({ contract: _selectedContract }), + isERC1155({ contract: _selectedContract }), + ]); + if (!is721 && !is1155) { + setIsFormLoading(false); + return toast.error( + `Error: ${formData.assetContractAddress} is neither an ERC721 or ERC1155 contract`, + ); + } + selectedTokenId = BigInt(formData.tokenId); + nftType = is721 ? "ERC721" : "ERC1155"; + /** + * Also in manual mode we need to make sure the user owns the tokenId they entered + * For ERC1155, the owned balance must be >= the entered quantity + * For ERC721, the owner address must match theirs + */ + if (nftType === "ERC1155") { + const balance = await balanceOf({ + contract: _selectedContract, + tokenId: selectedTokenId, + owner: account.address, + }); + if (balance === 0n) { + setIsFormLoading(false); + return toast.error( + `You do not own any tokenId #${selectedTokenId.toString()} from the collection: ${shortenAddress(formData.assetContractAddress)}`, + ); + } + if (balance < selectedQuantity) { + setIsFormLoading(false); + return toast.error( + `The balance you own for tokenId #${selectedTokenId.toString()} is less than the quantity (you own ${balance.toString()})`, + ); + } + } else { + if (selectedQuantity !== 1n) { + setIsFormLoading(false); + return toast.error( + "The quantity can only be 1 for ERC721 token", + ); + } + const owner = await ownerOf({ + contract: _selectedContract, + tokenId: selectedTokenId, + }).catch(() => undefined); + if (owner?.toLowerCase() !== account.address.toLowerCase()) { + setIsFormLoading(false); + return toast.error( + `You do not own the tokenId #${selectedTokenId.toString()} from the collection: ${shortenAddress(formData.assetContractAddress)}`, + ); + } + } + } else { + if (!formData.selected || !selectedContract) { + setIsFormLoading(false); + return toast.error("Please select an NFT to list"); + } + nftType = formData.selected.type; + _selectedContract = selectedContract; + selectedTokenId = BigInt(formData.selected.id); + } + /** + * Make sure the selected item is approved to be listed on the marketplace contract + * todo: We are checking "isApprovedForAll" for both erc1155 and 721. + * However for ERC721 there's also a function called "getApproved" which is used to check for approval status of a single token + * - might worth adding that logic here. + */ const isNftApproved = - formData.selected.type === "ERC1155" - ? isApprovedForAll1155 - : isApprovedForAll721; + nftType === "ERC1155" ? isApprovedForAll1155 : isApprovedForAll721; const isApproved = await isNftApproved({ - contract: selectedContract, + contract: _selectedContract, operator: contract.address, owner: account.address, }); if (!isApproved) { const setNftApproval = - formData.selected.type === "ERC1155" + nftType === "ERC1155" ? setApprovalForAll1155 : setApprovalForAll721; const approveTx = setNftApproval({ - contract: selectedContract, + contract: _selectedContract, operator: contract.address, approved: true, }); @@ -256,10 +342,10 @@ export const CreateListingsForm: React.FC = ({ ); const transaction = createListing({ contract, - assetContractAddress: formData.selected.contractAddress, - tokenId: BigInt(formData.selected.id), + assetContractAddress: _selectedContract.address, + tokenId: selectedTokenId, currencyContractAddress: formData.currencyContractAddress, - quantity: BigInt(formData.quantity), + quantity: selectedQuantity, startTimestamp: formData.startTimestamp, pricePerToken: String(formData.pricePerToken), endTimestamp, @@ -305,18 +391,16 @@ export const CreateListingsForm: React.FC = ({ const transaction = createAuction({ contract, - assetContractAddress: formData.selected.contractAddress, - tokenId: BigInt(formData.selected.id), + assetContractAddress: _selectedContract.address, + tokenId: selectedTokenId, startTimestamp: formData.startTimestamp, currencyContractAddress: formData.currencyContractAddress, endTimestamp: new Date( new Date().getTime() + Number.parseInt(formData.listingDurationInSeconds) * 1000, ), - minimumBidAmountWei: - minimumBidAmountWei * BigInt(formData.quantity), - buyoutBidAmountWei: - buyoutBidAmountWei * BigInt(formData.quantity), + minimumBidAmountWei: minimumBidAmountWei * selectedQuantity, + buyoutBidAmountWei: buyoutBidAmountWei * selectedQuantity, }); const promise = sendAndConfirmTx.mutateAsync(transaction, { @@ -353,116 +437,138 @@ export const CreateListingsForm: React.FC = ({ setIsFormLoading(false); })} > - - Select NFT - - Select the NFT you want to list for sale - - {!isSupportedChain ? ( - -
- -

- This chain is not supported by our NFT API yet, please enter the - contract address of the NFT you want to list. -

-
- - Contract address - - - {form.formState.errors.selected?.contractAddress?.message} - - - This will display all the NFTs you own from this contract. - - -
- ) : null} - {isWalletNFTsLoading || - (isOwnedNFTsLoading && - !isSupportedChain && - form.watch("selected.contractAddress")) ? ( -
- -
- ) : nfts && nfts.length !== 0 ? ( - - {nfts?.map((nft) => { - return ( - -
    -
  • - Name: {nft.metadata?.name || "N/A"} -
  • -
  • - Contract Address:{" "} - {shortenIfAddress(nft.contractAddress)} -
  • -
  • - Token ID: {nft.id.toString()} -
  • -
  • - Token Standard: {nft.type} -
  • -
- - } + {mode === "manual" ? ( + <> + + + Manually enter the contract address and token ID of the NFT you + want to list for sale + + NFT Contract Address + + + + Token ID + + + + ) : ( + <> + + + Select the NFT you want to list for sale + + {!isSupportedChain ? ( + +
+ +

+ This chain is not supported by our NFT API yet, please enter + the contract address of the NFT you want to list. +

+
+ - - isSelected(nft) - ? form.setValue("selected", undefined) - : form.setValue("selected", nft) - } - outline={isSelected(nft) ? "3px solid" : undefined} - outlineColor={isSelected(nft) ? "purple.500" : undefined} - overflow="hidden" - > - - -
- ); - })} -
- ) : nfts && nfts.length === 0 ? ( -
- -

- There are no NFTs owned by this wallet. You need NFTs to create a - listing. You can create NFTs with thirdweb.{" "} - - Explore NFT contracts - - . -

-
- ) : null} -
- + Contract address + + + {form.formState.errors.selected?.contractAddress?.message} + + + This will display all the NFTs you own from this contract. + + + + ) : null} + {isWalletNFTsLoading || + (isOwnedNFTsLoading && + !isSupportedChain && + form.watch("selected.contractAddress")) ? ( +
+ +
+ ) : nfts && nfts.length !== 0 ? ( + + {nfts?.map((nft) => { + return ( + +
    +
  • + Name:{" "} + {nft.metadata?.name || "N/A"} +
  • +
  • + Contract Address:{" "} + {shortenIfAddress(nft.contractAddress)} +
  • +
  • + Token ID: {nft.id.toString()} +
  • +
  • + Token Standard: {nft.type} +
  • +
+ + } + > + + isSelected(nft) + ? form.setValue("selected", undefined) + : form.setValue("selected", nft) + } + outline={isSelected(nft) ? "3px solid" : undefined} + outlineColor={ + isSelected(nft) ? "purple.500" : undefined + } + overflow="hidden" + > + + +
+ ); + })} +
+ ) : nfts && nfts.length === 0 ? ( +
+ +

+ There are no NFTs owned by this wallet. You need NFTs to + create a listing. You can create NFTs with thirdweb.{" "} + + Explore NFT contracts + + . +

+
+ ) : null} + + + )} + + Listing Currency = ({ The currency you want to sell your tokens for. - + {form.watch("listingType") === "auction" ? "Buyout Price Per Token" @@ -489,7 +595,7 @@ export const CreateListingsForm: React.FC = ({ {form.watch("selected")?.type?.toLowerCase() !== "erc721" && ( - +
Quantity
@@ -501,7 +607,7 @@ export const CreateListingsForm: React.FC = ({ )} {form.watch("listingType") === "auction" && ( <> - + Reserve Price Per Token @@ -522,7 +628,7 @@ export const CreateListingsForm: React.FC = ({ )} - {!form.watch("selected.id") && ( + {mode === "automatic" && !form.watch("selected.id") && ( No NFT selected diff --git a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx index a5355bfe06d..082b1660468 100644 --- a/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx +++ b/apps/dashboard/src/app/nebula-app/(app)/components/Chats.tsx @@ -15,10 +15,13 @@ import { import { useState } from "react"; import { toast } from "sonner"; import type { ThirdwebClient } from "thirdweb"; +import { sendTransaction } from "thirdweb"; import { useActiveAccount } from "thirdweb/react"; import type { Account } from "thirdweb/wallets"; +import { getThirdwebClient } from "../../../../@/constants/thirdweb.server"; import { TransactionButton } from "../../../../components/buttons/TransactionButton"; import { MarkdownRenderer } from "../../../../components/contract-components/published-contract/markdown-renderer"; +import { useV5DashboardChain } from "../../../../lib/v5-adapter"; import { submitFeedback } from "../api/feedback"; import { NebulaIcon } from "../icons/NebulaIcon"; @@ -241,16 +244,28 @@ function SendTransactionButton(props: { twAccount: TWAccount; }) { const account = useActiveAccount(); + const chain = useV5DashboardChain(props.txData?.chainId); + const sendTxMutation = useMutation({ mutationFn: () => { if (!account) { throw new Error("No active account"); } - if (!props.txData) { + if (!props.txData || !chain) { throw new Error("Invalid transaction"); } - return account.sendTransaction(props.txData); + + return sendTransaction({ + account, + transaction: { + ...props.txData, + nonce: Number(props.txData.nonce), + to: props.txData.to || undefined, // Get rid of the potential null value + chain, + client: getThirdwebClient(), + }, + }); }, }); diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/AccountAbstractionPage.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/AccountAbstractionPage.tsx index fcb3fd01e5d..00328f55525 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/AccountAbstractionPage.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/AccountAbstractionPage.tsx @@ -2,7 +2,6 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { TrackedLinkTW } from "@/components/ui/tracked-link"; import { - type Account, type ApiKeyService, accountStatus, } from "@3rdweb-sdk/react/hooks/useApi"; @@ -25,7 +24,6 @@ export function AccountAbstractionPage(props: { apiKeyServices: ApiKeyService[]; billingStatus: "validPayment" | (string & {}) | null; tab?: string; - twAccount: Account; }) { const { apiKeyServices } = props; @@ -81,11 +79,9 @@ export function AccountAbstractionPage(props: {
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx index 0550876f974..86f3c8fe4ed 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/account-abstraction/page.tsx @@ -4,7 +4,6 @@ import { ChakraProviderSetup } from "@/components/ChakraProviderSetup"; import type { Metadata } from "next"; import { notFound, redirect } from "next/navigation"; import { getAbsoluteUrl } from "../../../../../../lib/vercel-utils"; -import { getValidAccount } from "../../../../../account/settings/getAccount"; import { getAPIKeyForProjectId } from "../../../../../api/lib/getAPIKeys"; import { AccountAbstractionPage } from "./AccountAbstractionPage"; @@ -14,10 +13,7 @@ export default async function Page(props: { }) { const { team_slug, project_slug } = await props.params; - const [account, team, project] = await Promise.all([ - getValidAccount( - `/${team_slug}/${project_slug}/connect/account-abstraction`, - ), + const [team, project] = await Promise.all([ getTeamBySlug(team_slug), getProject(team_slug, project_slug), ]); @@ -45,7 +41,6 @@ export default async function Page(props: { projectKey={project.publishableKey} apiKeyServices={apiKey.services || []} tab={(await props.searchParams).tab} - twAccount={account} /> ); diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/loading.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/loading.tsx deleted file mode 100644 index 6c54ef15def..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/loading.tsx +++ /dev/null @@ -1,3 +0,0 @@ -"use client"; - -export { GenericLoadingPage as default } from "@/components/blocks/skeletons/GenericLoadingPage"; diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx deleted file mode 100644 index 1eaf77e9e90..00000000000 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/config/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { getProject } from "@/api/projects"; -import { getAPIKeyForProjectId } from "app/api/lib/getAPIKeys"; -import { notFound, redirect } from "next/navigation"; -import { InAppWalletSettingsPage } from "../../../../../../../components/embedded-wallets/Configure"; -import { getValidAccount } from "../../../../../../account/settings/getAccount"; -import { TRACKING_CATEGORY } from "../_constants"; - -export default async function Page(props: { - params: Promise<{ team_slug: string; project_slug: string }>; -}) { - const params = await props.params; - const [account, project] = await Promise.all([ - getValidAccount( - `/${params.team_slug}/${params.project_slug}/connect/in-app-wallets/config`, - ), - getProject(params.team_slug, params.project_slug), - ]); - - if (!project) { - redirect("/team"); - } - - const apiKey = await getAPIKeyForProjectId(project.id); - - if (!apiKey) { - notFound(); - } - - return ( - <> - - - ); -} diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx index 3dc051d0fcb..f3b8564326d 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/layout.tsx @@ -45,11 +45,6 @@ export default async function Layout(props: { path: `/team/${team_slug}/${project_slug}/connect/in-app-wallets/users`, exactMatch: true, }, - { - name: "Configuration", - path: `/team/${team_slug}/${project_slug}/connect/in-app-wallets/config`, - exactMatch: true, - }, ]} /> diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx index 55211eb2fb0..86c8a961ee1 100644 --- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx +++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/settings/ProjectGeneralSettingsPage.tsx @@ -35,9 +35,9 @@ import { type FieldArrayWithId, useFieldArray } from "react-hook-form"; import { toast } from "sonner"; import { joinWithComma, toArrFromList } from "utils/string"; import { - type ApiKeyValidationSchema, HIDDEN_SERVICES, - apiKeyValidationSchema, + type ProjectSettingsPageFormSchema, + projectSettingsPageFormSchema, } from "../../../../../components/settings/ApiKeys/validations"; type EditProjectUIPaths = { @@ -86,7 +86,7 @@ interface EditApiKeyProps { showNebulaSettings: boolean; } -type UpdateAPIForm = UseFormReturn; +type UpdateAPIForm = UseFormReturn; export const ProjectGeneralSettingsPageUI: React.FC = ( props, @@ -94,8 +94,8 @@ export const ProjectGeneralSettingsPageUI: React.FC = ( const { apiKey, updateMutation, deleteMutation } = props; const trackEvent = useTrack(); const router = useDashboardRouter(); - const form = useForm({ - resolver: zodResolver(apiKeyValidationSchema), + const form = useForm({ + resolver: zodResolver(projectSettingsPageFormSchema), defaultValues: { name: apiKey.name, domains: joinWithComma(apiKey.domains), @@ -484,7 +484,7 @@ function EnabledServicesSetting(props: { }); const handleAction = ( srvIdx: number, - srv: FieldArrayWithId, + srv: FieldArrayWithId, actionName: string, checked: boolean, ) => { diff --git a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx index 4fa6c060ccf..5f00301e239 100644 --- a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx +++ b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx @@ -422,6 +422,10 @@ function AuthEndpointFields(props: { name: "customAuthEndpoint.customHeaders", }); + const expandCustomAuthEndpointField = + form.watch("customAuthEndpoint")?.authEndpoint !== undefined && + canEditAdvancedFeatures; + return (
{ form.setValue( @@ -464,7 +466,7 @@ function AuthEndpointFields(props: { Allowed redirect URIs -