added user overview

This commit is contained in:
lnn0q 2025-06-01 20:11:57 +03:00
parent b5738c610d
commit 2ac082a4e4
5 changed files with 107 additions and 14 deletions

View file

@ -7,6 +7,16 @@ export const sessionFragment = gql`
}
`;
export const userFragment = gql`
fragment User on User {
id
username
description
createdAt
updatedAt
}
`;
export const bookFragment = gql`
fragment Book on Book {
id

View file

@ -1,7 +1,14 @@
import { ApolloError, ApolloQueryResult, gql, OperationVariables, useQuery } from '@apollo/client';
import { UserOneQuery } from '@generated/types';
import { userFragment } from '../fragments';
export function useUserOneQuery({ id, skip }: { id?: string; skip?: boolean }): {
export function useUserOneQuery({
input,
skip
}: {
readonly input: { readonly id: string } | { readonly username: string };
readonly skip?: boolean;
}): {
data: UserOneQuery | undefined;
loading: boolean;
error: ApolloError | undefined;
@ -10,7 +17,7 @@ export function useUserOneQuery({ id, skip }: { id?: string; skip?: boolean }):
) => Promise<ApolloQueryResult<UserOneQuery>>;
} {
const { data, loading, error, refetch } = useQuery<UserOneQuery>(query, {
variables: { id },
variables: { input },
skip
});
return {
@ -22,12 +29,10 @@ export function useUserOneQuery({ id, skip }: { id?: string; skip?: boolean }):
}
const query = gql`
query UserOne($id: ID!) {
userOne(id: $id) {
id
username
createdAt
updatedAt
query UserOne($input: UserOneInput!) {
userOne(input: $input) {
...User
}
}
${userFragment}
`;

View file

@ -1,4 +1,7 @@
import {Stack} from '@chakra-ui/react';
import { Avatar } from '@app/client/components/ui/avatar';
import { toFormattedDate } from '@app/client/helpers';
import { useUserOneQuery } from '@app/client/hooks/useUserOneQuery';
import { Card, Heading, HStack, SkeletonCircle, SkeletonText, Stack, Text } from '@chakra-ui/react';
import { createFileRoute } from '@tanstack/react-router';
export const Route = createFileRoute('/profile/$username')({
@ -7,5 +10,67 @@ export const Route = createFileRoute('/profile/$username')({
function RouteComponent() {
const { username } = Route.useParams();
return <Stack>{`Hello, "${username}"!`}</Stack>;
const { data } = useUserOneQuery({
input: {
username
}
});
if (data == null) return <UserOverviewSkeleton />;
return (
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
<UserOverview user={{ ...data.userOne }} />
</Stack>
);
}
function UserOverview({
user
}: {
readonly user: {
readonly id: string;
readonly username: string;
readonly description?: string | null;
readonly createdAt: string;
};
}) {
return (
<Stack width={{ base: '100%', md: '80%' }} maxWidth="840px" gap={3}>
<Stack direction="row" alignItems="start">
<Avatar size="2xl" fallback={user.username[0]} />
<Stack width="100%">
<Heading color="colorPalette.400" size="2xl" overflow="auto">
{user.username}
</Heading>
<Stack direction="row">
<Text fontWeight="bold">joined:</Text>
<Text color="fg.muted">{toFormattedDate({ value: user.createdAt })}</Text>
</Stack>
</Stack>
</Stack>
<Card.Root variant="subtle" width="100%">
<Card.Body paddingX="3" paddingY="1">
<Text>{user.description ?? 'No user description =('}</Text>
</Card.Body>
</Card.Root>
</Stack>
);
}
function UserOverviewSkeleton() {
return (
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
<Stack width={{ base: '100%', md: '80%' }} maxWidth="840px">
<SkeletonCircle width={70} height={70} />
<Stack gap="6" maxWidth="100%">
<HStack width="full">
<SkeletonText noOfLines={5} />
</HStack>
</Stack>
</Stack>
</Stack>
);
}

View file

@ -5,9 +5,17 @@ import { toUserSchema } from './mappers/userMapper';
export async function UserOneResolver(
_: any,
{ id }: QueryUserOneArgs,
{ input }: QueryUserOneArgs,
{ identity }: IdentityContext
): Promise<User> {
const model = await prisma.user.findUniqueOrThrow({ where: { id } });
if (input.id == null && input.username == null) throw new Error('Invalid user input.');
if (input.id != null && input.username != null) throw new Error('Invalid user input.');
const model = await prisma.user.findUniqueOrThrow({
where: {
id: input.id != null ? input.id : undefined,
username: input.username != null ? input.username : undefined
}
});
return toUserSchema(model);
}

View file

@ -3,7 +3,7 @@ scalar JSON
type Query {
identity: Identity!
userOne(id: ID!): User!
userOne(input: UserOneInput!): User!
bookOne(id: ID!): Book!
bookMany(input: BookManyInput!): BookManyOutput!
bookChapterOne(id: ID!): BookChapter!
@ -58,6 +58,11 @@ type User {
updatedAt: String!
}
input UserOneInput {
id: ID
username: String
}
input PaginationFilterInput {
terms: String
offset: Int