first commit

This commit is contained in:
2024-01-15 23:13:55 +01:00
commit 39705a7b43
286 changed files with 47391 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
import { ApiContext, DEFAULT_API_CONTEXT } from '@/common/providers/api-context'
import {
{{ pascalCase name }}ApiResult,
{{ pascalCase name }}CreateApiParams,
{{ pascalCase name }}DeleteApiParams,
{{ pascalCase name }}GetApiParams,
{{ pascalCase name }}Id,
{{ pascalCase name }}ListApiParams,
{{ pascalCase name }}PaginatedApiResult,
{{ pascalCase name }}UpdateApiParams,
} from './{{ kebabCase name }}.types'
export const {{ camelCase name }}ApiProto = (
baseUrl: string = process.env.NEXT_PUBLIC_API_ENDPOINT || '/api',
defaultApiContext = DEFAULT_API_CONTEXT
) => {
const endpointUrl = `${baseUrl}/{{ kebabCase name }}`
type UrlParams = { resourceId?: {{ pascalCase name}}Id }
const endpoint = (
urlParams: UrlParams,
queryParams: Record<string, string>
) => {
const queryParamString = new URLSearchParams(queryParams).toString()
const resourceIdParam =
urlParams.resourceId === undefined ? '' : `/${urlParams.resourceId}`
// TODO: Customize the endpoint url generation here
return `${endpointUrl}${resourceIdParam}?${queryParamString}`
}
return {
async list(
this: ApiContext,
{ page, size, ...otherQueryParams }: {{ pascalCase name }}ListApiParams
): Promise<{{ pascalCase name }}PaginatedApiResult> {
const urlParams: UrlParams = {}
const queryParams = {
// TODO: Map the pagination params as required by the API
page: `${page}`,
size: `${size}`,
// limit: `${size}`,
// offset: `${Math.max((page - 1) * size, 0)}`,
...otherQueryParams,
}
const url = endpoint(urlParams, queryParams)
console.debug(
`Listing {{ pascalCase name }} with page: ${page}, size: ${size}`,
`on url: ${url}`
)
const response = await this.client.get(url)
// TODO: Add code handle the response if needed
return response.data as {{ pascalCase name }}PaginatedApiResult
},
async delete(
this: ApiContext,
{ resourceId, ...queryParams }: {{ pascalCase name }}DeleteApiParams
): Promise<boolean> {
const urlParams: UrlParams = { resourceId }
const url = endpoint(urlParams, queryParams)
console.debug(
`Deleting {{ pascalCase name }} with id:`,
resourceId,
`on url: ${url}`
)
const response = await this.client.delete(url)
// TODO: Add code handle the response if needed
return response.status >= 200 && response.status < 300
},
async create(
this: ApiContext,
{ newResource, ...queryParams }: {{ pascalCase name }}CreateApiParams
): Promise<{{ pascalCase name }}Id> {
const urlParams: UrlParams = {}
const url = endpoint(urlParams, queryParams)
console.debug(
`Creating {{ pascalCase name }} resource:`,
newResource,
`on url: ${url}`
)
const response = await this.client.post(url, newResource)
// TODO: Add code handle the response if needed
// TODO: Adapt code to handle the receiving of the resourceId (if any)
const locationHeader = response.headers.location as
| string
| undefined
if (locationHeader) {
const segments = new URL(locationHeader).pathname.split('/')
const lastIdx = segments.length - 1
const resourceId =
segments[lastIdx] || segments[Math.max(lastIdx - 1, 0)]
if (!resourceId)
console.warn(new Error('Invalid location header received'))
return resourceId as {{ pascalCase name }}Id
}
console.warn(new Error('No location header received'))
return '' as {{ pascalCase name }}Id
},
async update(
this: ApiContext,
{
updatedResource,
// resourceId,
...queryParams
}: {{ pascalCase name }}UpdateApiParams
): Promise<boolean> {
const urlParams: UrlParams = {
// resourceId
}
const url = endpoint(urlParams, queryParams)
console.debug(
`updating {{ pascalCase name }} resource:`,
updatedResource,
`on url: ${url}`
)
const response = await this.client.put(url, updatedResource)
// TODO: Add code handle the response if needed
return response.status >= 200 && response.status < 300
},
async get(
this: ApiContext,
{ resourceId, ...queryParams }: {{ pascalCase name }}GetApiParams
): Promise<{{ pascalCase name }}ApiResult> {
const urlParams: UrlParams = {
resourceId,
}
const url = endpoint(urlParams, queryParams)
console.debug(
`Getting {{ pascalCase name }} with id:`,
resourceId,
`on url: ${url}`
)
const response = await this.client.get(url)
// TODO: Add code handle the response if needed
return response.data as {{ pascalCase name }}ApiResult
},
...defaultApiContext,
}
}
export const {{ camelCase name }}Api = {{ camelCase name }}ApiProto()

