From 02f0de24b50a609f7c6e3a7680eed7cde4405f25 Mon Sep 17 00:00:00 2001 From: Bram Borggreve Date: Sun, 21 Jan 2024 15:23:45 +0000 Subject: [PATCH] feat: generate grid layout for non-admin crud --- api-schema.graphql | 2 +- .../user/feature/src/lib/api-user.resolver.ts | 4 +- libs/sdk/src/generated/graphql-sdk.ts | 26 +-- .../web-feature-generator.spec.ts.snap | 154 ++++++++++++++++-- .../web-feature/web-feature-generator.spec.ts | 4 + ..._modelFileName__-list.feature.tsx.template | 41 +++-- .../__modelFileName__-ui-avatar.tsx.template | 10 ++ ..._modelFileName__-ui-grid-item.tsx.template | 15 ++ .../__modelFileName__-ui-grid.tsx.template | 42 +++++ .../__modelFileName__-ui-item.tsx.template | 33 ++++ .../src/lib/web-crud/generate-web-crud.ts | 4 + .../feature/src/lib/settings-feature.tsx | 2 +- .../feature/src/lib/shell-user.routes.tsx | 2 +- .../web/ui/core/src/lib/ui-header-profile.tsx | 6 +- libs/web/ui/core/src/lib/ui-page-limit.tsx | 34 ++-- libs/web/user/feature/src/index.ts | 4 +- ...user-feature.tsx => admin-user.routes.tsx} | 2 +- .../web/user/feature/src/lib/user-feature.tsx | 19 --- ...ature.tsx => user-user-detail-feature.tsx} | 2 +- .../src/lib/user-user-list-feature.tsx | 35 ++++ .../user/feature/src/lib/user-user.routes.tsx | 20 +++ libs/web/user/ui/src/index.ts | 1 + .../user/ui/src/lib/admin-user-ui-table.tsx | 2 +- .../web/user/ui/src/lib/user-ui-grid-item.tsx | 15 ++ libs/web/user/ui/src/lib/user-ui-grid.tsx | 42 +++++ pnpm-lock.yaml | 2 +- 26 files changed, 441 insertions(+), 82 deletions(-) create mode 100644 libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-avatar.tsx.template create mode 100644 libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid-item.tsx.template create mode 100644 libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid.tsx.template create mode 100644 libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-item.tsx.template rename libs/web/user/feature/src/lib/{admin-user-feature.tsx => admin-user.routes.tsx} (91%) delete mode 100644 libs/web/user/feature/src/lib/user-feature.tsx rename libs/web/user/feature/src/lib/{user-detail-feature.tsx => user-user-detail-feature.tsx} (98%) create mode 100644 libs/web/user/feature/src/lib/user-user-list-feature.tsx create mode 100644 libs/web/user/feature/src/lib/user-user.routes.tsx create mode 100644 libs/web/user/ui/src/lib/user-ui-grid-item.tsx create mode 100644 libs/web/user/ui/src/lib/user-ui-grid.tsx diff --git a/api-schema.graphql b/api-schema.graphql index adfba83..0a515cb 100644 --- a/api-schema.graphql +++ b/api-schema.graphql @@ -158,7 +158,7 @@ type User { id: String! identities: [Identity!] name: String - profileUrl: String + profileUrl: String! role: UserRole status: UserStatus updatedAt: DateTime diff --git a/libs/api/user/feature/src/lib/api-user.resolver.ts b/libs/api/user/feature/src/lib/api-user.resolver.ts index a71e879..24a7ef8 100644 --- a/libs/api/user/feature/src/lib/api-user.resolver.ts +++ b/libs/api/user/feature/src/lib/api-user.resolver.ts @@ -9,9 +9,9 @@ export class ApiUserResolver { return user.avatarUrl?.length ? user.avatarUrl : null } - @ResolveField(() => String, { nullable: true }) + @ResolveField(() => String) profileUrl(@Parent() user: User) { - return ['/profile', user.username].join('/') + return ['/u', user.username].join('/') } @ResolveField(() => [Identity], { nullable: true }) diff --git a/libs/sdk/src/generated/graphql-sdk.ts b/libs/sdk/src/generated/graphql-sdk.ts index 5dc5d1f..9c2c6bf 100644 --- a/libs/sdk/src/generated/graphql-sdk.ts +++ b/libs/sdk/src/generated/graphql-sdk.ts @@ -258,7 +258,7 @@ export type User = { id: Scalars['String']['output'] identities?: Maybe> name?: Maybe - profileUrl?: Maybe + profileUrl: Scalars['String']['output'] role?: Maybe status?: Maybe updatedAt?: Maybe @@ -319,7 +319,7 @@ export type LoginMutation = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -344,7 +344,7 @@ export type RegisterMutation = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -363,7 +363,7 @@ export type MeQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -479,7 +479,7 @@ export type AdminFindManyIdentityQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -654,7 +654,7 @@ export type UserDetailsFragment = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -674,7 +674,7 @@ export type AdminCreateUserMutation = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -703,7 +703,7 @@ export type AdminFindManyUserQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -748,7 +748,7 @@ export type AdminFindOneUserQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -770,7 +770,7 @@ export type AdminUpdateUserMutation = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -793,7 +793,7 @@ export type UserFindManyUserQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -825,7 +825,7 @@ export type UserFindOneUserQuery = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null @@ -846,7 +846,7 @@ export type UserUpdateUserMutation = { developer?: boolean | null id: string name?: string | null - profileUrl?: string | null + profileUrl: string role?: UserRole | null status?: UserStatus | null updatedAt?: Date | null diff --git a/libs/tools/src/generators/web-feature/__snapshots__/web-feature-generator.spec.ts.snap b/libs/tools/src/generators/web-feature/__snapshots__/web-feature-generator.spec.ts.snap index 38305da..df34358 100644 --- a/libs/tools/src/generators/web-feature/__snapshots__/web-feature-generator.spec.ts.snap +++ b/libs/tools/src/generators/web-feature/__snapshots__/web-feature-generator.spec.ts.snap @@ -581,7 +581,7 @@ exports[`web-feature generator should run successfully with crud 20`] = ` "import { Button, Group } from '@mantine/core'; import { UiPageLimit, UiSearchField } from '@proj/web-ui-core'; import { useUserFindManyTest } from '@proj/web-test-data-access'; -import { UserTestUiTable } from '@proj/web-test-ui'; +import { TestUiGrid } from '@proj/web-test-ui'; import { UiBack, UiDebugModal, @@ -620,16 +620,14 @@ export function UserTestListFeature() { {query.isLoading ? ( ) : items?.length ? ( - { - if (!window.confirm('Are you sure?')) return; - return deleteTest(test.id); - }} + void pagination.setPage(page)} + onPageChange={pagination.setPage} + limit={pagination.limit} + setLimit={pagination.setLimit} + setPage={pagination.setPage} /> ) : ( @@ -663,6 +661,10 @@ exports[`web-feature generator should run successfully with crud 22`] = ` "export * from './lib/admin-test-ui-create-form'; export * from './lib/admin-test-ui-table'; export * from './lib/admin-test-ui-update-form'; +export * from './lib/test-ui-avatar'; +export * from './lib/test-ui-grid'; +export * from './lib/test-ui-grid-item'; +export * from './lib/test-ui-item'; export * from './lib/user-test-ui-create-form'; export * from './lib/user-test-ui-table'; export * from './lib/user-test-ui-update-form'; @@ -818,6 +820,138 @@ export function AdminTestUiUpdateForm({ `; exports[`web-feature generator should run successfully with crud 26`] = ` +"import { User } from '@pubkey-stack/sdk'; +import { UiAvatar, UiAvatarProps } from '@pubkey-stack/web-ui-core'; + +export type UserUiAvatarProps = UiAvatarProps & { + test?: User; +}; + +export function UserUiAvatar({ test, ...props }: UserUiAvatarProps) { + return ; +} +" +`; + +exports[`web-feature generator should run successfully with crud 27`] = ` +"import { Paper } from '@mantine/core'; +import type { User } from '@pubkey-stack/sdk'; +import { UiDebugModal, UiGroup } from '@pubkey-ui/core'; +import { UserUiItem } from './test-ui-item'; + +export function UserUiGridItem({ test, to }: { test: User; to?: string }) { + return ( + + + + + + + ); +} +" +`; + +exports[`web-feature generator should run successfully with crud 28`] = ` +"import { Group, Pagination, SimpleGrid } from '@mantine/core'; +import type { User } from '@pubkey-stack/sdk'; +import { gridLimits, UiPageLimit } from '@pubkey-stack/web-ui-core'; +import { UiDebugModal, UiGroup, UiStack } from '@pubkey-ui/core'; +import { DataTableProps } from 'mantine-datatable'; +import { UserUiGridItem } from './test-ui-grid-item'; + +export function UserUiGrid({ + tests = [], + onPageChange, + page, + totalRecords, + limit, + setLimit, + setPage, +}: { + tests: User[]; + page: DataTableProps['page']; + totalRecords: number; + onPageChange: (page: number) => void; + limit: number; + setLimit: (limit: number) => void; + setPage: (page: number) => void; +}) { + const totalPages = totalRecords / limit + 1; + return ( + + + {tests.map((test) => ( + + ))} + + + + + + + + + + ); +} +" +`; + +exports[`web-feature generator should run successfully with crud 29`] = ` +"import { + AvatarProps, + Group, + type GroupProps, + Stack, + Text, +} from '@mantine/core'; +import { User } from '@pubkey-stack/sdk'; +import { UiAnchor, type UiAnchorProps } from '@pubkey-ui/core'; +import { UserUiAvatar } from './test-ui-avatar'; + +export function UserUiItem({ + anchorProps, + avatarProps, + groupProps, + test, + to, +}: { + anchorProps?: UiAnchorProps; + avatarProps?: Omit; + groupProps?: GroupProps; + test?: User; + to?: string | null; +}) { + if (!test) return null; + + return ( + + + + + + {test?.name} + + + + + ); +} +" +`; + +exports[`web-feature generator should run successfully with crud 30`] = ` "import { Button, Group } from '@mantine/core'; import { UserCreateTestInput } from '@proj/sdk'; import { formFieldText, UiForm, UiFormField } from '@pubkey-ui/core'; @@ -850,7 +984,7 @@ export function UserTestUiCreateForm({ " `; -exports[`web-feature generator should run successfully with crud 27`] = ` +exports[`web-feature generator should run successfully with crud 31`] = ` "import { ActionIcon, Anchor, Group, ScrollArea } from '@mantine/core'; import { Test } from '@proj/sdk'; import { IconPencil, IconTrash } from '@tabler/icons-react'; @@ -931,7 +1065,7 @@ export function UserTestUiTable({ " `; -exports[`web-feature generator should run successfully with crud 28`] = ` +exports[`web-feature generator should run successfully with crud 32`] = ` "import { Button, Group } from '@mantine/core'; import { UserUpdateTestInput, Test } from '@proj/sdk'; import { formFieldText, UiForm, UiFormField } from '@pubkey-ui/core'; diff --git a/libs/tools/src/generators/web-feature/web-feature-generator.spec.ts b/libs/tools/src/generators/web-feature/web-feature-generator.spec.ts index 3a4e1b8..bbb2f5c 100644 --- a/libs/tools/src/generators/web-feature/web-feature-generator.spec.ts +++ b/libs/tools/src/generators/web-feature/web-feature-generator.spec.ts @@ -96,6 +96,10 @@ describe('web-feature generator', () => { "libs/web/test/ui/src/lib/admin-test-ui-create-form.tsx", "libs/web/test/ui/src/lib/admin-test-ui-table.tsx", "libs/web/test/ui/src/lib/admin-test-ui-update-form.tsx", + "libs/web/test/ui/src/lib/test-ui-avatar.tsx", + "libs/web/test/ui/src/lib/test-ui-grid-item.tsx", + "libs/web/test/ui/src/lib/test-ui-grid.tsx", + "libs/web/test/ui/src/lib/test-ui-item.tsx", "libs/web/test/ui/src/lib/user-test-ui-create-form.tsx", "libs/web/test/ui/src/lib/user-test-ui-table.tsx", "libs/web/test/ui/src/lib/user-test-ui-update-form.tsx", diff --git a/libs/tools/src/lib/web-crud/files/feature/lib/__actorFileName__-__modelFileName__-list.feature.tsx.template b/libs/tools/src/lib/web-crud/files/feature/lib/__actorFileName__-__modelFileName__-list.feature.tsx.template index e0db7cd..d7ab376 100644 --- a/libs/tools/src/lib/web-crud/files/feature/lib/__actorFileName__-__modelFileName__-list.feature.tsx.template +++ b/libs/tools/src/lib/web-crud/files/feature/lib/__actorFileName__-__modelFileName__-list.feature.tsx.template @@ -1,12 +1,14 @@ import { Button, Group } from '@mantine/core' import { UiPageLimit, UiSearchField } from '@<%= npmScope %>/<%= app.fileName %>-ui-core' import { use<%= actor.className %>FindMany<%= model.className %> } from '@<%= npmScope %>/<%= app.fileName %>-<%= model.fileName %>-data-access' -import { <%= actor.className %><%= model.className %>UiTable } from '@<%= npmScope %>/<%= app.fileName %>-<%= model.fileName %>-ui' +import { <% if(actorAdmin){ %><%= actor.className %><%= model.className %>UiTable<% } else { %><%= model.className %>UiGrid<% } %> } from '@<%= npmScope %>/<%= app.fileName %>-<%= model.fileName %>-ui' import { UiBack, UiDebugModal, UiInfo, UiLoader, UiPage } from '@pubkey-ui/core' import { Link } from 'react-router-dom' export function <%= actor.className %><%= model.className %>ListFeature() { - const { delete<%= model.className %>, items, pagination, query, setSearch } = use<%= actor.className %>FindMany<%= model.className %>() + const { delete<%= model.className %>, items, pagination, query, setSearch } = use<%= actor.className %>FindMany<%= model.className %>({ + limit: <% if(actorAdmin){ %>10<% } else { %>12<% } %>, + }) return ( <%= model.className %>ListFeature() { > - + <% if(actorAdmin){ %><% } %> {query.isLoading ? ( - ) : items?.length ? ( - <<%= actor.className %><%= model.className %>UiTable - delete<%= model.className %>={(<%= model.propertyName %>) => { - if (!window.confirm('Are you sure?')) return - return delete<%= model.className %>(<%= model.propertyName %>.id) - }} - <%= plural.propertyName %>={items} - page={pagination.page} - totalRecords={pagination.total} - recordsPerPage={pagination.limit} - onPageChange={(page) => void pagination.setPage(page)} - /> - ) : ( + ) : items?.length ? <% if(actorAdmin){ %> + (<<%= actor.className %><%= model.className %>UiTable + delete<%= model.className %>={(<%= model.propertyName %>) => { + if (!window.confirm('Are you sure?')) return + return delete<%= model.className %>(<%= model.propertyName %>.id) + }} + <%= plural.propertyName %>={items} + page={pagination.page} + totalRecords={pagination.total} + recordsPerPage={pagination.limit} + onPageChange={(page) => void pagination.setPage(page)} + />)<% } else{ %>(<<%= model.className %>UiGrid + <%= plural.propertyName %>={items} + page={pagination.page} + totalRecords={pagination.total} + onPageChange={pagination.setPage} + limit={pagination.limit} + setLimit={pagination.setLimit} + setPage={pagination.setPage} + />)<% } %> : ( )} diff --git a/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-avatar.tsx.template b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-avatar.tsx.template new file mode 100644 index 0000000..9f984db --- /dev/null +++ b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-avatar.tsx.template @@ -0,0 +1,10 @@ +import { <%= model.className %> } from '@pubkey-stack/sdk' +import { UiAvatar, UiAvatarProps } from '@pubkey-stack/web-ui-core' + +export type <%= model.className %>UiAvatarProps = UiAvatarProps & { + <%= model.propertyName %>?: <%= model.className %> +} + +export function <%= model.className %>UiAvatar({ <%= model.propertyName %>, ...props }: <%= model.className %>UiAvatarProps) { + return ?.<%= label.propertyName %>} {...props} /> +} diff --git a/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid-item.tsx.template b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid-item.tsx.template new file mode 100644 index 0000000..85fb2b5 --- /dev/null +++ b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid-item.tsx.template @@ -0,0 +1,15 @@ +import { Paper } from '@mantine/core' +import type { <%= model.className %> } from '@pubkey-stack/sdk' +import { UiDebugModal, UiGroup } from '@pubkey-ui/core' +import { <%= model.className %>UiItem } from './<%= model.fileName %>-ui-item' + +export function <%= model.className %>UiGridItem({ <%= model.propertyName %>, to }: { <%= model.propertyName %>: <%= model.className %>; to?: string }) { + return ( + + + <<%= model.className %>UiItem <%= model.propertyName %>={<%= model.propertyName %>} to={to} /> + } /> + + + ) +} diff --git a/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid.tsx.template b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid.tsx.template new file mode 100644 index 0000000..80339c5 --- /dev/null +++ b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-grid.tsx.template @@ -0,0 +1,42 @@ +import { Group, Pagination, SimpleGrid } from '@mantine/core' +import type { <%= model.className %> } from '@pubkey-stack/sdk' +import { gridLimits, UiPageLimit } from '@pubkey-stack/web-ui-core' +import { UiDebugModal, UiGroup, UiStack } from '@pubkey-ui/core' +import { DataTableProps } from 'mantine-datatable' +import { <%= model.className %>UiGridItem } from './<%= model.propertyName %>-ui-grid-item' + +export function <%= model.className %>UiGrid({ + <%= plural.propertyName %> = [], + onPageChange, + page, + totalRecords, + limit, + setLimit, + setPage, +}: { + <%= plural.propertyName %>: <%= model.className %>[] + page: DataTableProps['page'] + totalRecords: number + onPageChange: (page: number) => void + limit: number + setLimit: (limit: number) => void + setPage: (page: number) => void +}) { + const totalPages = totalRecords / limit + 1 + return ( + + + {<%= plural.propertyName %>.map((<%= model.propertyName %>) => ( + <<%= model.className %>UiGridItem key={<%= model.propertyName %>.id} to={<%= model.propertyName %>.id} <%= model.propertyName %>={<%= model.propertyName %>} /> + ))} + + + + + } /> + + + + + ) +} diff --git a/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-item.tsx.template b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-item.tsx.template new file mode 100644 index 0000000..36e1c56 --- /dev/null +++ b/libs/tools/src/lib/web-crud/files/ui/lib/__modelFileName__-ui-item.tsx.template @@ -0,0 +1,33 @@ +import { AvatarProps, Group, type GroupProps, Stack, Text } from '@mantine/core' +import { <%= model.className %> } from '@pubkey-stack/sdk' +import { UiAnchor, type UiAnchorProps } from '@pubkey-ui/core' +import { <%= model.className %>UiAvatar } from './<%= model.fileName %>-ui-avatar' + +export function <%= model.className %>UiItem({ + anchorProps, + avatarProps, + groupProps, + <%= model.propertyName %>, + to, +}: { + anchorProps?: UiAnchorProps + avatarProps?: Omit + groupProps?: GroupProps + <%= model.propertyName %>?: <%= model.className %> + to?: string | null +}) { + if (!<%= model.propertyName %>) return null + + return ( + + + <<%= model.className %>UiAvatar <%= model.propertyName %>={<%= model.propertyName %>} {...avatarProps} /> + + + {<%= model.propertyName %>?.<%= label.propertyName %>} + + + + + ) +} diff --git a/libs/tools/src/lib/web-crud/generate-web-crud.ts b/libs/tools/src/lib/web-crud/generate-web-crud.ts index ba2c298..134e29f 100644 --- a/libs/tools/src/lib/web-crud/generate-web-crud.ts +++ b/libs/tools/src/lib/web-crud/generate-web-crud.ts @@ -114,5 +114,9 @@ export function generateWebCrud(tree: Tree, options: NormalizedApiCrudSchema) { `./lib/${vars.actor.fileName}-${vars.model.fileName}-ui-create-form`, `./lib/${vars.actor.fileName}-${vars.model.fileName}-ui-table`, `./lib/${vars.actor.fileName}-${vars.model.fileName}-ui-update-form`, + `./lib/${vars.model.fileName}-ui-avatar`, + `./lib/${vars.model.fileName}-ui-grid`, + `./lib/${vars.model.fileName}-ui-grid-item`, + `./lib/${vars.model.fileName}-ui-item`, ]) } diff --git a/libs/web/settings/feature/src/lib/settings-feature.tsx b/libs/web/settings/feature/src/lib/settings-feature.tsx index 5091fa2..3d3cc35 100644 --- a/libs/web/settings/feature/src/lib/settings-feature.tsx +++ b/libs/web/settings/feature/src/lib/settings-feature.tsx @@ -24,7 +24,7 @@ export default function SettingsFeature() { + } diff --git a/libs/web/shell/feature/src/lib/shell-user.routes.tsx b/libs/web/shell/feature/src/lib/shell-user.routes.tsx index ba10968..cda9bd7 100644 --- a/libs/web/shell/feature/src/lib/shell-user.routes.tsx +++ b/libs/web/shell/feature/src/lib/shell-user.routes.tsx @@ -16,9 +16,9 @@ const links: UiDashboardItem[] = [ const routes: RouteObject[] = [ // Admin Dashboard Routes are added by the web-crud generator { path: '/dashboard', element: }, - { path: '/profile/*', element: }, { path: '/settings/*', element: }, { path: '/solana/*', element: }, + { path: '/u/*', element: }, ] export default function ShellUserRoutes() { diff --git a/libs/web/ui/core/src/lib/ui-header-profile.tsx b/libs/web/ui/core/src/lib/ui-header-profile.tsx index 1dd1707..26e5add 100644 --- a/libs/web/ui/core/src/lib/ui-header-profile.tsx +++ b/libs/web/ui/core/src/lib/ui-header-profile.tsx @@ -12,7 +12,7 @@ export function UiHeaderProfile({ user, logout }: { user?: User | null; logout: const isAdmin = user?.role === UserRole.Admin const isDeveloper = user?.developer ?? false - return ( + return user ? ( - }> + }> View profile }> @@ -71,5 +71,5 @@ export function UiHeaderProfile({ user, logout }: { user?: User | null; logout: - ) + ) : null } diff --git a/libs/web/ui/core/src/lib/ui-page-limit.tsx b/libs/web/ui/core/src/lib/ui-page-limit.tsx index 2845606..77b0482 100644 --- a/libs/web/ui/core/src/lib/ui-page-limit.tsx +++ b/libs/web/ui/core/src/lib/ui-page-limit.tsx @@ -1,16 +1,7 @@ import { Select, SelectProps } from '@mantine/core' export function UiPageLimit({ - data = [ - { value: '5', label: '5' }, - { value: '10', label: '10' }, - { value: '20', label: '20' }, - { value: '50', label: '50' }, - { value: '100', label: '100' }, - { value: '200', label: '200' }, - { value: '500', label: '500' }, - { value: '1000', label: '1000' }, - ], + data = tableLimits, setPage, setLimit, limit, @@ -32,3 +23,26 @@ export function UiPageLimit({ /> ) } + +export const tableLimits = [ + { value: '5', label: '5' }, + { value: '10', label: '10' }, + { value: '20', label: '20' }, + { value: '50', label: '50' }, + { value: '100', label: '100' }, + { value: '200', label: '200' }, + { value: '500', label: '500' }, + { value: '1000', label: '1000' }, +] +export const gridLimits = [ + { value: '3', label: '3' }, + { value: '6', label: '6' }, + { value: '9', label: '9' }, + { value: '12', label: '12' }, + { value: '15', label: '15' }, + { value: '18', label: '18' }, + { value: '21', label: '21' }, + { value: '24', label: '24' }, + { value: '99', label: '99' }, + { value: '999', label: '999' }, +] diff --git a/libs/web/user/feature/src/index.ts b/libs/web/user/feature/src/index.ts index 9118322..1680619 100644 --- a/libs/web/user/feature/src/index.ts +++ b/libs/web/user/feature/src/index.ts @@ -1,4 +1,4 @@ import { lazy } from 'react' -export const AdminUserFeature = lazy(() => import('./lib/admin-user-feature')) -export const UserFeature = lazy(() => import('./lib/user-feature')) +export const AdminUserFeature = lazy(() => import('./lib/admin-user.routes')) +export const UserFeature = lazy(() => import('./lib/user-user.routes')) diff --git a/libs/web/user/feature/src/lib/admin-user-feature.tsx b/libs/web/user/feature/src/lib/admin-user.routes.tsx similarity index 91% rename from libs/web/user/feature/src/lib/admin-user-feature.tsx rename to libs/web/user/feature/src/lib/admin-user.routes.tsx index e2703b3..34bcc04 100644 --- a/libs/web/user/feature/src/lib/admin-user-feature.tsx +++ b/libs/web/user/feature/src/lib/admin-user.routes.tsx @@ -3,7 +3,7 @@ import { AdminUserCreateFeature } from './admin-user-create-feature' import { AdminUserDetailFeature } from './admin-user-detail-feature' import { AdminUserListFeature } from './admin-user-list-feature' -export default function AdminUserFeature() { +export default function AdminUserRoutes() { return useRoutes([ { path: '', element: }, { diff --git a/libs/web/user/feature/src/lib/user-feature.tsx b/libs/web/user/feature/src/lib/user-feature.tsx deleted file mode 100644 index 9ede4f5..0000000 --- a/libs/web/user/feature/src/lib/user-feature.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useAuth } from '@pubkey-stack/web-auth-data-access' -import { UiWarning } from '@pubkey-ui/core' -import { Navigate, Route, Routes } from 'react-router-dom' -import { UserDetailFeature } from './user-detail-feature' - -export default function UserFeature() { - const { user } = useAuth() - - if (!user?.username) { - return - } - - return ( - - } /> - } /> - - ) -} diff --git a/libs/web/user/feature/src/lib/user-detail-feature.tsx b/libs/web/user/feature/src/lib/user-user-detail-feature.tsx similarity index 98% rename from libs/web/user/feature/src/lib/user-detail-feature.tsx rename to libs/web/user/feature/src/lib/user-user-detail-feature.tsx index d72c742..c77f3bd 100644 --- a/libs/web/user/feature/src/lib/user-detail-feature.tsx +++ b/libs/web/user/feature/src/lib/user-user-detail-feature.tsx @@ -10,7 +10,7 @@ import { UiCard, UiContainer, UiDebugModal, UiGroup, UiLoader, UiStack, UiWarnin import { IconMoodSmile } from '@tabler/icons-react' import { Link, useParams } from 'react-router-dom' -export function UserDetailFeature() { +export function UserUserDetailFeature() { const { user: authUser } = useAuth() const { username } = useParams<{ username: string }>() as { username: string } const { user, query } = useUserFineOneUser({ username }) diff --git a/libs/web/user/feature/src/lib/user-user-list-feature.tsx b/libs/web/user/feature/src/lib/user-user-list-feature.tsx new file mode 100644 index 0000000..f794643 --- /dev/null +++ b/libs/web/user/feature/src/lib/user-user-list-feature.tsx @@ -0,0 +1,35 @@ +import { Group } from '@mantine/core' +import { UiSearchField } from '@pubkey-stack/web-ui-core' +import { useUserFindManyUser } from '@pubkey-stack/web-user-data-access' +import { UserUiGrid } from '@pubkey-stack/web-user-ui' +import { UiInfo, UiLoader, UiPage } from '@pubkey-ui/core' +import { IconUserSearch } from '@tabler/icons-react' + +export function UserUserListFeature() { + const { items, pagination, query, setSearch } = useUserFindManyUser({ + limit: 12, + }) + + return ( + + + } placeholder="Search user" setSearch={setSearch} /> + + {query.isLoading ? ( + + ) : items?.length ? ( + + ) : ( + + )} + + ) +} diff --git a/libs/web/user/feature/src/lib/user-user.routes.tsx b/libs/web/user/feature/src/lib/user-user.routes.tsx new file mode 100644 index 0000000..aa9f347 --- /dev/null +++ b/libs/web/user/feature/src/lib/user-user.routes.tsx @@ -0,0 +1,20 @@ +import { useAuth } from '@pubkey-stack/web-auth-data-access' +import { UiWarning } from '@pubkey-ui/core' +import { Route, Routes } from 'react-router-dom' +import { UserUserDetailFeature } from './user-user-detail-feature' +import { UserUserListFeature } from './user-user-list-feature' + +export default function UserUserRoutes() { + const { user } = useAuth() + + if (!user?.username) { + return + } + + return ( + + } /> + } /> + + ) +} diff --git a/libs/web/user/ui/src/index.ts b/libs/web/user/ui/src/index.ts index 804056d..6b0a251 100644 --- a/libs/web/user/ui/src/index.ts +++ b/libs/web/user/ui/src/index.ts @@ -9,4 +9,5 @@ export * from './lib/user-ui-role-badge' export * from './lib/user-ui-search' export * from './lib/user-ui-status-badge' export * from './lib/user-ui-item' +export * from './lib/user-ui-grid' export * from './lib/user-ui-update-form' diff --git a/libs/web/user/ui/src/lib/admin-user-ui-table.tsx b/libs/web/user/ui/src/lib/admin-user-ui-table.tsx index 65eb107..0f0502d 100644 --- a/libs/web/user/ui/src/lib/admin-user-ui-table.tsx +++ b/libs/web/user/ui/src/lib/admin-user-ui-table.tsx @@ -69,7 +69,7 @@ export function AdminUserUiTable({ textAlign: 'right', render: (item) => ( - + + + + + + + ) +} diff --git a/libs/web/user/ui/src/lib/user-ui-grid.tsx b/libs/web/user/ui/src/lib/user-ui-grid.tsx new file mode 100644 index 0000000..85b9875 --- /dev/null +++ b/libs/web/user/ui/src/lib/user-ui-grid.tsx @@ -0,0 +1,42 @@ +import { Group, Pagination, SimpleGrid } from '@mantine/core' +import type { User } from '@pubkey-stack/sdk' +import { gridLimits, UiPageLimit } from '@pubkey-stack/web-ui-core' +import { UiDebugModal, UiGroup, UiStack } from '@pubkey-ui/core' +import { DataTableProps } from 'mantine-datatable' +import { UserUiGridItem } from './user-ui-grid-item' + +export function UserUiGrid({ + users = [], + onPageChange, + page, + totalRecords, + limit, + setLimit, + setPage, +}: { + users: User[] + page: DataTableProps['page'] + totalRecords: number + onPageChange: (page: number) => void + limit: number + setLimit: (limit: number) => void + setPage: (page: number) => void +}) { + const totalPages = totalRecords / limit + 1 + return ( + + + {users.map((user) => ( + + ))} + + + + + + + + + + ) +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2504827..cd5c79f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,7 +195,7 @@ dependencies: specifier: 6.21.1 version: 6.21.1(react-dom@18.2.0)(react@18.2.0) reflect-metadata: - specifier: ^0.1.12 + specifier: ^0.1.13 version: 0.1.14 rxjs: specifier: ^7.8.1