Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dashboard-for-dapps): credential content filtering #394

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions apps/dashboard-for-dapps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@chakra-ui/react": "^3.0.2",
"@idos-network/codecs": "workspace:*",
"@idos-network/idos-sdk": "workspace:*",
"@stablelib/base64": "^1.0.1",
"@stablelib/utf8": "^1.0.1",
Expand Down
63 changes: 63 additions & 0 deletions apps/dashboard-for-dapps/src/components/secret-key-prompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
Button,
DialogActionTrigger,
DialogBody,
DialogCloseTrigger,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
DialogTitle,
Field,
PasswordInput,
} from "@/components/ui";
import { Stack } from "@chakra-ui/react";
import { useRef, useState } from "react";

export function SecretKeyPrompt({
open,
toggle,
onSubmit,
}: {
open: boolean;
toggle: (value?: boolean) => void;
onSubmit: (key: string) => void;
}) {
const ref = useRef<HTMLInputElement>(null);
const [key, setKey] = useState("");

const handleSave = () => {
onSubmit(key);
toggle(false);
};

return (
<DialogRoot
open={open}
placement="center"
onOpenChange={() => {
toggle(false);
}}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Enter your secret key</DialogTitle>
</DialogHeader>
<DialogBody>
<Stack gap="4">
<Field label="Secret key:">
<PasswordInput ref={ref} onChange={(e) => setKey(e.target.value)} />
</Field>
</Stack>
</DialogBody>
<DialogFooter>
<DialogActionTrigger asChild>
<Button variant="outline">Cancel</Button>
</DialogActionTrigger>
<Button onClick={handleSave}>Save</Button>
</DialogFooter>
<DialogCloseTrigger />
</DialogContent>
</DialogRoot>
);
}
1 change: 1 addition & 0 deletions apps/dashboard-for-dapps/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from "./dialog";
export * from "./drawer";
export * from "./empty-state";
export * from "./field";
export * from "./pagination";
export * from "./password-input";
export * from "./provider";
export * from "./refresh-button";
Expand Down
11 changes: 11 additions & 0 deletions apps/dashboard-for-dapps/src/components/ui/link-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use client";

import type { HTMLChakraProps, RecipeProps } from "@chakra-ui/react";
import { createRecipeContext } from "@chakra-ui/react";

export interface LinkButtonProps extends HTMLChakraProps<"a", RecipeProps<"button">> {}

const { withContext } = createRecipeContext({ key: "button" });

// Replace "a" with your framework's link component
export const LinkButton = withContext<HTMLAnchorElement, LinkButtonProps>("a");
188 changes: 188 additions & 0 deletions apps/dashboard-for-dapps/src/components/ui/pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
"use client";

import type { ButtonProps, TextProps } from "@chakra-ui/react";
import {
Button,
Pagination as ChakraPagination,
IconButton,
Text,
createContext,
usePaginationContext,
} from "@chakra-ui/react";
import * as React from "react";
import { HiChevronLeft, HiChevronRight, HiMiniEllipsisHorizontal } from "react-icons/hi2";
import { LinkButton } from "./link-button";

interface ButtonVariantMap {
current: ButtonProps["variant"];
default: ButtonProps["variant"];
ellipsis: ButtonProps["variant"];
}

type PaginationVariant = "outline" | "solid" | "subtle";

interface ButtonVariantContext {
size: ButtonProps["size"];
variantMap: ButtonVariantMap;
getHref?: (page: number) => string;
}

const [RootPropsProvider, useRootProps] = createContext<ButtonVariantContext>({
name: "RootPropsProvider",
});

export interface PaginationRootProps extends Omit<ChakraPagination.RootProps, "type"> {
size?: ButtonProps["size"];
variant?: PaginationVariant;
getHref?: (page: number) => string;
}

const variantMap: Record<PaginationVariant, ButtonVariantMap> = {
outline: { default: "ghost", ellipsis: "plain", current: "outline" },
solid: { default: "outline", ellipsis: "outline", current: "solid" },
subtle: { default: "ghost", ellipsis: "plain", current: "subtle" },
};

export const PaginationRoot = React.forwardRef<HTMLDivElement, PaginationRootProps>(
function PaginationRoot(props, ref) {
const { size = "sm", variant = "outline", getHref, ...rest } = props;
return (
<RootPropsProvider value={{ size, variantMap: variantMap[variant], getHref }}>
<ChakraPagination.Root ref={ref} type={getHref ? "link" : "button"} {...rest} />
</RootPropsProvider>
);
},
);

export const PaginationEllipsis = React.forwardRef<HTMLDivElement, ChakraPagination.EllipsisProps>(
function PaginationEllipsis(props, ref) {
const { size, variantMap } = useRootProps();
return (
<ChakraPagination.Ellipsis ref={ref} {...props} asChild>
<Button as="span" variant={variantMap.ellipsis} size={size}>
<HiMiniEllipsisHorizontal />
</Button>
</ChakraPagination.Ellipsis>
);
},
);

export const PaginationItem = React.forwardRef<HTMLButtonElement, ChakraPagination.ItemProps>(
function PaginationItem(props, ref) {
const { page } = usePaginationContext();
const { size, variantMap, getHref } = useRootProps();

const current = page === props.value;
const variant = current ? variantMap.current : variantMap.default;

if (getHref) {
return (
<LinkButton href={getHref(props.value)} variant={variant} size={size}>
{props.value}
</LinkButton>
);
}

return (
<ChakraPagination.Item ref={ref} {...props} asChild>
<Button variant={variant} size={size}>
{props.value}
</Button>
</ChakraPagination.Item>
);
},
);