View File

@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query'
import { Pagination } from '@/hookey'
import { useApiContext } from '@/common/providers/api-context'
import { {{ camelCase name }}Api } from './{{ kebabCase name }}.api'
import { {{ pascalCase name }}GetApiParams } from './{{ kebabCase name }}.types'
export const use{{ pascalCase name }}s = Pagination.makePaginationHook({
cacheKey: '{{ kebabCase name }}-api-list',
clientFn: {{ camelCase name }}Api.list,
useApiContext: useApiContext,
// TODO: Connect getCount and getPageData with the list response data
getCount: (data) => data.count,
getPageData: (data) => data.results,
})
export const use{{ pascalCase name }} = (params: {{ pascalCase name }}GetApiParams) => {
return useQuery(
['{{ kebabCase name }}-api-get', params] as [string, typeof params],
({ queryKey: [_key, params] }) => {{ camelCase name }}Api.get(params)
)
}

View File

@@ -0,0 +1,46 @@
import type { Pagination } from '@/hookey'
export type {{ pascalCase name }} = {
{{ camelCase name }}Id: {{ pascalCase name }}Id
}
// TODO: Set the id type
export type {{ pascalCase name }}Id = string | number
export type {{ pascalCase name }}ApiResult = {
// TODO: Replace with actual get api result
results: {{ pascalCase name }}
}
export type {{ pascalCase name }}PaginatedApiResult = {
// TODO: Replace with actual list api result
results: {{ pascalCase name }}[]
count: number
}
export type {{ pascalCase name }}ListApiParams = Pagination.UsePaginatedQueryParams<{
// TODO: Add other params here
}>
export type {{ pascalCase name }}GetApiParams = {
resourceId: {{ pascalCase name }}Id
// TODO: Add other params here
}
export type {{ pascalCase name }}CreateApiParams = {
newResource: Omit<{{ pascalCase name }}, '{{ camelCase name }}Id'>
// TODO: Add other params here
}
export type {{ pascalCase name }}UpdateApiParams = {
updatedResource: {{ pascalCase name }}
// TODO: Switch params if the api requires an id in the url for updates
// updatedResource: Omit<{{ pascalCase name }}, '{{ camelCase name }}Id'>
// resourceId: {{ pascalCase name }}Id
// TODO: Add other params here
}
export type {{ pascalCase name }}DeleteApiParams = {
resourceId: {{ pascalCase name }}Id
// TODO: Add other params here
}

View File

@@ -0,0 +1,3 @@
export * from './{{ kebabCase name }}.api'
export * from './{{ kebabCase name }}.hooks'
export * from './{{ kebabCase name }}.types'

View File

@@ -0,0 +1,8 @@
import React from 'react'
import styles from './{{ kebabCase name }}.module.css'
export type {{pascalCase name}}Props = {}
export function {{pascalCase name}}(props: {{pascalCase name}}Props) {
return <div data-testid="{{ kebabCase name }}" className={styles.container}></div>
}

View File

@@ -0,0 +1,3 @@
.container {
}

View File

@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'
import { expect } from '@storybook/jest'
import { within } from '@storybook/testing-library'
import { {{ pascalCase name }} } from './{{ kebabCase name }}.component'
const meta: Meta<typeof {{ pascalCase name }}> = {
title: '{{ pascalCase name }}',
component: {{ pascalCase name }},
argTypes: {},
}
export default meta
type Story = StoryObj<typeof {{ pascalCase name }}>
export const Default: Story = {
args: {},
async play({ canvasElement }) {
const canvas = within(canvasElement)
const container = canvas.getByTestId('{{ kebabCase name }}')
expect(container).toBeTruthy()
},
}

