Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion apps/dashboard/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,7 @@ NEXT_PUBLIC_TURNSTILE_SITE_KEY=""
TURNSTILE_SECRET_KEY=""
REDIS_URL=""

ANALYTICS_SERVICE_URL=""
ANALYTICS_SERVICE_URL=""

# Required for Nebula Chat
NEXT_PUBLIC_NEBULA_URL=""
1 change: 1 addition & 0 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"color": "^4.2.3",
"compare-versions": "^6.1.0",
"date-fns": "4.1.0",
"fetch-event-stream": "0.1.5",
"flat": "^6.0.1",
"framer-motion": "11.13.3",
"fuse.js": "7.0.0",
Expand Down
12 changes: 12 additions & 0 deletions apps/dashboard/src/@/api/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import "server-only";
import { API_SERVER_URL } from "@/constants/env";
import { getAuthToken } from "../../app/api/lib/getAuthToken";

type EnabledTeamScope =
| "pay"
| "storage"
| "rpc"
| "bundler"
| "insight"
| "embeddedWallets"
| "relayer"
| "chainsaw"
| "nebula";

export type Team = {
id: string;
name: string;
Expand All @@ -15,6 +26,7 @@ export type Team = {
billingStatus: "validPayment" | (string & {}) | null;
billingEmail: string | null;
growthTrialEligible: boolean | null;
enabledScopes: EnabledTeamScope[];
};

export async function getTeamBySlug(slug: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,36 +28,34 @@ export const Mobile: Story = {

function Story() {
return (
<div className="min-h-screen bg-background py-4 text-foreground">
<div className="container flex max-w-[1000px] flex-col gap-8 lg:p-10">
<BadgeContainer label="Base">
<DangerSettingCard
title="This is a title"
description="This is a description"
buttonLabel="Some Action"
buttonOnClick={() => {}}
isPending={false}
confirmationDialog={{
title: "This is confirmation title",
description: "This is confirmation description",
}}
/>
</BadgeContainer>
<div className="container flex max-w-[1000px] flex-col gap-8 lg:p-10">
<BadgeContainer label="Base">
<DangerSettingCard
title="This is a title"
description="This is a description"
buttonLabel="Some Action"
buttonOnClick={() => {}}
isPending={false}
confirmationDialog={{
title: "This is confirmation title",
description: "This is confirmation description",
}}
/>
</BadgeContainer>

<BadgeContainer label="Loading">
<DangerSettingCard
title="This is a title"
description="This is a description"
buttonLabel="Some Action"
buttonOnClick={() => {}}
isPending={true}
confirmationDialog={{
title: "This is confirmation title",
description: "This is confirmation description",
}}
/>
</BadgeContainer>
</div>
<BadgeContainer label="Loading">
<DangerSettingCard
title="This is a title"
description="This is a description"
buttonLabel="Some Action"
buttonOnClick={() => {}}
isPending={true}
confirmationDialog={{
title: "This is confirmation title",
description: "This is confirmation description",
}}
/>
</BadgeContainer>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,10 @@ export function MultiNetworkSelector(props: {
onSelectedValuesChange={(chainIds) => {
props.onChange(chainIds.map(Number));
}}
placeholder="Select Chains"
placeholder={
allChains.length === 0 ? "Loading Chains..." : "Select Chains"
}
disabled={allChains.length === 0}
overrideSearchFn={searchFn}
renderOption={renderOption}
/>
Expand Down
13 changes: 6 additions & 7 deletions apps/dashboard/src/@/components/blocks/multi-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
maxCount = Number.POSITIVE_INFINITY,
className,
selectedValues,
overrideSearchFn,
renderOption,
searchPlaceholder,
...props
},
ref,
Expand Down Expand Up @@ -105,8 +108,6 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
// show 50 initially and then 20 more when reaching the end
const { itemsToShow, lastItemRef } = useShowMore<HTMLButtonElement>(50, 20);

const { overrideSearchFn } = props;

const optionsToShow = useMemo(() => {
const filteredOptions: {
label: string;
Expand Down Expand Up @@ -152,7 +153,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
}, [searchValue]);

return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal>
<PopoverTrigger asChild>
<Button
ref={ref}
Expand Down Expand Up @@ -238,7 +239,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
{/* Search */}
<div className="relative">
<Input
placeholder={props.searchPlaceholder || "Search"}
placeholder={searchPlaceholder || "Search"}
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
className="!h-auto rounded-b-none border-0 border-border border-b py-4 pl-10 focus-visible:ring-0 focus-visible:ring-offset-0"
Expand Down Expand Up @@ -285,9 +286,7 @@ export const MultiSelect = forwardRef<HTMLButtonElement, MultiSelectProps>(
</div>

<div className="min-w-0 grow">
{props.renderOption
? props.renderOption(option)
: option.label}
{renderOption ? renderOption(option) : option.label}
</div>
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export function ScrollShadow(props: {
scrollableClassName?: string;
disableTopShadow?: boolean;
shadowColor?: string;
shadowClassName?: string;
}) {
const scrollableEl = useRef<HTMLDivElement>(null);
const shadowTopEl = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -94,29 +95,45 @@ export function ScrollShadow(props: {
}
>
<div
className={cn(styles.scrollShadowTop, styles.scrollShadowY)}
className={cn(
styles.scrollShadowTop,
styles.scrollShadowY,
props.shadowClassName,
)}
ref={shadowTopEl}
style={{
opacity: "0",
display: props.disableTopShadow ? "none" : "block",
}}
/>
<div
className={cn(styles.scrollShadowBottom, styles.scrollShadowY)}
className={cn(
styles.scrollShadowBottom,
styles.scrollShadowY,
props.shadowClassName,
)}
ref={shadowBottomEl}
style={{
opacity: "0",
}}
/>
<div
className={cn(styles.scrollShadowLeft, styles.scrollShadowX)}
className={cn(
styles.scrollShadowLeft,
styles.scrollShadowX,
props.shadowClassName,
)}
ref={shadowLeftEl}
style={{
opacity: "0",
}}
/>
<div
className={cn(styles.scrollShadowRight, styles.scrollShadowX)}
className={cn(
styles.scrollShadowRight,
styles.scrollShadowX,
props.shadowClassName,
)}
ref={shadowRightEl}
style={{
opacity: "0",
Expand Down
7 changes: 6 additions & 1 deletion apps/dashboard/src/@/components/ui/code/code.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type CodeProps = {
scrollableClassName?: string;
keepPreviousDataOnCodeChange?: boolean;
copyButtonClassName?: string;
ignoreFormattingErrors?: boolean;
};

export const CodeClient: React.FC<CodeProps> = ({
Expand All @@ -21,10 +22,14 @@ export const CodeClient: React.FC<CodeProps> = ({
scrollableClassName,
keepPreviousDataOnCodeChange = false,
copyButtonClassName,
ignoreFormattingErrors,
}) => {
const codeQuery = useQuery({
queryKey: ["html", code],
queryFn: () => getCodeHtml(code, lang),
queryFn: () =>
getCodeHtml(code, lang, {
ignoreFormattingErrors: ignoreFormattingErrors,
}),
placeholderData: keepPreviousDataOnCodeChange
? keepPreviousData
: undefined,
Expand Down
19 changes: 16 additions & 3 deletions apps/dashboard/src/@/components/ui/code/getCodeHtml.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,28 @@ function isPrettierSupportedLang(lang: BundledLanguage) {
);
}

export async function getCodeHtml(code: string, lang: BundledLanguage) {
export async function getCodeHtml(
code: string,
lang: BundledLanguage,
options?: {
ignoreFormattingErrors?: boolean;
},
) {
const formattedCode = isPrettierSupportedLang(lang)
? await format(code, {
parser: "babel-ts",
plugins: [parserBabel, estree],
printWidth: 60,
}).catch((e) => {
console.error(e);
console.error("Failed to format code");
if (!options?.ignoreFormattingErrors) {
console.error(e);
console.error("Failed to format code");
console.log({
code,
lang,
});
}

return code;
})
: code;
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/ui/inline-code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function InlineCode({
return (
<code
className={cn(
"mx-0.5 inline rounded-lg border border-border px-1.5 py-[3px] font-mono text-[0.85em] text-foreground",
"mx-0.5 inline rounded-lg border border-border bg-muted px-[0.4em] py-[0.25em] font-mono text-[0.85em] text-foreground",
className,
)}
>
Expand Down
12 changes: 12 additions & 0 deletions apps/dashboard/src/@/components/ui/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,22 @@ function useUnderline<El extends HTMLElement>() {
}

update();
let resizeObserver: ResizeObserver | undefined = undefined;

if (containerRef.current) {
resizeObserver = new ResizeObserver(() => {
setTimeout(() => {
update();
}, 100);
});
resizeObserver.observe(containerRef.current);
}

// add event listener for resize
window.addEventListener("resize", update);
return () => {
window.removeEventListener("resize", update);
resizeObserver?.disconnect();
};
}, [activeTabEl]);

Expand Down
24 changes: 24 additions & 0 deletions apps/dashboard/src/@/components/ui/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,27 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
Textarea.displayName = "Textarea";

export { Textarea };

export function AutoResizeTextarea(props: TextareaProps) {
const textareaRef = React.useRef<HTMLTextAreaElement>(null);

// biome-ignore lint/correctness/useExhaustiveDependencies: value is needed in deps array
React.useEffect(() => {
const textarea = textareaRef.current;
if (textarea) {
textarea.style.height = "auto";
textarea.style.height = `${textarea.scrollHeight}px`;
}
}, [props.value]);

return (
<Textarea
ref={textareaRef}
{...props}
style={{
...props.style,
overflowY: "hidden",
}}
/>
);
}
2 changes: 2 additions & 0 deletions apps/dashboard/src/@/constants/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ export const BASE_URL = isProd
: (process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL
? `https://${process.env.NEXT_PUBLIC_VERCEL_BRANCH_URL}`
: "http://localhost:3000") || "https://thirdweb-dev.com";

export const NEXT_PUBLIC_NEBULA_URL = process.env.NEXT_PUBLIC_NEBULA_URL;
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/account/settings/getAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function getRawAccount() {
* If there's no account or account onboarding not complete, redirect to login page
* @param pagePath - the path of the current page to redirect back to after login/onboarding
*/
export async function getValidAccount(pagePath: string) {
export async function getValidAccount(pagePath?: string) {
const account = await getRawAccount();

// enforce login & onboarding
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export function SecondaryNavLinks() {
<ResourcesDropdownButton />

<Link
href="/support"
target="_blank"
href="https://thirdweb.com/support"
className="text-muted-foreground text-sm hover:text-foreground"
>
Support
Expand Down
Loading
Loading