Skip to content

Commit

Permalink
feat: stores
Browse files Browse the repository at this point in the history
  • Loading branch information
rharkor committed Sep 13, 2024
1 parent 06d2c05 commit f8bb414
Show file tree
Hide file tree
Showing 50 changed files with 176 additions and 1,000 deletions.
4 changes: 2 additions & 2 deletions packages/cli-app/src/api/configuration/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { z } from "zod"

import { getConfiguration, setConfiguration } from "@/lib/configuration"
import { env } from "@/lib/env"
import { pluginsDirectory } from "@/lib/plugins"
import { rootPluginsDirectory } from "@/lib/plugins"
import { handleApiError } from "@/lib/utils/server-utils"
import { apiInputFromSchema } from "@/types"
import { applyConfigurationTask } from "@next-boilerplate/scripts/utils/template-config/apply.js"
Expand Down Expand Up @@ -46,7 +46,7 @@ export const applyConfiguration = async ({}: apiInputFromSchema<typeof undefined
//* Apply the configuration
await applyConfigurationTask({
configFileName: "config.json",
pluginsDirectory,
pluginsDirectory: rootPluginsDirectory,
root: env.ROOT_PATH,
noTask: true,
})
Expand Down
1 change: 1 addition & 0 deletions packages/cli-app/src/api/configuration/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const configurationSchema = () =>
z.object({
name: z.string().optional(),
plugins: z.array(fullPluginSchema).optional(),
stores: z.array(z.string()).optional(),
})
export type TConfiguration = z.infer<ReturnType<typeof configurationSchema>>

Expand Down
1 change: 1 addition & 0 deletions packages/cli-app/src/app/components/sidenav.dr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const SidenavDr = dictionaryRequirements({
configuration: true,
templates: true,
plugins: true,
stores: true,
})
7 changes: 7 additions & 0 deletions packages/cli-app/src/app/components/sidenav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export default function Sidenav({
afterContent={
<motion.div
className="relative"
initial={{
scale: 0,
opacity: 0,
}}
animate={
(configuration.data.configuration.plugins?.length ?? 0) > 0
? {
Expand Down Expand Up @@ -85,6 +89,9 @@ export default function Sidenav({
<Item href="/plugins" isCurrent={pathname === "/plugins"}>
{dictionary.plugins}
</Item>
<Item href="/stores" isCurrent={pathname === "/stores"}>
{dictionary.stores}
</Item>
</ul>
</nav>
)
Expand Down
15 changes: 15 additions & 0 deletions packages/cli-app/src/app/stores/content.dr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { HeaderDr } from "@/components/ui/header.dr"
import { dictionaryRequirements } from "@/lib/utils/dictionary"

export const StoresContentDr = dictionaryRequirements(
{
stores: true,
search: true,
addStore: true,
storeRemote: true,
storeRemoteExample: true,
close: true,
save: true,
},
HeaderDr
)
101 changes: 101 additions & 0 deletions packages/cli-app/src/app/stores/content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"use client"

import { useState } from "react"

import { Plus } from "lucide-react"

import Header from "@/components/ui/header"
import ItemCard from "@/components/ui/item-card"
import { ModalHeader } from "@/components/ui/modal"
import { TDictionary } from "@/lib/langs"
import { trpc } from "@/lib/trpc/client"
import { RouterOutputs } from "@/lib/trpc/utils"
import { gitRemoteToName } from "@/lib/utils/client-utils"
import { Button } from "@nextui-org/button"
import { Input } from "@nextui-org/input"
import { Modal, ModalBody, ModalContent, ModalFooter } from "@nextui-org/modal"

import { StoresContentDr } from "./content.dr"

export default function StoresContent({
ssrConfiguration,
dictionary,
}: {
ssrConfiguration: RouterOutputs["configuration"]["getConfiguration"]
dictionary: TDictionary<typeof StoresContentDr>
}) {
const configuration = trpc.configuration.getConfiguration.useQuery(undefined, {
initialData: ssrConfiguration,
})

const [isAddStoreOpen, setIsAddStoreOpen] = useState(false)
const [newStoreName, setNewStoreName] = useState("")

const utils = trpc.useUtils()
const updateConfigurationMutation = trpc.configuration.updateConfiguration.useMutation({
onSuccess: async () => {
await utils.configuration.invalidate()
},
})
const onAddStore = async (e: React.FormEvent) => {
e.preventDefault()
if (!newStoreName) return
await updateConfigurationMutation.mutateAsync({
configuration: {
...configuration.data.configuration,
stores: Array.from(new Set([...(configuration.data.configuration.stores ?? []), newStoreName])),
},
})
setIsAddStoreOpen(false)
setNewStoreName("")
}

const isPending = updateConfigurationMutation.isPending

return (
<>
<Header
title={dictionary.stores}
dictionary={dictionary}
actions={
<Button color="primary" onPress={() => setIsAddStoreOpen(true)} isLoading={isPending}>
<Plus className="size-4 shrink-0" />
{dictionary.addStore}
</Button>
}
/>
<ul className="flex flex-1 flex-col gap-2">
{(configuration.data.configuration.stores ?? []).map((storeRemote) => (
<ItemCard key={storeRemote} id={storeRemote} title={gitRemoteToName(storeRemote)} description={storeRemote} />
))}
</ul>
<Modal isOpen={isAddStoreOpen} onOpenChange={setIsAddStoreOpen}>
<ModalContent>
{(onClose) => (
<>
<ModalHeader className="flex flex-col gap-1">{dictionary.addStore}</ModalHeader>
<form onSubmit={onAddStore}>
<ModalBody>
<Input
label={dictionary.storeRemote}
value={newStoreName}
onValueChange={setNewStoreName}
placeholder={dictionary.storeRemoteExample}
/>
</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onClose} isDisabled={isPending}>
{dictionary.close}
</Button>
<Button color="primary" type="submit" isLoading={isPending}>
{dictionary.save}
</Button>
</ModalFooter>
</form>
</>
)}
</ModalContent>
</Modal>
</>
)
}
20 changes: 20 additions & 0 deletions packages/cli-app/src/app/stores/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Section from "@/components/ui/section"
import { getDictionary } from "@/lib/langs"
import { serverTrpc } from "@/lib/trpc/server"
import { dictionaryRequirements } from "@/lib/utils/dictionary"
import { extractLocale } from "@/lib/utils/server-utils"

import StoresContent from "./content"
import { StoresContentDr } from "./content.dr"

export default async function Stores() {
const locale = extractLocale()
const dictionary = await getDictionary(locale, dictionaryRequirements(StoresContentDr))
const ssrConfiguration = await serverTrpc.configuration.getConfiguration()

return (
<Section>
<StoresContent ssrConfiguration={ssrConfiguration} dictionary={dictionary} />
</Section>
)
}
4 changes: 2 additions & 2 deletions packages/cli-app/src/components/ui/item-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export default function ItemCard({
title: string
subTitle?: React.ReactNode
actions?: React.ReactNode
description: string
description?: string
className?: string
endContent?: React.ReactNode
href?: string
Expand All @@ -39,7 +39,7 @@ export default function ItemCard({
</div>
<div className="flex flex-row gap-2">{actions}</div>
</div>
<p className="min-h-5 truncate text-sm">{description}</p>
{description !== undefined && <p className="min-h-5 truncate text-sm">{description}</p>}
{endContent}
</>
)
Expand Down
7 changes: 6 additions & 1 deletion packages/cli-app/src/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,5 +237,10 @@
"pluginSettings": "Plugin settings",
"apply": "Apply",
"configurationApplied": "Configuration applied",
"search": "Search"
"search": "Search",
"stores": "Stores",
"store": "Store",
"addStore": "Add store",
"storeRemote": "Store URL (git)",
"storeRemoteExample": "[email protected]:rharkor/next-boilerplate--store.git"
}
7 changes: 6 additions & 1 deletion packages/cli-app/src/langs/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,5 +237,10 @@
"pluginSettings": "Paramètres du plugin",
"apply": "Appliquer",
"configurationApplied": "Configuration appliquée",
"search": "Rechercher"
"search": "Rechercher",
"stores": "Magasins",
"store": "Magasin",
"addStore": "Ajouter un magasin",
"storeRemote": "URL du magasin (git)",
"storeRemoteExample": "[email protected]:rharkor/next-boilerplate--store.git"
}
3 changes: 3 additions & 0 deletions packages/cli-app/src/lib/configuration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const webConfigToApiConfig = (webConfig: TConfiguration): z.infer<typeof optiona
}
return fullP
}),
// TODO Default store
stores: webConfig.stores ?? [],
})
return content
} catch (error) {
Expand Down Expand Up @@ -95,6 +97,7 @@ const apiConfigToWebConfig = async (apiConfig: z.infer<typeof optionalConfigSche
}),
}
}),
stores: apiConfig.stores,
}
webConfigurationSchema().parse(content)
return content
Expand Down
12 changes: 7 additions & 5 deletions packages/cli-app/src/lib/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { pluginConfigSchema, TPluginConfig } from "@next-boilerplate/scripts/uti
import { logger } from "@rharkor/logger"
import { TRPCError } from "@trpc/server"

