From f96a0de373c81539f0db8ecf9a3fcb2598a5c6e3 Mon Sep 17 00:00:00 2001
From: mrjvs <jellevs@gmail.com>
Date: Sat, 20 Jan 2024 13:09:42 +0100
Subject: [PATCH] Style the settings onboarding card

---
 .../player/internals/ScrapeCard.tsx           |  20 +--
 .../player/internals/StatusCircle.tsx         |  40 +++---
 src/pages/parts/settings/SetupPart.tsx        | 117 ++++++++++++++++--
 themes/default.ts                             |   1 +
 4 files changed, 145 insertions(+), 33 deletions(-)

diff --git a/src/components/player/internals/ScrapeCard.tsx b/src/components/player/internals/ScrapeCard.tsx
index 479bf2b1..dc057901 100644
--- a/src/components/player/internals/ScrapeCard.tsx
+++ b/src/components/player/internals/ScrapeCard.tsx
@@ -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();
diff --git a/src/components/player/internals/StatusCircle.tsx b/src/components/player/internals/StatusCircle.tsx
index 32855321..ede7392e 100644
--- a/src/components/player/internals/StatusCircle.tsx
+++ b/src/components/player/internals/StatusCircle.tsx
@@ -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>
diff --git a/src/pages/parts/settings/SetupPart.tsx b/src/pages/parts/settings/SetupPart.tsx
index f639b43b..478722c6 100644
--- a/src/pages/parts/settings/SetupPart.tsx
+++ b/src/pages/parts/settings/SetupPart.tsx
@@ -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>
   );
 }
diff --git a/themes/default.ts b/themes/default.ts
index dfda2d86..bd31b3ff 100644
--- a/themes/default.ts
+++ b/themes/default.ts
@@ -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,
       },