mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-10 07:25:48 +01:00
✨ Add tRPC user query
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
TextInput,
|
||||
ThemeIcon,
|
||||
UnstyledButton,
|
||||
useMantineTheme,
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
IconAlertTriangle,
|
||||
@@ -47,11 +48,12 @@ interface MainLayoutProps {
|
||||
export const MainLayout = ({ children }: MainLayoutProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { attributes } = usePackageAttributesStore();
|
||||
const theme = useMantineTheme();
|
||||
return (
|
||||
<AppShell
|
||||
styles={{
|
||||
root: {
|
||||
background: '#f1f1f1',
|
||||
background: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1],
|
||||
},
|
||||
}}
|
||||
navbar={
|
||||
|
||||
@@ -1,12 +1,98 @@
|
||||
import { Title } from "@mantine/core";
|
||||
import { MainLayout } from "~/components/layout/admin/main-admin.layout";
|
||||
import {
|
||||
ActionIcon,
|
||||
Autocomplete,
|
||||
Avatar,
|
||||
Button,
|
||||
Flex,
|
||||
Group,
|
||||
Pagination,
|
||||
SegmentedControl,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
} from '@mantine/core';
|
||||
import { IconPlus, IconTrash } from '@tabler/icons-react';
|
||||
import Head from 'next/head';
|
||||
import { useState } from 'react';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
const ManageUsersPage = () => {
|
||||
const { isLoading, data, fetchNextPage, fetchPreviousPage } = api.user.getAll.useInfiniteQuery(
|
||||
{
|
||||
limit: 10,
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
}
|
||||
);
|
||||
|
||||
const [activePage, _] = useState(1);
|
||||
|
||||
return (
|
||||
<MainLayout>
|
||||
<Title>Manage users</Title>
|
||||
<Head>
|
||||
<title>Users • Homarr</title>
|
||||
</Head>
|
||||
<Title mb="xl">Manage users</Title>
|
||||
|
||||
<Group position="apart" mb="md">
|
||||
<SegmentedControl
|
||||
data={[
|
||||
{ label: 'Active', value: 'active' },
|
||||
{ label: 'Inactive', value: 'inactive' },
|
||||
]}
|
||||
/>
|
||||
<Flex columnGap={10}>
|
||||
<Autocomplete
|
||||
placeholder="Filter"
|
||||
data={['React', 'Angular', 'Svelte', 'Vue']}
|
||||
variant="filled"
|
||||
/>
|
||||
<Button leftIcon={<IconPlus size="1rem" />} variant="default">
|
||||
Create
|
||||
</Button>
|
||||
</Flex>
|
||||
</Group>
|
||||
|
||||
{data && (
|
||||
<>
|
||||
<Table mb="md" withBorder highlightOnHover>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.pages[0].users.map((user) => (
|
||||
<tr>
|
||||
<td>
|
||||
<Group position="apart">
|
||||
<Group spacing="xs">
|
||||
<Avatar size="sm" />
|
||||
<Text>{user.name}</Text>
|
||||
</Group>
|
||||
<Group>
|
||||
<ActionIcon color="red" variant="light">
|
||||
<IconTrash size="1rem" />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Group>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<Pagination
|
||||
total={data.pages.length}
|
||||
value={activePage}
|
||||
onNextPage={fetchNextPage}
|
||||
onPreviousPage={fetchPreviousPage}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</MainLayout>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageUsersPage;
|
||||
@@ -1,12 +1,16 @@
|
||||
import { Title } from "@mantine/core";
|
||||
import { MainLayout } from "~/components/layout/admin/main-admin.layout";
|
||||
import { Title } from '@mantine/core';
|
||||
import { Head } from 'next/document';
|
||||
import { MainLayout } from '~/components/layout/admin/main-admin.layout';
|
||||
|
||||
const ManageUserInvitesPage = () => {
|
||||
return (
|
||||
<MainLayout>
|
||||
<Head>
|
||||
<title>User invites • Homarr</title>
|
||||
</Head>
|
||||
<Title>Manage user invites</Title>
|
||||
</MainLayout>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default ManageUserInvitesPage;
|
||||
@@ -110,7 +110,7 @@ export const userRouter = createTRPCRouter({
|
||||
},
|
||||
});
|
||||
}),
|
||||
getWithSettings: protectedProcedure.query(async ({ ctx }) => {
|
||||
getWithSettings: protectedProcedure.query(async ({ ctx, input }) => {
|
||||
const user = await ctx.prisma.user.findUnique({
|
||||
where: {
|
||||
id: ctx.session?.user?.id,
|
||||
@@ -133,4 +133,36 @@ export const userRouter = createTRPCRouter({
|
||||
settings: user.settings,
|
||||
};
|
||||
}),
|
||||
|
||||
getAll: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
limit: z.number().min(1).max(100).nullish(),
|
||||
cursor: z.number().nullish(),
|
||||
})
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const limit = input.limit ?? 50;
|
||||
const cursor = input.cursor;
|
||||
const users = await ctx.prisma.user.findMany({
|
||||
take: limit + 1, // get an extra item at the end which we'll use as next cursor
|
||||
cursor: cursor ? { myCursor: cursor } : undefined,
|
||||
});
|
||||
|
||||
let nextCursor: typeof cursor | undefined = undefined;
|
||||
if (users.length > limit) {
|
||||
const nextItem = users.pop();
|
||||
nextCursor = nextItem!.myCursor;
|
||||
}
|
||||
|
||||
return {
|
||||
users: users.map((user) => ({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
emailVerified: user.emailVerified
|
||||
})),
|
||||
nextCursor,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user