From 808cd791e22a2c6b9ef46cad8df827ede862a464 Mon Sep 17 00:00:00 2001 From: Kevin Hill Date: Mon, 21 Oct 2024 05:48:58 -0700 Subject: [PATCH] more store actions and closer to a RC --- src/hooks/useStore.ts | 32 +++++-- src/hooks/useStorePrimitives.ts | 17 ++++ src/pages/Options/Tabs/JSONTab.tsx | 8 +- src/pages/Options/Tabs/SettingsTab.tsx | 95 +++++++++++++++++++ src/pages/Options/Tabs/WorkflowTab.tsx | 51 ++++++---- src/pages/Options/components/AddButton.tsx | 7 +- src/pages/Options/components/AppForm.tsx | 8 +- .../Options/components/RetoolAppUrl2.tsx | 20 ++-- 8 files changed, 191 insertions(+), 47 deletions(-) create mode 100644 src/hooks/useStorePrimitives.ts create mode 100644 src/pages/Options/Tabs/SettingsTab.tsx diff --git a/src/hooks/useStore.ts b/src/hooks/useStore.ts index d2b19bd..e8755ec 100644 --- a/src/hooks/useStore.ts +++ b/src/hooks/useStore.ts @@ -17,6 +17,7 @@ export type State = { workflow: { id: string; apiKey: string; + enabled: boolean; }; apps: RetoolApp[]; }; @@ -24,6 +25,7 @@ export type State = { export type Actions = { reset: () => void; addApp: (app: RetoolApp) => void; + createApp: (namr: RetoolApp["name"]) => void; removeApp: (name: RetoolApp["name"]) => void; updateApp: (name: RetoolApp["name"], props: Partial) => void; updateActiveApp: (props: Partial) => void; @@ -33,6 +35,8 @@ export type Actions = { setEditMode: (state: boolean) => void; setActiveTab: (tab: TabKeys) => void; updateWorkflow: (workflow: Partial) => void; + getRetoolWorkflowUrl: () => string; + toggleWorkflowProvider: () => void; }; export const STORAGE_KEY = "app-embedder-for-retool4"; @@ -45,6 +49,7 @@ const initialState: State = { workflow: { id: "13d34554-9891-40c0-a032-fda523774e97", apiKey: "retool_wk_bde9d74b27644cf3a0691211ff18dee2", + enabled: false, }, apps: [INSPECTOR_APP, ...DEMO_APPS], }; @@ -58,6 +63,17 @@ export const useStore = create()( setDomain: (domain) => set(() => ({ domain })), setEditMode: (isEditing) => set(() => ({ isEditing })), addApp: (app) => set((state) => ({ apps: [...state.apps, app] })), + createApp: (name: RetoolApp["name"]) => { + get().addApp({ + name, + public: false, + version: "latest", + env: "development", + hash: [], + query: [], + }); + get().setActiveApp(name); + }, removeApp: (name) => { set((state) => ({ apps: state.apps.filter((app) => app.name !== name), @@ -71,17 +87,15 @@ export const useStore = create()( })); }, getActiveApp: () => { - const activeAppName = get().activeAppName; - return get().apps.find((app) => app.name === activeAppName); + const { activeAppName, apps } = get(); + return apps.find((app) => app.name === activeAppName); }, setActiveApp: (name) => { set(() => ({ activeAppName: name })); }, updateActiveApp: (props) => { - const name = get().activeAppName; - if (name) { - get().updateApp(name, props); - } + const { activeAppName, updateApp } = get(); + if (activeAppName) updateApp(activeAppName, props); }, updateWorkflow: (props) => { set((state) => ({ @@ -89,6 +103,12 @@ export const useStore = create()( workflow: { ...state.workflow, ...props }, })); }, + getRetoolWorkflowUrl: () => { + return `https://${get().domain}.retool.com/workflows/${get().workflow.id}`; + }, + toggleWorkflowProvider: () => { + get().updateWorkflow({ enabled: !get().workflow.enabled }); + }, }), { name: STORAGE_KEY, diff --git a/src/hooks/useStorePrimitives.ts b/src/hooks/useStorePrimitives.ts new file mode 100644 index 0000000..18b4331 --- /dev/null +++ b/src/hooks/useStorePrimitives.ts @@ -0,0 +1,17 @@ +import { useStore } from "@/hooks/useStore"; + +import type { State } from "@/hooks/useStore"; + +export function useStorePrimitives() { + const _state = useStore(); + let state: Partial = {}; + + for (const [k, v] of Object.entries(_state)) { + const key = k as keyof State; + if (typeof _state[key] !== "function") { + state = { ...state, [key]: v }; + } + } + + return state; +} diff --git a/src/pages/Options/Tabs/JSONTab.tsx b/src/pages/Options/Tabs/JSONTab.tsx index 900845d..32a2391 100644 --- a/src/pages/Options/Tabs/JSONTab.tsx +++ b/src/pages/Options/Tabs/JSONTab.tsx @@ -1,14 +1,14 @@ import React from "react"; -import { Card, Col, Row } from "react-bootstrap"; +import { Card } from "react-bootstrap"; import Container from "react-bootstrap/Container"; -import { useStore } from "@/hooks/useStore"; +import { useStorePrimitives } from "@/hooks/useStorePrimitives"; import { SimpleJsonView } from "../components/SimpleJsonView"; function JSONTab() { - const state = useStore(); - console.log(state); + const state = useStorePrimitives(); + return (
diff --git a/src/pages/Options/Tabs/SettingsTab.tsx b/src/pages/Options/Tabs/SettingsTab.tsx new file mode 100644 index 0000000..dfe25cb --- /dev/null +++ b/src/pages/Options/Tabs/SettingsTab.tsx @@ -0,0 +1,95 @@ +import React from "react"; +import { Alert, Button, Col, Container, Row } from "react-bootstrap"; +import { FormProvider, type SubmitHandler, useForm } from "react-hook-form"; + +import { useEditMode } from "@/hooks/useEditMode"; +import { useStore } from "@/hooks/useStore"; +import { successToast } from "@/lib/toast"; + +import AppCard from "../components/AppCard"; +import AppForm from "../components/AppForm"; +import DomainInput from "../components/DomainInput"; + +import type { RetoolApp } from "@/types/extension"; + +function ConfigTab() { + const methods = useForm(); + const setActiveTab = useStore((s) => s.setActiveTab); + const { isEditing, startEditMode, stopEditMode } = useEditMode(); + + const createApp = useStore((s) => s.createApp); + const getActiveApp = useStore((s) => s.getActiveApp); + const activeAppName = useStore((s) => s.activeAppName); + const updateActiveApp = useStore((s) => s.updateActiveApp); + + const createNewApp = () => { + startEditMode(); + createApp("new-app-1"); + }; + + const onSave: SubmitHandler = (data) => { + updateActiveApp(data); + successToast("Edits saved."); + stopEditMode(); + }; + + const onCancel = () => { + methods.reset(); + stopEditMode(); + }; + + const app = getActiveApp(); + + return ( + <> + + +

General Config

+ + +

Current App

+ {isEditing ? ( + + + + ) : ( + + {activeAppName ? ( + startEditMode()} + /> + ) : ( + + None Selected + To view your app list{" "} + { + e.preventDefault(); + setActiveTab("storage"); + }} + > + click here + + . + + )} + + )} + +
+ {!isEditing && ( +
+ +
+ )} + + ); +} + +export default ConfigTab; diff --git a/src/pages/Options/Tabs/WorkflowTab.tsx b/src/pages/Options/Tabs/WorkflowTab.tsx index a96f936..8cd0d81 100644 --- a/src/pages/Options/Tabs/WorkflowTab.tsx +++ b/src/pages/Options/Tabs/WorkflowTab.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { Alert, Col, Row } from "react-bootstrap"; import Button from "react-bootstrap/Button"; import Card from "react-bootstrap/Card"; @@ -11,30 +11,37 @@ import { useWorkflowData } from "@/hooks/useWorkflow"; import { SimpleJsonView } from "../components/SimpleJsonView"; function WorkflowTab() { - const [useProvider, setUseProvider] = useState(false); + const { + apiKey, + id: workflowId, + enabled: workflowEnabled, + } = useStore((s) => s.workflow); - const { apiKey, id: workflowId } = useStore((s) => s.workflow); const updateWorkflow = useStore((s) => s.updateWorkflow); + const retoolWorkflowUrl = useStore((s) => s.getRetoolWorkflowUrl); + const toggleWorkflowProvider = useStore((s) => s.toggleWorkflowProvider); + const { workflow, fetchWorkflowData } = useWorkflowData(apiKey, workflowId); useEffect(() => { - if (useProvider && workflowId && apiKey) { + if (workflowEnabled && workflowId && apiKey) { fetchWorkflowData(); } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [useProvider, workflow]); + }, [workflowEnabled, workflowId, apiKey]); return (
-

Remote Apps List

+

Workflow Provider

How To Use - Here are all your saved Retool App Definitions + Use the workflow provider to add more apps to your list, from + Retool! @@ -42,11 +49,19 @@ function WorkflowTab() { - +
- Workflow Details + Workflow
+ + View in Retool +
@@ -57,7 +72,7 @@ function WorkflowTab() { Workflow ID updateWorkflow({ id: e.target.value })} /> @@ -70,7 +85,7 @@ function WorkflowTab() { updateWorkflow({ apiKey: e.target.value })} /> @@ -81,15 +96,15 @@ function WorkflowTab() {
); diff --git a/src/pages/Options/components/AppForm.tsx b/src/pages/Options/components/AppForm.tsx index 52061e5..de9d30a 100644 --- a/src/pages/Options/components/AppForm.tsx +++ b/src/pages/Options/components/AppForm.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import React from "react"; +import React, { useRef } from "react"; import { Button, Col, Form, InputGroup, Row } from "react-bootstrap"; import { Controller, useFieldArray, useForm } from "react-hook-form"; @@ -41,8 +41,6 @@ function AppForm({ app, onSave, onCancel }: Props) { defaultValues: { ...app }, }); - // console.log(watch()); - const hashFields = useFieldArray({ name: "hash", control }); const queryFields = useFieldArray({ name: "query", control }); @@ -217,10 +215,6 @@ function AppForm({ app, onSave, onCancel }: Props) { - {/* - - - */} diff --git a/src/pages/Options/components/RetoolAppUrl2.tsx b/src/pages/Options/components/RetoolAppUrl2.tsx index 3d4068f..7c1f8be 100644 --- a/src/pages/Options/components/RetoolAppUrl2.tsx +++ b/src/pages/Options/components/RetoolAppUrl2.tsx @@ -7,12 +7,12 @@ import ParameterList from "./ParameterList"; import type { RetoolApp } from "@/types/extension"; -function RetoolAppUrl2({ app, domain }: { app: RetoolApp; domain: string }) { +function RetoolAppUrl2({ app, domain }: { app?: RetoolApp; domain: string }) { const fullUrl = useRetoolAppUrl(domain, app); const url = new URL(fullUrl); const queryParams = Array.from(url.searchParams.entries()); - const hashParams = app.hash.map( + const hashParams = app?.hash?.map( (p) => [p.param, p.value] as [string, string] ); @@ -23,7 +23,7 @@ function RetoolAppUrl2({ app, domain }: { app: RetoolApp; domain: string }) { href={fullUrl.toString()} target="_blank" rel="noreferrer" - title={`Open App in Retool (${app.name})`} + title={`Open App in Retool (${app?.name})`} >

{url.origin + url.pathname}

@@ -34,14 +34,14 @@ function RetoolAppUrl2({ app, domain }: { app: RetoolApp; domain: string }) { - -
Hash Params:
-
    - {hashParams.length > 0 && ( + {hashParams && hashParams.length > 0 && ( + +
    Hash Params:
    +
      - )} -
    - +
+ + )}
);