Style the settings onboarding card

This commit is contained in:
mrjvs 2024-01-20 13:09:42 +01:00
parent de5d47a730
commit f96a0de373
4 changed files with 145 additions and 33 deletions

View File

@ -2,7 +2,10 @@ import classNames from "classnames";
import { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { StatusCircle } from "@/components/player/internals/StatusCircle";
import {
StatusCircle,
StatusCircleProps,
} from "@/components/player/internals/StatusCircle";
import { Transition } from "@/components/utils/Transition";
export interface ScrapeItemProps {
@ -23,13 +26,14 @@ const statusTextMap: Partial<Record<ScrapeCardProps["status"], string>> = {
pending: "player.scraping.items.pending",
};
const statusMap: Record<ScrapeCardProps["status"], StatusCircle["type"]> = {
failure: "error",
notfound: "noresult",
pending: "loading",
success: "success",
waiting: "waiting",
};
const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> =
{
failure: "error",
notfound: "noresult",
pending: "loading",
success: "success",
waiting: "waiting",
};
export function ScrapeItem(props: ScrapeItemProps) {
const { t } = useTranslation();

View File

@ -4,23 +4,24 @@ import classNames from "classnames";
import { Icon, Icons } from "@/components/Icon";
import { Transition } from "@/components/utils/Transition";
export interface StatusCircle {
export interface StatusCircleProps {
type: "loading" | "success" | "error" | "noresult" | "waiting";
percentage?: number;
className?: string;
}
export interface StatusCircleLoading extends StatusCircle {
export interface StatusCircleLoading extends StatusCircleProps {
type: "loading";
percentage: number;
}
function statusIsLoading(
props: StatusCircle | StatusCircleLoading,
props: StatusCircleProps | StatusCircleLoading,
): props is StatusCircleLoading {
return props.type === "loading";
}
export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
export function StatusCircle(props: StatusCircleProps | StatusCircleLoading) {
const [spring] = useSpring(
() => ({
percentage: statusIsLoading(props) ? props.percentage : 0,
@ -30,18 +31,21 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
return (
<div
className={classNames({
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
true,
"text-video-scraping-loading": props.type === "loading",
"text-video-scraping-noresult text-opacity-50":
props.type === "waiting",
"text-video-scraping-error bg-video-scraping-error":
props.type === "error",
"text-green-500 bg-green-500": props.type === "success",
"text-video-scraping-noresult bg-video-scraping-noresult":
props.type === "noresult",
})}
className={classNames(
{
"p-0.5 border-current border-[3px] rounded-full h-6 w-6 relative transition-colors":
true,
"text-video-scraping-loading": props.type === "loading",
"text-video-scraping-noresult text-opacity-50":
props.type === "waiting",
"text-video-scraping-error bg-video-scraping-error":
props.type === "error",
"text-green-500 bg-green-500": props.type === "success",
"text-video-scraping-noresult bg-video-scraping-noresult":
props.type === "noresult",
},
props.className,
)}
>
<Transition animation="fade" show={statusIsLoading(props)}>
<svg
@ -65,13 +69,13 @@ export function StatusCircle(props: StatusCircle | StatusCircleLoading) {
</Transition>
<Transition animation="fade" show={props.type === "error"}>
<Icon
className="absolute inset-0 flex items-center justify-center text-white"
className="absolute inset-0 flex items-center justify-center text-background-main"
icon={Icons.X}
/>
</Transition>
<Transition animation="fade" show={props.type === "success"}>
<Icon
className="absolute inset-0 flex items-center text-xs justify-center text-white"
className="absolute inset-0 flex items-center text-sm justify-center text-background-main"
icon={Icons.CHECKMARK}
/>
</Transition>

View File

@ -1,9 +1,19 @@
import classNames from "classnames";
import { t } from "i18next";
import { ReactNode } from "react";
import { useNavigate } from "react-router-dom";
import { useAsync } from "react-use";
import { isExtensionActive } from "@/backend/extension/messaging";
import { singularProxiedFetch } from "@/backend/helpers/fetch";
import { Button } from "@/components/buttons/Button";
import { Icon, Icons } from "@/components/Icon";
import { SettingsCard } from "@/components/layout/SettingsCard";
import {
StatusCircle,
StatusCircleProps,
} from "@/components/player/internals/StatusCircle";
import { Heading3 } from "@/components/utils/Text";
import { useAuthStore } from "@/stores/auth";
const testUrl = "https://postman-echo.com/get";
@ -63,17 +73,110 @@ function useIsSetup() {
};
}
function SetupCheckList(props: {
status: Status;
grey?: boolean;
children?: ReactNode;
}) {
const statusMap: Record<Status, StatusCircleProps["type"]> = {
error: "error",
success: "success",
unset: "noresult",
};
return (
<div className="flex items-start text-type-dimmed my-4">
<StatusCircle
type={statusMap[props.status]}
className={classNames({
"!text-video-scraping-noresult !bg-video-scraping-noresult opacity-50":
props.grey,
"scale-90 mr-3": true,
})}
/>
<div>
<p
className={classNames({
"!text-type-dimmed opacity-75": props.grey,
"text-type-danger": props.status === "error",
"text-white": props.status === "success",
})}
>
{props.children}
</p>
{props.status === "error" ? (
<p className="max-w-96">
There is something wrong with this setting. Go through setup again
to fix it.
</p>
) : null}
</div>
</div>
);
}
export function SetupPart() {
const navigate = useNavigate();
const { loading, setupStates, globalState } = useIsSetup();
if (loading || !setupStates) return <p>Loading states...</p>;
const textLookupMap: Record<Status, { title: string; desc: string }> = {
error: {
title: "err1",
desc: "err2",
},
success: {
title: "success1",
desc: "success2",
},
unset: {
title: "unset1",
desc: "unset2",
},
};
return (
<div>
<p className="font-bold text-white">state: {globalState}</p>
<p>extension: {setupStates.extension}</p>
<p>proxy: {setupStates.proxy}</p>
<p>defaults: {setupStates.defaultProxy}</p>
<Button onClick={() => navigate("/onboarding")}>Do setup</Button>
</div>
<SettingsCard>
<div className="flex items-start gap-4">
<div>
<div
className={classNames({
"rounded-full h-12 w-12 flex bg-opacity-15 justify-center items-center":
true,
"text-type-success bg-type-success": globalState === "success",
"text-type-danger bg-type-danger":
globalState === "error" || globalState === "unset",
})}
>
<Icon
icon={globalState === "success" ? Icons.CHECKMARK : Icons.X}
className="text-xl"
/>
</div>
</div>
<div className="flex-1">
<Heading3 className="!mb-3">
{t(textLookupMap[globalState].title)}
</Heading3>
<p className="max-w-[20rem] font-medium mb-6">
{t(textLookupMap[globalState].desc)}
</p>
<SetupCheckList status={setupStates.extension}>
Extension
</SetupCheckList>
<SetupCheckList status={setupStates.proxy}>
Custom proxy
</SetupCheckList>
<SetupCheckList grey status={setupStates.defaultProxy}>
Default setup
</SetupCheckList>
</div>
<div className="mt-5">
<Button theme="purple" onClick={() => navigate("/onboarding")}>
Do setup
</Button>
</div>
</div>
</SettingsCard>
);
}

View File

@ -152,6 +152,7 @@ export const defaultTheme = {
divider: tokens.ash.c500,
secondary: tokens.ash.c100,
danger: tokens.semantic.red.c100,
success: tokens.semantic.green.c100,
link: tokens.purple.c100,
linkHover: tokens.purple.c50,
},