Skip to content
Open
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
1 change: 1 addition & 0 deletions apps/devfractal-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"react-dom": "18.2.0",
"react-error-boundary": "4.0.11",
"react-router-dom": "6.16.0",
"react-virtuoso": "4.6.1",
"sort-by": "1.2.0",
"tailwind-merge": "1.14.0",
"tailwindcss-animate": "1.0.7",
Expand Down
4 changes: 2 additions & 2 deletions apps/devfractal-demo/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'

import { queryClient } from '@/queryClient'

import { DataTable } from './server-side/DataTable'
import { DataGridApp } from './server-side/DataGridApp'
import './global.css'

if (process.env.NODE_ENV === 'development') {
Expand All @@ -14,7 +14,7 @@ if (process.env.NODE_ENV === 'development') {
const router = createBrowserRouter([
{
path: '*',
element: <DataTable />,
element: <DataGridApp />,
},
])

Expand Down
64 changes: 14 additions & 50 deletions apps/devfractal-demo/src/mocks/data-table/dataHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,26 @@
import { fromSearchParams, isArray, isNotNullish, toInt } from 'devfractal'
import { fromSearchParams } from 'devfractal'
import { rest } from 'msw'

import {
getSearchedProducts,
getSlicedProducts,
getSortedProducts,
} from './operations'
import { getDataFromTable } from './database'

export const dataHandlers = [
rest.get('/api/data/products', (req, res, ctx) => {
const queryParams = fromSearchParams(req.url.searchParams)

if (
isNotNullish(queryParams.searchBy) &&
isNotNullish(queryParams.search)
) {
return res(
ctx.json(
getSearchedProducts(
toInt(queryParams.page),
toInt(queryParams.limit),
isArray(queryParams.column)
? queryParams.column
: [queryParams.column],
queryParams.searchBy,
queryParams.search,
queryParams.sortBy,
queryParams.order,
),
),
)
}

if (isNotNullish(queryParams.sortBy) && isNotNullish(queryParams.order)) {
return res(
ctx.json(
getSortedProducts(
toInt(queryParams.page),
toInt(queryParams.limit),
isArray(queryParams.column)
? queryParams.column
: [queryParams.column],
queryParams.sortBy,
queryParams.order,
),
),
)
}
const { show, column, page, limit, sortBy, order, searchBy, search } =
queryParams

return res(
ctx.json(
getSlicedProducts(
toInt(queryParams.page),
toInt(queryParams.limit),
isArray(queryParams.column)
? queryParams.column
: [queryParams.column],
),
getDataFromTable({
show,
column,
page,
limit,
sortBy,
order,
searchBy,
search,
}),
),
)
}),
Expand Down
232 changes: 232 additions & 0 deletions apps/devfractal-demo/src/mocks/data-table/database.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import {
any,
chain,
filter,
isArray,
isDefined,
omit$,
paged,
pipe,
} from 'devfractal'

import { data, type Product } from '@/server-side'

import { orderBy, picked } from './utils'

export type ProductsResponse = {
products: Array<any>
currentPage: number
totalPages: number
totalItems: number
columns: string[]
}

export function getDataFromTable(params: {
show: 'all' | 'paged'
column: string | string[]
page?: number
limit?: number
sortBy?: string
order?: 'asc' | 'desc'
searchBy?: keyof Product | 'all'
search?: string
}): ProductsResponse {
const selected = isArray(params.column) ? params.column : [params.column]

if (
params.show === 'all' &&
isDefined(params.sortBy) &&
isDefined(params.order) &&
isDefined(params.searchBy) &&
isDefined(params.search)
) {
const searchKey = params.searchBy
const searchStr = params.search

return {
products: chain(
data,
orderBy(params.sortBy as keyof Product, params.order),
filter(product => hasSearchString(product, searchKey, searchStr)),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: 0,
totalItems: data.length,
totalPages: 0,
}
}

if (
params.show === 'all' &&
isDefined(params.sortBy) &&
isDefined(params.order)
) {
return {
products: chain(
data,
orderBy(params.sortBy as keyof Product, params.order),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: 0,
totalItems: data.length,
totalPages: 0,
}
}

if (
params.show === 'all' &&
isDefined(params.searchBy) &&
isDefined(params.search)
) {
const searchKey = params.searchBy
const searchStr = params.search

return {
products: chain(
data,
filter(product => hasSearchString(product, searchKey, searchStr)),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: 0,
totalItems: data.length,
totalPages: 0,
}
}

if (params.show === 'all') {
return {
products: chain(data, picked([...selected] as (keyof Product)[])),
columns: selected,
currentPage: 0,
totalItems: data.length,
totalPages: 0,
}
}

return getPaginatedProducts({ ...omit$(params, ['show']) })
}

function getPaginatedProducts(params: {
column: string | string[]
page?: number
limit?: number
sortBy?: string
order?: 'asc' | 'desc'
searchBy?: keyof Product | 'all'
search?: string
}): ProductsResponse {
const selected = isArray(params.column) ? params.column : [params.column]

if (
isDefined(params.page) &&
isDefined(params.limit) &&
isDefined(params.sortBy) &&
isDefined(params.order) &&
isDefined(params.searchBy) &&
isDefined(params.search)
) {
const searchKey = params.searchBy
const searchStr = params.search

return {
products: chain(
data,
paged(params.page, params.limit),
orderBy(params.sortBy as keyof Product, params.order),
filter(product => hasSearchString(product, searchKey, searchStr)),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: params.page,
totalItems: data.length,
totalPages: Math.ceil(data.length / params.limit),
}
}

if (
isDefined(params.page) &&
isDefined(params.limit) &&
isDefined(params.sortBy) &&
isDefined(params.order)
) {
return {
products: chain(
data,
paged(params.page, params.limit),
orderBy(params.sortBy as keyof Product, params.order),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: params.page,
totalItems: data.length,
totalPages: Math.ceil(data.length / params.limit),
}
}

if (
isDefined(params.page) &&
isDefined(params.limit) &&
isDefined(params.searchBy) &&
isDefined(params.search)
) {
const searchKey = params.searchBy
const searchStr = params.search

return {
products: chain(
data,
paged(params.page, params.limit),
filter(product => hasSearchString(product, searchKey, searchStr)),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: params.page,
totalItems: data.length,
totalPages: Math.ceil(data.length / params.limit),
}
}

if (isDefined(params.page) && isDefined(params.limit)) {
return {
products: chain(
data,
paged(params.page, params.limit),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: params.page,
totalItems: data.length,
totalPages: Math.ceil(data.length / params.limit),
}
}

return {
products: chain(
data,
paged(1, 10),
picked([...selected] as (keyof Product)[]),
),
columns: selected,
currentPage: 1,
totalItems: data.length,
totalPages: Math.ceil(data.length / 10),
}
}

function hasSearchString<T extends Record<string, any>, K extends keyof T>(
obj: T,
key: K | 'all',
text: string,
): boolean {
if (key !== 'all') {
return obj[key].toString().toLowerCase().includes(text.toLowerCase())
}

return pipe(
Object.values(obj),
any(value => value.toString().toLowerCase().includes(text.toLowerCase())),
)
}
Loading