Skip to content

Commit

Permalink
feat: debounce
Browse files Browse the repository at this point in the history
  • Loading branch information
rharkor committed Sep 17, 2024
1 parent ec0770d commit 399a610
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 46 deletions.
26 changes: 25 additions & 1 deletion packages/cli-app/src/api/configuration/queries.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
import { z } from "zod"

import { getConfiguration } from "@/lib/configuration"
import { getPlugins } from "@/lib/plugins"
import { handleApiError } from "@/lib/utils/server-utils"
import { apiInputFromSchema } from "@/types"
import { getStoreUID } from "@next-boilerplate/cli-helpers/stores"
import { TRPCError } from "@trpc/server"

import { getConfigurationResponseSchema } from "./schemas"

export const getConfigurationQuery = async ({}: apiInputFromSchema<typeof undefined>) => {
try {
const configuration = await getConfiguration()
const _configuration = await getConfiguration()
const plugins = await getPlugins()

const configuration = {
..._configuration,
plugins: _configuration.plugins?.map((plugin) => {
const foundPlugin = plugins.find(
(p) => p.sourcePath === plugin.sourcePath && getStoreUID(p.store) === getStoreUID(plugin.store)
)
if (!foundPlugin) {
throw new TRPCError({
message: `The plugin ${plugin.sourcePath} was not found (store: ${plugin.store.name}@${plugin.store.version}). Currently available plugins: ${plugins.map((p) => p.sourcePath).join(", ")}`,
code: "INTERNAL_SERVER_ERROR",
})
}

return {
...plugin,
remotePlugin: foundPlugin,
}
}),
}

const data: z.infer<ReturnType<typeof getConfigurationResponseSchema>> = { configuration }
return data
Expand Down
16 changes: 15 additions & 1 deletion packages/cli-app/src/api/configuration/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from "zod"

import { fullPluginSchema } from "@/lib/plugins/types"
import { pluginSchema } from "@next-boilerplate/cli-helpers/plugins"
import { storeSchema } from "@next-boilerplate/cli-helpers/stores"

export const configurationSchema = () =>
Expand All @@ -11,7 +12,20 @@ export const configurationSchema = () =>
})
export type TConfiguration = z.infer<ReturnType<typeof configurationSchema>>

export const getConfigurationResponseSchema = () => z.object({ configuration: configurationSchema() })
export const getConfigurationResponseSchema = () =>
z.object({
configuration: configurationSchema()
.omit({ plugins: true })
.extend({
plugins: z
.array(
fullPluginSchema.extend({
remotePlugin: pluginSchema,
})
)
.optional(),
}),
})

export const updateConfigurationRequestSchema = () => z.object({ configuration: configurationSchema() })
export const updateConfigurationResponseSchema = () => z.object({ configuration: configurationSchema() })
Expand Down
63 changes: 44 additions & 19 deletions packages/cli-app/src/app/components/current-configuration.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fragment, useRef, useState } from "react"
import { Fragment, useEffect, useMemo, useRef, useState } from "react"

