Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/polite-games-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@example/erp": patch
"@kivotos/core": minor
"@kivotos/next": minor
---

[[DRIZZ-56] Router Matching])(https://app.plane.so/softnetics/browse/DRIZZ-56/) - using radix3 as a route matching data structure
93 changes: 50 additions & 43 deletions examples/erp/drizzlify/config.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,63 @@
import z from 'zod'

import { wrapNextJs } from '@kivotos/next'

import { categoriesCollection } from './collections/categories'
import { postsCollection } from './collections/posts'
import { usersCollection } from './collections/users'
import { baseConfig, builder } from './helper'

export const serverConfig = baseConfig.toServerConfig({
collections: [usersCollection, postsCollection, categoriesCollection],
endpoints: {
customOne: builder.endpoint(
{
path: '/hello',
query: z.object({
name: z.string().optional(),
}),
method: 'GET',
responses: {
200: z.object({
message: z.string(),
const serverConfig = wrapNextJs(
baseConfig.toServerConfig({
collections: [usersCollection, postsCollection, categoriesCollection],
endpoints: {
customOne: builder.endpoint(
{
path: '/hello',
query: z.object({
name: z.string().optional(),
}),
},
},
({ query }) => {
return {
status: 200 as const,
body: {
message: `Hello ${query.name ?? 'World'}`,
method: 'GET',
responses: {
200: z.object({
message: z.string(),
}),
},
},
({ query }) => {
return {
status: 200 as const,
body: {
message: `Hello ${query.name ?? 'World'}`,
},
}
}
}
),
customTwo: builder.endpoint(
{
path: '/hello2',
query: z.object({
name: z.string().optional(),
}),
method: 'GET',
responses: {
200: z.object({
message: z.string(),
),
customTwo: builder.endpoint(
{
path: '/hello2',
query: z.object({
name: z.string().optional(),
}),
},
},
({ query }) => {
return {
status: 200 as const,
body: {
message: `Hello2 ${query.name ?? 'World'}`,
method: 'GET',
responses: {
200: z.object({
message: z.string(),
}),
},
},
({ query }) => {
return {
status: 200 as const,
body: {
message: `Hello2 ${query.name ?? 'World'}`,
},
}
}
}
),
},
})
),
},
})
)

export type ServerConfig = typeof serverConfig
export { serverConfig }
9 changes: 7 additions & 2 deletions examples/erp/src/app/(admin)/admin/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ import './tailwind.css'

import { RootLayout } from '@kivotos/next'

import { serverConfig } from '~/drizzlify/config'
import { serverConfig } from '../../../../drizzlify/config'
import { serverFunction } from '../_helper/server'

export default function Layout({ children }: { children: React.ReactNode }) {
return <RootLayout serverConfig={serverConfig}>{children}</RootLayout>
return (
<RootLayout serverConfig={serverConfig} serverFunction={serverFunction}>
{children}
</RootLayout>
)
}
4 changes: 1 addition & 3 deletions examples/erp/src/app/(admin)/api/[...segments]/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { createApiResourceRouter } from '@kivotos/next'

import { serverConfig } from '~/drizzlify/config'

const { GET, POST, PUT, PATCH, DELETE } = createApiResourceRouter(serverConfig)
const { GET, POST, PUT, PATCH, DELETE } = serverConfig.resourceRouter
export { DELETE, GET, PATCH, POST, PUT }
11 changes: 7 additions & 4 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ export interface ClientConfig<
}
}

export function getBaseField<const TField extends Field>(name: string, field: TField): FieldClient {
export function getFieldClient<const TField extends Field>(
name: string,
field: TField
): FieldClient {
if (isRelationField(field)) {
if (field.type === 'create' || field.type === 'connectOrCreate') {
if (field._.source === 'relations') {
const sanitizedFields = Object.fromEntries(
Object.entries(field.fields).map(([key, value]) => {
return [key, getBaseField(key, value)]
return [key, getFieldClient(key, value)]
})
)

Expand Down Expand Up @@ -169,7 +172,7 @@ export function getClientCollection<const TCollection extends Collection>(
return R.pipe(collection, R.omit(['_', 'admin']), (collection) => ({
...collection,
fields: R.mapValues(collection.fields as Record<string, Field>, (value, key) =>
getBaseField(key, value)
getFieldClient(key, value)
),
})) as unknown as ToClientCollection<TCollection>
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type {
InferTableNameFromCollection,
} from './collection'
export { ApiDefaultMethod } from './collection'
export type { BaseConfig, ClientConfig, ServerConfig } from './config'
export type { BaseConfig, ClientConfig, MinimalContext, ServerConfig } from './config'
export { defineBaseConfig, getClientCollection, getClientConfig } from './config'
export type {
ApiRoute,
Expand Down
6 changes: 0 additions & 6 deletions packages/next/src/components/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
'use client'

import { useParams } from 'next/navigation'

interface NavProps {}

export function Nav(props: NavProps) {
const params = useParams<{ params: string[] }>()

const slug = params.params[0] as string

return (
<nav>
<span>Logo</span>
Expand Down
151 changes: 151 additions & 0 deletions packages/next/src/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import type { ReactNode } from 'react'

import { createRouter } from 'radix3'

import type { ApiRouter, Collection, MinimalContext, ServerConfig } from '@kivotos/core'

import { createApiResourceRouter } from './resource'
import { AuthLayout } from './views/auth/layout'
import { SignInView } from './views/auth/sign-in'
import { CreateView } from './views/collections/create'
import { CollectionLayout } from './views/collections/layout'
import { ListView } from './views/collections/list'
import { OneView } from './views/collections/one'
import { UpdateView } from './views/collections/update'

export interface NextJsServerConfig<
TFullSchema extends Record<string, unknown> = Record<string, unknown>,
TContext extends MinimalContext<TFullSchema> = MinimalContext<TFullSchema>,
TCollections extends Collection<any, any, any, any, any, any>[] = Collection<
any,
any,
any,
any,
any,
any
>[],
TApiRouter extends ApiRouter<TContext> = ApiRouter<any>,
> extends ServerConfig<TFullSchema, TContext, TCollections, TApiRouter> {
radixRouter: ReturnType<typeof createRouter<{ view: (args: any) => ReactNode }>>
resourceRouter: ReturnType<typeof createApiResourceRouter>
}

export function wrapNextJs<
TFullSchema extends Record<string, unknown> = Record<string, unknown>,
TContext extends MinimalContext<TFullSchema> = MinimalContext<TFullSchema>,
TCollections extends Collection<any, any, any, any, any, any>[] = Collection<
any,
any,
any,
any,
any,
any
>[],
TApiRouter extends ApiRouter<TContext> = ApiRouter<any>,
>(
serverConfig: ServerConfig<TFullSchema, TContext, TCollections, TApiRouter>
): NextJsServerConfig<TFullSchema, TContext, TCollections, TApiRouter> {
const radixRouter = createRouter<{ view: (args: any) => ReactNode }>()

// Collection
radixRouter.insert(`/collections/:slug`, {
view: (args: {
slug: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<CollectionLayout serverConfig={args.serverConfig}>
<ListView {...args} />,
</CollectionLayout>
),
})
radixRouter.insert(`/collections/:slug/:identifier`, {
view: (args: {
slug: string
identifier: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<CollectionLayout serverConfig={args.serverConfig}>
<OneView {...args} />
</CollectionLayout>
),
})
radixRouter.insert(`/collections/:slug/create`, {
view: (args: {
slug: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<CollectionLayout serverConfig={args.serverConfig}>
<CreateView {...args} />
</CollectionLayout>
),
})
radixRouter.insert(`/collections/:slug/update/:identifier`, {
view: (args: {
slug: string
identifier: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<CollectionLayout serverConfig={args.serverConfig}>
<UpdateView {...args} />
</CollectionLayout>
),
})

// Auth
radixRouter.insert(`/auth/sign-in`, {
view: (args: {
slug: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<AuthLayout serverConfig={args.serverConfig}>
<SignInView {...args} />
</AuthLayout>
),
})
radixRouter.insert(`/auth/sign-up`, {
view: (args: {
slug: string
identifier: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<AuthLayout serverConfig={args.serverConfig}>
<OneView {...args} />
</AuthLayout>
),
})
radixRouter.insert(`/auth/forgot-password`, {
view: (args: {
slug: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<AuthLayout serverConfig={args.serverConfig}>
<CreateView {...args} />
</AuthLayout>
),
})
radixRouter.insert(`/auth/reset-password`, {
view: (args: {
slug: string
identifier: string
serverConfig: ServerConfig
searchParams: { [key: string]: string | string[] }
}) => (
<AuthLayout serverConfig={args.serverConfig}>
<UpdateView {...args} />
</AuthLayout>
),
})

return {
...serverConfig,
radixRouter,
resourceRouter: createApiResourceRouter(serverConfig),
}
}
5 changes: 2 additions & 3 deletions packages/next/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
export { AutoField } from './components/auto-field'
export { Nav } from './components/nav'
export { RootCollectionLayout as RootLayout } from './layouts/root-collection'
export { wrapNextJs } from './config'
export { RootLayout } from './layouts/root'
export { ForgotPasswordPage } from './pages/auth/forgot-password'
export { LoginPage } from './pages/auth/login/index'
export { RegisterPage } from './pages/auth/register/index'
export { ResetPasswordConfirmPage } from './pages/auth/reset-password'
export { RootAuthPage } from './pages/auth/root-auth'
export { RootPage } from './pages/root'
export { RootCollectionPage } from './pages/root-collection'
export { UIPlayground } from './pages/ui-playground'
export { createApiResourceRouter } from './resource'
export { handleServerFunction, type ServerFunction } from './server-function'
export { CreateView } from './views/collections/create'
export { ListView } from './views/collections/list'
Expand Down
29 changes: 0 additions & 29 deletions packages/next/src/layouts/root-collection.tsx

This file was deleted.

Loading
Loading