import { env } from "../env"
import { getStores } from "../stores"

import {
Expand All @@ -16,14 +17,19 @@ import {
TPluginStore,
} from "./store"

// Get the current package directory
const cwd = process.cwd()
// eslint-disable-next-line no-process-env
const dir = path.resolve(cwd, env.CLI_REL_PATH ?? "../..")

const configFileName = "config.json"
export const rootPluginsDirectory = path.join(dir, "assets", "plugins")

const loadPlugins = async () => {
const pluginsFilled: TPluginStore[] = []
const stores = await getStores()
for (const store of stores) {
const pluginsDirectory = path.join(store.fullPath, "data", "plugins")
logger.debug(`Loading plugins (${pluginsDirectory})`)
if (!(await fs.exists(pluginsDirectory))) {
throw new TRPCError({
message: `The plugins directory doesn't exist at ${pluginsDirectory}`,
Expand Down Expand Up @@ -58,10 +64,6 @@ const loadPlugins = async () => {

pluginsFilled.sort((a, b) => a.name.localeCompare(b.name))

pluginsFilled.forEach((plugin) => {
logger.debug(`Plugin ${plugin.id} loaded`)
})

setPluginsToStore(pluginsFilled)
return pluginsFilled
}
Expand Down
4 changes: 4 additions & 0 deletions packages/cli-app/src/lib/utils/client-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ export const handleMutationError = <T extends TRPCClientErrorLike<AppRouter>>(
}
return resp
}

export const gitRemoteToName = (remote: string) => {
return remote.replace(/.*\//, "").replace(/\.git$/, "")
}
3 changes: 0 additions & 3 deletions packages/cli/assets/stores/0/config.json

This file was deleted.

Empty file.
11 changes: 0 additions & 11 deletions packages/cli/assets/stores/0/data/plugins/app/empty/config.json

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit f8bb414

Please sign in to comment.