Skip to content

Commit

Permalink
Design improvements (#198)
Browse files Browse the repository at this point in the history
* feat: improve design

* feat: improve design

* feat: update screenshot

* feat: update tests

* fix: routing

* feat: update custom page

* feat: set playwright resolution

* fix: dashboard issue with page router

* feat: use better example user name

* feat: update breadcrumb style

* fixes

* fix e2e

* fix e2e

* add copyable config option

* migrate checkbox to radix

* fix e2e

* remove log & unused checkbox

---------

Co-authored-by: Hugo FOYART <11079152+foyarash@users.noreply.github.com>
  • Loading branch information
baptadn and foyarash authored Mar 25, 2024
1 parent 289539d commit c09c70c
Show file tree
Hide file tree
Showing 45 changed files with 812 additions and 523 deletions.
5 changes: 5 additions & 0 deletions .changeset/fair-jokes-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@premieroctet/next-admin": minor
---

feat: revamp design
2 changes: 1 addition & 1 deletion apps/docs/components/Logo.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const Logo = ({ width }) => (
}
}`}
</style>
<g clip-path="url(#clip0)">
<g clipPath="url(#clip0)">
<rect width="101" height="69" />
<path
d="M46.7321 3C46.3748 2.3812 45.7145 2 45 2C44.2855 2 43.6252 2.3812 43.2679 3L9.49296 61.5C9.13569 62.1188 9.13569 62.8812 9.49296 63.5C9.85022 64.1188 10.5105 64.5 11.225 64.5H78.775C79.4895 64.5 80.1498 64.1188 80.507 63.5C80.8643 62.8812 80.8643 62.1188 80.507 61.5L46.7321 3Z"
Expand Down
1 change: 1 addition & 0 deletions apps/docs/pages/docs/api-docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ This property determines how your data is displayed in the [List View](/docs/glo
| `search` | an array of searchable fields | undefined - all scalar fields are searchable |
| `display` | an array of fields that are displayed in the list | undefined - all scalar fields are displayed |
| `fields` | an object containing the model fields as keys, and customization values, see [below](#fields-property) | undefined |
| `copy` | an array of fields that are copyable into the clipboard | undefined - no field is copyable by default |

> Note that the `search` property is only available for `scalar` fields.
Expand Down
33 changes: 17 additions & 16 deletions apps/docs/pages/docs/i18n.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ Next Admin supports i18n with the `translations` prop of the `NextAdmin` compone

The following keys are accepted:

| Name | Description | Default value |
| -------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------- |
| list.header.add.label | The "Add" button in the list header | Add |
| list.header.search.placeholder | The placeholder used in the search input | Search |
| list.footer.indicator.showing | The "Showing from" text in the list indicator, e.g: <u>Showing from</u> 1 to 10 of 25 | Showing from |
| list.footer.indicator.to | The "to" text in the list indicator, e.g: Showing from 1 <u>to</u> 10 of 25 | to |
| list.footer.indicator.of | The "of" text in the list indicator, e.g: Showing from 1 to 10 <u>of</u> 25 | of |
| list.row.actions.delete.label | The text in the delete button displayed at the end of each row | Delete |
| list.empty.label | The text displayed when there is no row in the list | No \{\{resource\}\} found |
| form.button.save.label | The text displayed in the form submit button | Submit |
| form.button.delete.label | The text displayed in the form delete button | Delete |
| form.widgets.file_upload.label | The text displayed in file upload widget to select a file | Choose a file |
| form.widgets.file_upload.drag_and_drop | The text displayed in file upload widget to indicate a drag & drop is possible | or drag and drop |
| form.widgets.file_upload.delete | The text displayed in file upload widget to delete the current file | Delete |
| actions.label | The text displayed in the dropdown button for the actions list | Action |
| actions.delete.label | The text displayed for the default delete action in the actions dropdown | Delete |
| Name | Description | Default value |
| -------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------- |
| list.header.add.label | The "Add" button in the list header | Add |
| list.header.search.placeholder | The placeholder used in the search input | Search |
| list.footer.indicator.showing | The "Showing from" text in the list indicator, e.g: <u>Showing from</u> 1 to 10 of 25 | Showing from |
| list.footer.indicator.to | The "to" text in the list indicator, e.g: Showing from 1 <u>to</u> 10 of 25 | to |
| list.footer.indicator.of | The "of" text in the list indicator, e.g: Showing from 1 to 10 <u>of</u> 25 | of |
| list.row.actions.delete.label | The text in the delete button displayed at the end of each row | Delete |
| list.empty.label | The text displayed when there is no row in the list | No \{\{resource\}\} found |
| list.empty.caption | The caption displayed when there is no row in the list | Get started by creating a new \{\{resource\}\} |
| form.button.save.label | The text displayed in the form submit button | Submit |
| form.button.delete.label | The text displayed in the form delete button | Delete |
| form.widgets.file_upload.label | The text displayed in file upload widget to select a file | Choose a file |
| form.widgets.file_upload.drag_and_drop | The text displayed in file upload widget to indicate a drag & drop is possible | or drag and drop |
| form.widgets.file_upload.delete | The text displayed in file upload widget to delete the current file | Delete |
| actions.label | The text displayed in the dropdown button for the actions list | Action |
| actions.delete.label | The text displayed for the default delete action in the actions dropdown | Delete |

There is two ways to translate these default keys, provide a function named `getMessages` inside the options or provide `translations` props to `NextAdmin` component.

Expand Down
2 changes: 1 addition & 1 deletion apps/docs/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/docs/public/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 4 additions & 6 deletions apps/example/app/[locale]/admin/[[...nextadmin]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { NextAdmin } from "@premieroctet/next-admin";
import { getPropsFromParams } from "@premieroctet/next-admin/dist/appRouter";
import { getMessages } from "next-intl/server";
import { deleteItem, submitFormAction } from "@/actions/nextadmin";
import Dashboard from "@/components/Dashboard";
import { options } from "@/options";
import { prisma } from "@/prisma";
import schema from "@/prisma/json-schema/json-schema.json";
import { NextAdmin } from "@premieroctet/next-admin";
import { getPropsFromParams } from "@premieroctet/next-admin/dist/appRouter";
import { getMessages } from "next-intl/server";

export default async function AdminPage({
params,
Expand Down Expand Up @@ -33,10 +32,9 @@ export default async function AdminPage({
<NextAdmin
{...props}
locale={params.locale as string}
dashboard={Dashboard}
user={{
data: {
name: "Example User",
name: "John Doe",
},
logoutUrl: "/",
}}
Expand Down
51 changes: 27 additions & 24 deletions apps/example/app/[locale]/admin/custom/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { MainLayout } from "@premieroctet/next-admin";
import { createRandomPost } from "../../../../actions/posts";
import { getMainLayoutProps } from "@premieroctet/next-admin/dist/mainLayout";
import { prisma } from "../../../../prisma";
import { options } from "../../../../options";
import { prisma } from "../../../../prisma";

const CustomPage = async () => {
const mainLayoutProps = getMainLayoutProps({ options, isAppDir: true });
Expand All @@ -11,40 +10,44 @@ const CustomPage = async () => {
const totalPosts = await prisma.post.count();
const totalCategories = await prisma.category.count();

const stats = [
{ name: "Total Users", stat: totalUsers },
{ name: "Total Posts", stat: totalPosts },
{ name: "Total Categories", stat: totalCategories },
];

return (
<MainLayout
{...mainLayoutProps}
user={{
data: {
name: "Example User",
name: "John Doe",
},
logoutUrl: "/",
}}
>
<div>
<div className="p-10">
<h1 className="text-xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight mb-4">
Custom page
Dashboard
</h1>
<div className="mt-2">
<h2 className="text-base font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight mb-2">
Custom queries
</h2>
<p className="text-md">Total Users: {totalUsers}</p>
<p className="text-md">Total Posts: {totalPosts}</p>
<p className="text-md">Total Categories: {totalCategories}</p>
</div>
<div className="mt-2">
<h2 className="text-base font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight mb-2">
Custom actions
</h2>
<form action={createRandomPost} className="mt-2">
<button
type="submit"
className="bg-indigo-500 p-2 text-white hover:bg-indigo-700 inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background"
>
Create random post
</button>
</form>
<div>
<dl className="mt-5 grid grid-cols-1 gap-5 sm:grid-cols-3">
{stats.map((item) => (
<div
key={item.name}
className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6"
>
<dt className="truncate text-sm font-medium text-gray-500">
{item.name}
</dt>
<dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
{item.stat}
</dd>
</div>
))}
</dl>
</div>
</div>
</div>
</MainLayout>
Expand Down
6 changes: 3 additions & 3 deletions apps/example/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import Image from "next/image";
import { Divider, Text, Title } from "@tremor/react";
import Image from "next/image";

const Dashboard = () => {
return (
<div className="flex flex-col gap-5">
<div className="flex justify-start items-center pt-10">
<div className="flex flex-col gap-5 p-10">
<div className="flex justify-start items-center">
<div className="mr-10 w-2/3 gap-2 flex flex-col">
<Title className="text-gray-800">Next Admin</Title>
<Text className="text-gray-600">
Expand Down
13 changes: 5 additions & 8 deletions apps/example/e2e/003-custom_pages.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
import { test, expect } from "@playwright/test";
import { expect, test } from "@playwright/test";

test.describe("Custom pages", () => {
test("Custom page should be visible and clickable", async ({ page }) => {
await page.goto(process.env.BASE_URL!);

await page.click(`a[href$="/custom"]`);

await expect(
page.locator("h1", {
hasText: "Custom page",
hasText: "Dashboard",
})
).toBeVisible();

await page.getByText("Create random post").click();

await page.waitForURL((url) => url.pathname.includes("/post/"));

await expect(page.getByText("Random post created")).toBeVisible();
// await page.getByText("Create random post").click();
// await page.waitForURL((url) => url.pathname.includes("/post/"));
// await expect(page.getByText("Random post created")).toBeVisible();
});
});
7 changes: 4 additions & 3 deletions apps/example/e2e/004-custom_actions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { test, expect } from "@playwright/test";
import { expect, test } from "@playwright/test";

test.describe("User's custom actions", () => {
test("Submit email", async ({ page }) => {
page.on("dialog", (page) => page.accept());
await page.goto(`${process.env.BASE_URL}/user`);
await expect(page.getByTestId("actions-dropdown")).not.toBeVisible();

const checkboxes = page.locator('table input[type="checkbox"]');
const checkboxes = page.locator('table button[role="checkbox"]');
await checkboxes.first().check();
await checkboxes.nth(2).check();

Expand All @@ -24,7 +24,7 @@ test.describe("User's custom actions", () => {
await page.goto(`${process.env.BASE_URL}/user?page=3`);
await expect(page.getByTestId("actions-dropdown")).not.toBeVisible();

const checkboxes = page.locator('table input[type="checkbox"]');
const checkboxes = page.locator('table button[role="checkbox"]');
await checkboxes.nth(4).check();
await checkboxes.last().check();

Expand All @@ -38,6 +38,7 @@ test.describe("User's custom actions", () => {
.getByTestId("actions-dropdown-content")
.getByText("Delete")
.click();

await page.waitForURL((url) => !!url.searchParams.get("message"));
await expect(page.getByText("Deleted successfully")).toBeVisible();
await expect(page.locator("table tbody tr")).toHaveCount(3);
Expand Down
13 changes: 9 additions & 4 deletions apps/example/e2e/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Page, expect } from "@playwright/test";
import { PrismaClient } from "@prisma/client";
import { ModelName } from "@premieroctet/next-admin";
import { PrismaClient } from "@prisma/client";
import { models } from "./001-crud.spec";

export const prisma = new PrismaClient();
Expand Down Expand Up @@ -44,15 +44,20 @@ export const createItem = async (
page: Page
): Promise<string> => {
await page.goto(`${process.env.BASE_URL}/${model}`);
await page.getByRole("button", { name: "Add" }).click();
await page.getByTestId("add-new-button").click();
await page.waitForURL(`${process.env.BASE_URL}/${model}/new`);

await fillForm(model, page, dataTest);
await page.click('button:has-text("Save and continue editing")');

await page.click('button[type="submit"]');
await page.waitForURL((url) => !url.pathname.endsWith("/new"));

const url = new URL(page.url());
const id = url.pathname.split("/").pop();

expect(Number(id)).not.toBeNaN();
expect(page.getByText("Created successfully")).toBeDefined();

return id!;
};

Expand Down Expand Up @@ -161,7 +166,7 @@ const getRows = async (page: Page) => {
export const search = async (page: Page) => {
await page.goto(`${process.env.BASE_URL}/User`);
await page.fill('input[name="search"]', "user0@nextadmin.io");
await page.waitForTimeout(600);
await page.waitForURL((url) => !!url.searchParams.get("search"));
const table = await page.$("table");
const tbody = await table?.$("tbody");
const rows = await tbody?.$$("tr");
Expand Down
5 changes: 3 additions & 2 deletions apps/example/options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import DatePicker from "./components/DatePicker";

export const options: NextAdminOptions = {
basePath: "/admin",
title: "Next Admin Example (App dir)",
title: "⚡️ My Admin",
model: {
User: {
toString: (user) => `${user.name} (${user.email})`,
Expand All @@ -15,6 +15,7 @@ export const options: NextAdminOptions = {
list: {
display: ["id", "name", "email", "posts", "role", "birthDate"],
search: ["name", "email", "role"],
copy: ["email"],
fields: {
role: {
formatter: (role) => {
Expand Down Expand Up @@ -161,7 +162,7 @@ export const options: NextAdminOptions = {
pages: {
"/custom": {
title: "Custom page",
icon: "AdjustmentsHorizontalIcon",
icon: "PresentationChartBarIcon",
},
},
sidebar: {
Expand Down
2 changes: 1 addition & 1 deletion apps/example/pageRouterOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DatePicker from "./components/DatePicker";

export const options: NextAdminOptions = {
basePath: "/pagerouter/admin",
title: "Next Admin Example (Pages dir)",
title: "⚡️ My Admin",
model: {
User: {
toString: (user) => `${user.name} (${user.email})`,
Expand Down
4 changes: 1 addition & 3 deletions apps/example/pages/pagerouter/admin/[[...nextadmin]].tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AdminComponentProps, NextAdmin } from "@premieroctet/next-admin";
import { GetServerSideProps, GetServerSidePropsResult } from "next";
import Dashboard from "../../../components/Dashboard";
import { options } from "../../../pageRouterOptions";
import { prisma } from "../../../prisma";
import schema from "../../../prisma/json-schema/json-schema.json";
Expand All @@ -12,11 +11,10 @@ export default function Admin(props: AdminComponentProps) {
return (
<NextAdmin
{...props}
dashboard={Dashboard}
options={pageOptions}
user={{
data: {
name: "Example User",
name: "John Doe",
},
logoutUrl: "/",
}}
Expand Down
Loading

0 comments on commit c09c70c

Please sign in to comment.