import { AnimatePresence, motion } from "framer-motion"
import { ArrowDownToLine, Eye, MoreHorizontal, Pencil } from "lucide-react"
Expand All @@ -15,7 +15,7 @@ import Section from "@/components/ui/section"
import { TDictionary } from "@/lib/langs"
import { trpc } from "@/lib/trpc/client"
import { RouterOutputs } from "@/lib/trpc/utils"
import { getItemUID } from "@next-boilerplate/cli-helpers/stores"
import { getItemUID, getStoreUID } from "@next-boilerplate/cli-helpers/stores"
import { Button } from "@nextui-org/button"
import { Divider } from "@nextui-org/divider"
import { Dropdown, DropdownItem, DropdownMenu, DropdownTrigger } from "@nextui-org/dropdown"
Expand Down Expand Up @@ -157,6 +157,24 @@ export default function CurrentConfiguration({
// }, [configuration.data.configuration.plugins])

const [search, setSearch] = useState("")
const [debouncedSearch, setDebouncedSearch] = useState(search)

useEffect(() => {
const timeout = setTimeout(() => {
setDebouncedSearch(search)
}, 300)
return () => {
clearTimeout(timeout)
}
}, [search])

const configPlugins = useMemo(() => {
return (
configuration.data.configuration.plugins?.filter((plugin) => {
return plugin.name.toLowerCase().includes(debouncedSearch.toLowerCase())
}) ?? []
)
}, [configuration.data.configuration.plugins, debouncedSearch])

const hasEmptyConfiguration =
!configuration.data.configuration.plugins || configuration.data.configuration.plugins.length === 0
Expand All @@ -168,11 +186,7 @@ export default function CurrentConfiguration({
await updateConfiguration({
configuration: {
plugins: configuration.data.configuration.plugins?.map((p) => {
if (
p.name === plugin.name &&
p.store.name === plugin.store.name &&
p.store.version === plugin.store.version
) {
if (p.name === plugin.name && getStoreUID(p.store) === getStoreUID(plugin.store)) {
return plugin
}
return p
Expand All @@ -181,11 +195,6 @@ export default function CurrentConfiguration({
})
}

const configPlugins =
configuration.data.configuration.plugins?.filter((plugin) => {
return plugin.name.toLowerCase().includes(search.toLowerCase())
}) ?? []

return (
<Section>
<Header
Expand Down Expand Up @@ -215,7 +224,7 @@ export default function CurrentConfiguration({
{configPlugins.map((plugin) => (
<Plugin
key={getItemUID(plugin)}
pluginInConfiguration={plugin}
plugin={plugin}
dictionary={dictionary}
isPending={isPending}
onDelete={onDelete(plugin)}
Expand Down Expand Up @@ -245,13 +254,13 @@ export default function CurrentConfiguration({
}

function Plugin({
pluginInConfiguration,
plugin,
dictionary,
onDelete: _onDelete,
onEdit: _onEdit,
isPending,
}: {
pluginInConfiguration: TPlugin
plugin: TPlugin
dictionary: TDictionary<typeof CurrentConfigurationDr>
onDelete: (boundingBox: DOMRect) => Promise<void>
onEdit: (plugin: TPlugin) => Promise<void>
Expand All @@ -275,14 +284,31 @@ function Plugin({
onClose: onEditClose,
} = useDisclosure()

const [overriddenPlugin, setOverriddenPlugin] = useState<TPlugin>(plugin)

useEffect(() => {
setOverriddenPlugin(plugin)
}, [plugin])

const onEdit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
await _onEdit(plugin)
await _onEdit(overriddenPlugin)
onEditClose()
}

const editPath = (fromKey: string) => (to: string) => {
// TODO
setOverriddenPlugin((prev) => ({
...prev,
paths: prev.paths.map((p) => {
if (p.from === fromKey) {
return {
...p,
to,
}
}
return p
}),
}))
}

return (
Expand Down Expand Up @@ -376,9 +402,8 @@ function Plugin({
<div className="flex flex-col gap-1">
<Input isDisabled isReadOnly value={p.from} label={dictionary.sourcePath} />
<Input
value={p.to}
value={overriddenPlugin.paths.find((op) => op.from === p.from)?.to ?? ""}
onValueChange={editPath(p.from)}
/* TODO Replace with original to */
placeholder={p.to}
label={dictionary.outputPath}
/>
Expand Down
6 changes: 2 additions & 4 deletions packages/cli-app/src/app/plugins/[id]/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Section from "@/components/ui/section"
import { TDictionary } from "@/lib/langs"
import { trpc } from "@/lib/trpc/client"
import { RouterOutputs } from "@/lib/trpc/utils"
import { getStoreUID } from "@next-boilerplate/cli-helpers/stores"
import { Button } from "@nextui-org/button"
import { Spinner } from "@nextui-org/spinner"

Expand Down Expand Up @@ -44,10 +45,7 @@ export default function PluginContent({

const isInstalled = useMemo(() => {
return configuration.data.configuration.plugins?.some(
(p) =>
p.name === plugin.data.plugin.name &&
p.store.name === plugin.data.plugin.store.name &&
p.store.version === plugin.data.plugin.store.version
(p) => p.name === plugin.data.plugin.name && getStoreUID(p.store) === getStoreUID(plugin.data.plugin.store)
)
}, [configuration.data, plugin.data])

Expand Down
7 changes: 2 additions & 5 deletions packages/cli-app/src/app/plugins/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import ItemCard from "@/components/ui/item-card"
import { TDictionary } from "@/lib/langs"
import { trpc } from "@/lib/trpc/client"
import { RouterOutputs } from "@/lib/trpc/utils"
import { getItemUID } from "@next-boilerplate/cli-helpers/stores"
import { getItemUID, getStoreUID } from "@next-boilerplate/cli-helpers/stores"
import { Button } from "@nextui-org/button"
import { Spinner } from "@nextui-org/spinner"
import { Tooltip } from "@nextui-org/tooltip"
Expand Down Expand Up @@ -37,10 +37,7 @@ export default function Plugin({

const isPluginInConfiguration = useMemo(() => {
return configuration.data.configuration.plugins?.some(
(_plugin) =>
_plugin.name === plugin.name &&
_plugin.store.name === plugin.store.name &&
_plugin.store.version === plugin.store.version
(_plugin) => _plugin.name === plugin.name && getStoreUID(_plugin.store) === getStoreUID(plugin.store)
)
}, [configuration.data.configuration.plugins, plugin])

Expand Down
22 changes: 16 additions & 6 deletions packages/cli-app/src/app/templates/[id]/content.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import { useState } from "react"
import { useEffect, useMemo, useState } from "react"

import { BookUp } from "lucide-react"

Expand Down Expand Up @@ -61,12 +61,22 @@ export default function TemplateContent({
}

const [search, setSearch] = useState("")
const [debouncedSearch, setDebouncedSearch] = useState("")

const plugins = template.data.template.plugins.filter(
(plugin) => {
return plugin.name.toLowerCase().includes(search.toLowerCase())
},
[search]
useEffect(() => {
const timeout = setTimeout(() => {
setDebouncedSearch(search)
}, 300)

return () => clearTimeout(timeout)
}, [search])

const plugins = useMemo(
() =>
template.data.template.plugins.filter((plugin) => {
return plugin.name.toLowerCase().includes(debouncedSearch.toLowerCase())
}),
[template.data.template.plugins, debouncedSearch]
)

return (
Expand Down
6 changes: 2 additions & 4 deletions packages/cli-app/src/lib/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { z } from "zod"

import { configurationSchema as webConfigurationSchema, TConfiguration } from "@/api/configuration/schemas"
import { configSchema } from "@next-boilerplate/cli-helpers/config"
import { getStoreUID } from "@next-boilerplate/cli-helpers/stores"
import { handleDownloadStores } from "@next-boilerplate/cli-helpers/stores-helpers"
import { logger } from "@rharkor/logger"
import { TRPCError } from "@trpc/server"
Expand Down Expand Up @@ -78,10 +79,7 @@ const apiConfigToWebConfig = async (apiConfig: z.infer<typeof optionalConfigSche
name: apiConfig.name,
plugins: apiConfig.plugins?.map((plugin) => {
const foundPlugin = plugins.find(
(p) =>
p.sourcePath === plugin.name &&
p.store.name === plugin.store.name &&
p.store.version === plugin.store.version
(p) => p.sourcePath === plugin.name && getStoreUID(p.store) === getStoreUID(plugin.store)
)
if (!foundPlugin) {
throw new TRPCError({
Expand Down
1 change: 1 addition & 0 deletions packages/cli-app/src/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const doNotSendNotTranslatedError = false
5 changes: 1 addition & 4 deletions packages/cli-app/src/lib/templates/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,7 @@ export const getTemplate = async (name: string, store: z.infer<typeof storeConfi
const pluginInTemplate =
typeof _pluginInTemplate === "string" ? { name: _pluginInTemplate, store } : _pluginInTemplate
const plugin = plugins.find(
(p) =>
p.sourcePath === pluginInTemplate.name &&
p.store.name === pluginInTemplate.store.name &&
p.store.version === pluginInTemplate.store.version
(p) => p.sourcePath === pluginInTemplate.name && getStoreUID(p.store) === getStoreUID(pluginInTemplate.store)
)
if (!plugin) {
logger.error(
Expand Down
5 changes: 3 additions & 2 deletions packages/cli-app/src/lib/utils/server-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { logger } from "@rharkor/logger"
import { TRPCError } from "@trpc/server"
import { TRPC_ERROR_CODE_KEY } from "@trpc/server/rpc"

import { doNotSendNotTranslatedError } from "../constants"
import { i18n, Locale } from "../i18n-config"
import { _getDictionary, TDictionary } from "../langs"

Expand Down Expand Up @@ -78,11 +79,11 @@ export async function ApiError(
const locale = extractLocale()
const dictionary = await _getDictionary("errors", locale, undefined)
let message = findNestedKeyInDictionary(messageCode, dictionary)
if (!message) {
if (!message && doNotSendNotTranslatedError) {
logger.error(new Error(`Error not found in dictionary: ${messageCode}`))
message = dictionary.unknownError
}
const data: TErrorMessage = { message, code: messageCode, extra }
const data: TErrorMessage = { message: message ?? messageCode, code: messageCode, extra }
throw new TRPCError({
code: code ?? "BAD_REQUEST",
message: JSON.stringify(data),
Expand Down

0 comments on commit 399a610

Please sign in to comment.