export const PaginationPrevTrigger = React.forwardRef<
HTMLButtonElement,
ChakraPagination.PrevTriggerProps
>(function PaginationPrevTrigger(props, ref) {
const { size, variantMap, getHref } = useRootProps();
const { previousPage } = usePaginationContext();

if (getHref) {
return (
<LinkButton
href={previousPage != null ? getHref(previousPage) : undefined}
variant={variantMap.default}
size={size}
>
<HiChevronLeft />
</LinkButton>
);
}

return (
<ChakraPagination.PrevTrigger ref={ref} asChild {...props}>
<IconButton variant={variantMap.default} size={size}>
<HiChevronLeft />
</IconButton>
</ChakraPagination.PrevTrigger>
);
});

export const PaginationNextTrigger = React.forwardRef<
HTMLButtonElement,
ChakraPagination.NextTriggerProps
>(function PaginationNextTrigger(props, ref) {
const { size, variantMap, getHref } = useRootProps();
const { nextPage } = usePaginationContext();

if (getHref) {
return (
<LinkButton
href={nextPage != null ? getHref(nextPage) : undefined}
variant={variantMap.default}
size={size}
>
<HiChevronRight />
</LinkButton>
);
}

return (
<ChakraPagination.NextTrigger ref={ref} asChild {...props}>
<IconButton variant={variantMap.default} size={size}>
<HiChevronRight />
</IconButton>
</ChakraPagination.NextTrigger>
);
});

export const PaginationItems = (props: React.HTMLAttributes<HTMLElement>) => {
return (
<ChakraPagination.Context>
{({ pages }) =>
pages.map((page, index) => {
return page.type === "ellipsis" ? (
<PaginationEllipsis key={index} index={index} {...props} />
) : (
<PaginationItem key={index} type="page" value={page.value} {...props} />
);
})
}
</ChakraPagination.Context>
);
};

interface PageTextProps extends TextProps {
format?: "short" | "compact" | "long";
}

export const PaginationPageText = React.forwardRef<HTMLParagraphElement, PageTextProps>(
function PaginationPageText(props, ref) {
const { format = "compact", ...rest } = props;
const { page, totalPages, pageRange, count } = usePaginationContext();
const content = React.useMemo(() => {
if (format === "short") return `${page} / ${totalPages}`;
if (format === "compact") return `${page} of ${totalPages}`;
return `${pageRange.start + 1} - ${pageRange.end} of ${count}`;
}, [format, page, totalPages, pageRange, count]);

return (
<Text fontWeight="medium" ref={ref} {...rest}>
{content}
</Text>
);
},
);
6 changes: 6 additions & 0 deletions apps/dashboard-for-dapps/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useLocalStorage } from "@uidotdev/usehooks";

export const useSecretKey = () => {
const [secretKey, setSecretKey] = useLocalStorage("SECRET_KEY", "");
return [secretKey, setSecretKey] as const;
};
31 changes: 27 additions & 4 deletions apps/dashboard-for-dapps/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,17 @@
// Import Routes

import { Route as rootRoute } from "./routes/__root";
import { Route as CredentialsImport } from "./routes/credentials";
import { Route as IndexImport } from "./routes/index";

// Create/Update Routes

const CredentialsRoute = CredentialsImport.update({
id: "/credentials",
path: "/credentials",
getParentRoute: () => rootRoute,
} as any);

const IndexRoute = IndexImport.update({
id: "/",
path: "/",
Expand All @@ -32,39 +39,51 @@ declare module "@tanstack/react-router" {
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
"/credentials": {
id: "/credentials";
path: "/credentials";
fullPath: "/credentials";
preLoaderRoute: typeof CredentialsImport;
parentRoute: typeof rootRoute;
};
}
}

// Create and export the route tree

export interface FileRoutesByFullPath {
"/": typeof IndexRoute;
"/credentials": typeof CredentialsRoute;
}

export interface FileRoutesByTo {
"/": typeof IndexRoute;
"/credentials": typeof CredentialsRoute;
}

export interface FileRoutesById {
__root__: typeof rootRoute;
"/": typeof IndexRoute;
"/credentials": typeof CredentialsRoute;
}

export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: "/";
fullPaths: "/" | "/credentials";
fileRoutesByTo: FileRoutesByTo;
to: "/";
id: "__root__" | "/";
to: "/" | "/credentials";
id: "__root__" | "/" | "/credentials";
fileRoutesById: FileRoutesById;
}

export interface RootRouteChildren {
IndexRoute: typeof IndexRoute;
CredentialsRoute: typeof CredentialsRoute;
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
CredentialsRoute: CredentialsRoute,
};

export const routeTree = rootRoute
Expand All @@ -77,11 +96,15 @@ export const routeTree = rootRoute
"__root__": {
"filePath": "__root.tsx",
"children": [
"/"
"/",
"/credentials"
]
},
"/": {
"filePath": "index.tsx"
},
"/credentials": {
"filePath": "credentials.tsx"
}
}
}
Expand Down
Loading
Loading