Skip to content

Commit

Permalink
Merge pull request #52 from premieroctet/fix/13-formatting-relationsh…
Browse files Browse the repository at this point in the history
…ip-columns

Add formatting system to relationship column and fix relationshsip one-to-many issue
  • Loading branch information
cregourd authored Oct 17, 2023
2 parents 2342228 + bc60ec6 commit d2444ea
Show file tree
Hide file tree
Showing 8 changed files with 37 additions and 26 deletions.
2 changes: 0 additions & 2 deletions .changeset/chilly-avocados-wink.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/early-suns-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@premieroctet/next-admin": patch
---

Add formatting system to relationship column
2 changes: 1 addition & 1 deletion apps/example/pages/admin/[[...nextadmin]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const options: NextAdminOptions = {
Post: {
toString: (post) => `${post.title}`,
list: {
display: ['id', 'title', 'content', 'published', 'authorId', 'categories'],
display: ['id', 'title', 'content', 'published', 'author', 'categories'],
search: ['title', 'content'],
},
edit: {
Expand Down
19 changes: 10 additions & 9 deletions packages/next-admin/src/components/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React, { ReactNode } from "react";

import { ListDataFieldValue } from "../types";
import { ListDataFieldValue, ListDataItem, ModelName } from "../types";
import Link from "next/link";
import clsx from "clsx";
import { useConfig } from "../context/ConfigContext";

type Props = {
cell: ListDataFieldValue | ReactNode;
cell: ListDataFieldValue | ReactNode
formatter: (cell: any) => ReactNode
};

export default function Cell({ cell }: Props) {
export default function Cell({ cell, formatter }: Props) {
const { basePath } = useConfig()

const isReactNode = (cell: ListDataFieldValue | ReactNode): cell is ReactNode => {
Expand All @@ -26,32 +27,32 @@ export default function Cell({ cell }: Props) {
href={`${basePath}/${cell.value.url}`}
className="hover:underline cursor-pointer text-indigo-700 hover:text-indigo-900 font-semibold"
>
{cell.value.label}
{formatter(cell.value.label)}
</Link>
);
} else if (cell.type === "count") {
return (
<div className="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10">
<p>{cell.value}</p>
<p>{formatter(cell.value)}</p>
</div>
);
} else if (cell.type === "date") {
return (
<div className="whitespace-nowrap max-w-[20ch] overflow-hidden text-ellipsis text-neutral-600">
<p>{cell.value.toString()}</p>
<p>{formatter(cell.value.toString())}</p>
</div>
);
}
} else if (typeof cell === "string") {
return (
<div className="whitespace-nowrap overflow-hidden text-ellipsis text-neutral-600">
<p>{cell.toString()}</p>
<p>{formatter(cell.toString())}</p>
</div>
);
} else if (typeof cell === "number") {
return (
<div className="whitespace-nowrap max-w-[20ch] overflow-hidden text-ellipsis text-neutral-600">
<p>{cell.toString()}</p>
<p>{formatter(cell.toString())}</p>
</div>
);
} else if (typeof cell === "boolean") {
Expand All @@ -63,7 +64,7 @@ export default function Cell({ cell }: Props) {
"bg-neutral-50 text-neutral-600"
)}
>
<p>{cell.toString()}</p>
<p>{formatter(cell.toString())}</p>
</div>
);
}
Expand Down
13 changes: 11 additions & 2 deletions packages/next-admin/src/components/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,18 @@ function List({ resource, data, total, options }: ListProps) {
},
cell: ({ row }) => {
const modelData = row.original;
const cellData = options?.list?.fields?.[property as keyof ListFieldsOptions<typeof resource>]?.formatter?.(modelData) ?? modelData[property];
const cellData = modelData[property as keyof ListFieldsOptions<ModelName>];
const dataFormatter = options?.list?.fields?.[property as keyof ListFieldsOptions<ModelName>]?.formatter || ((cell: any) => {
if (typeof cell === "object") {
return JSON.stringify(cell.id)
} else {
return cell
}
})


return (
<Cell cell={cellData} />
<Cell cell={cellData} formatter={dataFormatter} />
);
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/next-admin/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export type ModelName = Prisma.ModelName;
export type ScalarField<T extends ModelName> = Prisma.TypeMap["model"][T]["payload"]["scalars"];
export type ObjectField<T extends ModelName> = Prisma.TypeMap["model"][T]["payload"]["objects"];

export type Model<M extends ModelName, T extends never | number = never> = ScalarField<M> & {
export type Model<M extends ModelName, T extends object | number = object> = ScalarField<M> & {
[P in keyof ObjectField<M>]:
ObjectField<M>[P] extends { scalars: infer S } ? T extends never ? S : T
: ObjectField<M>[P] extends { scalars: infer S } | null ? T extends never ? S | null : T | null
Expand Down
3 changes: 1 addition & 2 deletions packages/next-admin/src/utils/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Prisma, PrismaClient } from "@prisma/client";
import { findRelationInData, getPrismaModelForResource } from "./server";
import {
ListFieldsOptions,
ModelName,
NextAdminOptions,
Order,
Expand Down Expand Up @@ -66,7 +65,7 @@ export const preparePrismaListRequest = <M extends ModelName>(
const listDisplayedKeys = list.display
select = listDisplayedKeys?.reduce((acc, column) => {
const field = model?.fields.find(({ name }) => name === column);
if (field?.kind === "object") {
if (field?.kind === "object" && field?.isList === true) {
if (!acc._count) acc._count = { select: {} };
acc._count.select = { ...acc._count.select, [column]: true };
} else {
Expand Down
17 changes: 8 additions & 9 deletions packages/next-admin/src/utils/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,25 +200,24 @@ export const findRelationInData = async (
dmmfSchema?: Prisma.DMMF.Field[]
) => {
dmmfSchema?.forEach((dmmfProperty) => {
const dmmfPropertyName = dmmfProperty.name
const dmmfPropertyType = dmmfProperty.type;
const dmmfPropertyKind = dmmfProperty.kind;
const dmmfPropertyRelationFromFields = dmmfProperty.relationFromFields;
const dmmfPropertyRelationToFields = dmmfProperty.relationToFields;

if (dmmfPropertyKind === "object") {
if (dmmfPropertyKind === "object" ) {
if (
dmmfPropertyRelationFromFields!.length > 0 &&
dmmfPropertyRelationToFields!.length > 0
) {
const relationProperty = dmmfPropertyRelationFromFields![0];
data.map((item) => {
if (item[relationProperty]) {
item[relationProperty] = {
if (item[dmmfPropertyName]) {
item[dmmfPropertyName] = {
type: "link",
value: {
label: item[relationProperty],
url: `${dmmfProperty.type as ModelName}/${item[relationProperty]
}`,
label: item[dmmfPropertyName],
url: `${dmmfProperty.type as ModelName}/${item[dmmfPropertyName]["id"]}`,
},
};
} else {
Expand Down Expand Up @@ -265,8 +264,8 @@ export const parseFormData = <M extends ModelName>(
const dmmfPropertyType = dmmfProperty.type;
const dmmfPropertyKind = dmmfProperty.kind;
if (dmmfPropertyKind === "object") {
if(Boolean(formData[dmmfPropertyName])) {
parsedData[dmmfPropertyName] = JSON.parse(formData[dmmfPropertyName] as string) as ModelWithoutRelationships<M>[typeof dmmfPropertyName];
if (Boolean(formData[dmmfPropertyName])) {
parsedData[dmmfPropertyName] = JSON.parse(formData[dmmfPropertyName] as string) as ModelWithoutRelationships<M>[typeof dmmfPropertyName];
} else {
parsedData[dmmfPropertyName] = null as ModelWithoutRelationships<M>[typeof dmmfPropertyName];
}
Expand Down

0 comments on commit d2444ea

Please sign in to comment.