View File

@@ -0,0 +1,3 @@
'use client'
export * from './{{kebabCase name}}.component'

View File

@@ -0,0 +1,8 @@
import React, { PropsWithChildren } from 'react'
import styles from './{{ kebabCase name }}.module.css'
export type {{pascalCase name}}LayoutProps = PropsWithChildren<{}>
export function {{pascalCase name}}Layout(props: {{pascalCase name}}LayoutProps) {
return <div data-testid="{{ kebabCase name }}-layout" className={styles.container}>{props.children}</div>
}

View File

@@ -0,0 +1,3 @@
.container {
}

View File

@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'
import { expect } from '@storybook/jest'
import { within } from '@storybook/testing-library'
import { {{ pascalCase name }}Layout } from './{{ kebabCase name }}.layout'
const meta: Meta<typeof {{ pascalCase name }}Layout> = {
title: '{{ pascalCase name }}Layout',
component: {{ pascalCase name }}Layout,
argTypes: {},
}
export default meta
type Story = StoryObj<typeof {{ pascalCase name }}Layout>
export const Default: Story = {
args: {},
async play({ canvasElement }) {
const canvas = within(canvasElement)
const container = canvas.getByTestId('{{ kebabCase name }}-layout')
expect(container).toBeTruthy()
},
}

View File

@@ -0,0 +1 @@
export * from './{{kebabCase name}}.layout'

View File

@@ -0,0 +1,3 @@
.container {
}

View File

@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'
import { expect } from '@storybook/jest'
import { within } from '@storybook/testing-library'
import { {{ pascalCase name }}View } from './{{ kebabCase name }}.view'
const meta: Meta<typeof {{ pascalCase name }}View> = {
title: '{{ pascalCase name }}View',
component: {{ pascalCase name }}View,
argTypes: {},
}
export default meta
type Story = StoryObj<typeof {{ pascalCase name }}View>
export const Default: Story = {
args: {},
async play({ canvasElement }) {
const canvas = within(canvasElement)
const container = canvas.getByTestId('{{ kebabCase name }}-view')
expect(container).toBeTruthy()
},
}

View File

@@ -0,0 +1,13 @@
import React from 'react'
import styles from './{{ kebabCase name }}.module.css'
type {{pascalCase name}}ViewProps = {
// query parameters
searchParams: { [key: string]: string | string[] | undefined }
// url parameters
params: { [key: string]: string | undefined }
}
export function {{pascalCase name}}View(props: {{pascalCase name}}ViewProps) {
return <div data-testid="{{ kebabCase name }}-view" className={styles.container}></div>
}

View File

@@ -0,0 +1 @@
export * from './{{kebabCase name}}.view'

View File

@@ -0,0 +1,3 @@
.container {
}

View File

@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react'
import { expect } from '@storybook/jest'
import { within } from '@storybook/testing-library'
import { {{ pascalCase name }}Widget } from './{{ kebabCase name }}.widget'
const meta: Meta<typeof {{ pascalCase name }}Widget> = {
title: '{{ pascalCase name }}Widget',
component: {{ pascalCase name }}Widget,
argTypes: {},
}
export default meta
type Story = StoryObj<typeof {{ pascalCase name }}Widget>
export const Default: Story = {
args: {},
async play({ canvasElement }) {
const canvas = within(canvasElement)
const container = canvas.getByTestId('{{ kebabCase name }}-widget')
expect(container).toBeTruthy()
},
}

View File

@@ -0,0 +1,8 @@
import React from 'react'
import styles from './{{ kebabCase name }}.module.css'
export type {{pascalCase name}}WidgetProps = {}
export function {{pascalCase name}}Widget(props: {{pascalCase name}}WidgetProps) {
return <div data-testid="{{ kebabCase name }}-widget" className={styles.container}></div>
}

View File

@@ -0,0 +1,3 @@
'use client'
export * from './{{kebabCase name}}.widget'