refactor, added slate, added editor, added book
This commit is contained in:
parent
bc250e73bb
commit
029b7e9353
33 changed files with 714 additions and 496 deletions
|
|
@ -1,16 +1,18 @@
|
|||
import { gql } from '@apollo/client';
|
||||
import { Skeleton, Stack } from '@chakra-ui/react';
|
||||
import { Stack } from '@chakra-ui/react';
|
||||
import { BooksListQuery } from '@generated/types';
|
||||
import { useState } from 'react';
|
||||
import { Pagination } from '../components/Pagination';
|
||||
import { WorkingList } from '../components/WorkingList';
|
||||
import { WorkingListSearch } from '../components/WorkingListSearch';
|
||||
import { ListSearch } from '../components/ListSearch';
|
||||
import { bookFragment } from '../fragments';
|
||||
import { usePaginatedQuery } from '../hooks/usePaginatedQuery';
|
||||
import { PaginatedListSkeleton } from './PaginatedListSkeleton';
|
||||
|
||||
export function BookSearchList() {
|
||||
const [workingSearchValue, setWorkingSearchValue] = useState<string>('');
|
||||
const [workingsListPage, setWorkingsListPage] = useState<number>(1);
|
||||
const [workingsListPageSize, setWorkingsListPageSize] = useState<number>(2);
|
||||
const [workingsListPageSize, setWorkingsListPageSize] = useState<number>(10);
|
||||
const startIndex = (workingsListPage - 1) * workingsListPageSize;
|
||||
|
||||
const { data } = usePaginatedQuery<BooksListQuery>({
|
||||
|
|
@ -26,35 +28,24 @@ export function BookSearchList() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<ListSearch placeholder="Search works..." onSubmit={value => setWorkingSearchValue(value)} />
|
||||
{data != null ? (
|
||||
<>
|
||||
<WorkingListSearch
|
||||
onSubmit={value => {
|
||||
setWorkingSearchValue(value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<WorkingList workingsList={[...data.bookMany.items]} />
|
||||
|
||||
{data.bookMany.items.length !== 0 && (
|
||||
<Pagination
|
||||
total={data.bookMany.total}
|
||||
page={workingsListPage}
|
||||
setPage={setWorkingsListPage}
|
||||
pageSize={workingsListPageSize}
|
||||
/>
|
||||
{data.bookMany.total > workingsListPageSize && (
|
||||
<Stack justifyContent="center" alignItems="center">
|
||||
<Pagination
|
||||
total={data.bookMany.total}
|
||||
page={workingsListPage}
|
||||
setPage={setWorkingsListPage}
|
||||
pageSize={workingsListPageSize}
|
||||
setPageSize={setWorkingsListPageSize}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Stack direction="row" justifyContent="center" alignItems="center" gap={2}>
|
||||
<Skeleton height={10} width="80%" />
|
||||
<Skeleton height={10} width="calc(20% - 6px)" />
|
||||
</Stack>
|
||||
<Skeleton height={20} />
|
||||
<Skeleton height={20} />
|
||||
<Skeleton height={20} />
|
||||
</>
|
||||
<PaginatedListSkeleton />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
@ -64,21 +55,10 @@ const query = gql`
|
|||
query BooksList($input: BookManyInput!) {
|
||||
bookMany(input: $input) {
|
||||
items {
|
||||
id
|
||||
title
|
||||
description
|
||||
author {
|
||||
id
|
||||
username
|
||||
}
|
||||
updatedAt
|
||||
progressStatus
|
||||
ratingTotal
|
||||
reviewsTotal
|
||||
wordsTotal
|
||||
chaptersTotal
|
||||
...Book
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
${bookFragment}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Button, Card, Stack, Text, Wrap } from '@chakra-ui/react';
|
||||
import isHotkey from 'is-hotkey';
|
||||
import React, { KeyboardEvent, MouseEvent, useCallback, useMemo, useState } from 'react';
|
||||
import React, { KeyboardEvent, MouseEvent, useCallback, useMemo } from 'react';
|
||||
import { createEditor, Descendant, Editor, Element as SlateElement, Transforms } from 'slate';
|
||||
import { withHistory } from 'slate-history';
|
||||
import {
|
||||
|
|
@ -44,7 +44,7 @@ export function EditorSlate() {
|
|||
}
|
||||
}}
|
||||
>
|
||||
<RichTextExample />
|
||||
<EditorViewport />
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</>
|
||||
|
|
@ -65,7 +65,7 @@ type AlignType = (typeof TEXT_ALIGN_TYPES)[number];
|
|||
type ListType = (typeof LIST_TYPES)[number];
|
||||
type CustomElementFormat = CustomElementType | AlignType | ListType;
|
||||
|
||||
const RichTextExample = () => {
|
||||
function EditorViewport() {
|
||||
const renderElement = useCallback((props: RenderElementProps) => <Element {...props} />, []);
|
||||
const renderLeaf = useCallback((props: RenderLeafProps) => <Leaf {...props} />, []);
|
||||
const editor = useMemo(() => withHistory(withReact(createEditor())), []);
|
||||
|
|
@ -123,12 +123,17 @@ const RichTextExample = () => {
|
|||
<BlockButton format="right" icon="nf nf-fa-align_right" />
|
||||
<BlockButton format="justify" icon="nf nf-fa-align_justify" />
|
||||
</Wrap>
|
||||
<Stack width="100%" display="flex" justifyContent="center" alignItems="center">
|
||||
<Stack
|
||||
width="100%"
|
||||
maxWidth="100%"
|
||||
paddingX={3}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Prose
|
||||
width="100%"
|
||||
height="100%"
|
||||
maxWidth="unset"
|
||||
maxHeight="unset"
|
||||
minWidth="100%"
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
|
|
@ -137,7 +142,11 @@ const RichTextExample = () => {
|
|||
className="slate-editor"
|
||||
style={{
|
||||
outline: 'none',
|
||||
minWidth: '100%'
|
||||
minWidth: '100%',
|
||||
maxWidth: 'unset',
|
||||
maxHeight: '75vh',
|
||||
padding: 0,
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
renderElement={renderElement}
|
||||
renderLeaf={renderLeaf}
|
||||
|
|
@ -158,9 +167,9 @@ const RichTextExample = () => {
|
|||
</Stack>
|
||||
</Slate>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const toggleBlock = (editor: CustomEditor, format: CustomElementFormat) => {
|
||||
function toggleBlock(editor: CustomEditor, format: CustomElementFormat) {
|
||||
const isActive = isBlockActive(editor, format, isAlignType(format) ? 'align' : 'type');
|
||||
const isList = isListType(format);
|
||||
|
||||
|
|
@ -188,9 +197,9 @@ const toggleBlock = (editor: CustomEditor, format: CustomElementFormat) => {
|
|||
const block = { type: format, children: [] };
|
||||
Transforms.wrapNodes(editor, block);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const toggleMark = (editor: CustomEditor, format: CustomTextKey) => {
|
||||
function toggleMark(editor: CustomEditor, format: CustomTextKey) {
|
||||
const isActive = isMarkActive(editor, format);
|
||||
|
||||
if (isActive) {
|
||||
|
|
@ -198,13 +207,13 @@ const toggleMark = (editor: CustomEditor, format: CustomTextKey) => {
|
|||
} else {
|
||||
Editor.addMark(editor, format, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const isBlockActive = (
|
||||
function isBlockActive(
|
||||
editor: CustomEditor,
|
||||
format: CustomElementFormat,
|
||||
blockType: 'type' | 'align' = 'type'
|
||||
) => {
|
||||
) {
|
||||
const { selection } = editor;
|
||||
if (!selection) return false;
|
||||
|
||||
|
|
@ -223,15 +232,15 @@ const isBlockActive = (
|
|||
})
|
||||
);
|
||||
|
||||
return !!match;
|
||||
};
|
||||
return Boolean(match);
|
||||
}
|
||||
|
||||
const isMarkActive = (editor: CustomEditor, format: CustomTextKey) => {
|
||||
function isMarkActive(editor: CustomEditor, format: CustomTextKey) {
|
||||
const marks = Editor.marks(editor);
|
||||
return marks ? marks[format] === true : false;
|
||||
};
|
||||
}
|
||||
|
||||
const Element = ({ attributes, children, element }: RenderElementProps) => {
|
||||
function Element({ attributes, children, element }: RenderElementProps) {
|
||||
const style: React.CSSProperties = {};
|
||||
if (isAlignElement(element)) {
|
||||
style.textAlign = element.align as AlignType;
|
||||
|
|
@ -280,9 +289,9 @@ const Element = ({ attributes, children, element }: RenderElementProps) => {
|
|||
</p>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Leaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
||||
function Leaf({ attributes, children, leaf }: RenderLeafProps) {
|
||||
if (leaf.bold) {
|
||||
children = <strong>{children}</strong>;
|
||||
}
|
||||
|
|
@ -300,7 +309,7 @@ const Leaf = ({ attributes, children, leaf }: RenderLeafProps) => {
|
|||
}
|
||||
|
||||
return <span {...attributes}>{children}</span>;
|
||||
};
|
||||
}
|
||||
|
||||
interface BlockButtonProps {
|
||||
format: CustomElementFormat;
|
||||
|
|
@ -308,7 +317,7 @@ interface BlockButtonProps {
|
|||
text?: string;
|
||||
}
|
||||
|
||||
const BlockButton = ({ format, icon, text }: BlockButtonProps) => {
|
||||
function BlockButton({ format, icon, text }: BlockButtonProps) {
|
||||
const editor = useSlate();
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -325,7 +334,7 @@ const BlockButton = ({ format, icon, text }: BlockButtonProps) => {
|
|||
</Stack>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
interface MarkButtonProps {
|
||||
format: CustomTextKey;
|
||||
|
|
@ -333,7 +342,7 @@ interface MarkButtonProps {
|
|||
text?: string;
|
||||
}
|
||||
|
||||
const MarkButton = ({ format, icon, text }: MarkButtonProps) => {
|
||||
function MarkButton({ format, icon, text }: MarkButtonProps) {
|
||||
const editor = useSlate();
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -347,16 +356,16 @@ const MarkButton = ({ format, icon, text }: MarkButtonProps) => {
|
|||
{text}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const isAlignType = (format: CustomElementFormat): format is AlignType => {
|
||||
function isAlignType(format: CustomElementFormat): format is AlignType {
|
||||
return TEXT_ALIGN_TYPES.includes(format as AlignType);
|
||||
};
|
||||
}
|
||||
|
||||
const isListType = (format: CustomElementFormat): format is ListType => {
|
||||
function isListType(format: CustomElementFormat): format is ListType {
|
||||
return LIST_TYPES.includes(format as ListType);
|
||||
};
|
||||
}
|
||||
|
||||
const isAlignElement = (element: CustomElement): element is CustomElementWithAlign => {
|
||||
function isAlignElement(element: CustomElement): element is CustomElementWithAlign {
|
||||
return 'align' in element;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
import { Card, Heading } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
import { BaseEditor, createEditor, Descendant } from 'slate';
|
||||
import { Editable, ReactEditor, Slate, withReact } from 'slate-react';
|
||||
|
||||
type CustomElement = { type: 'paragraph'; children: CustomText[] };
|
||||
type CustomText = { text: string };
|
||||
|
||||
declare module 'slate' {
|
||||
interface CustomTypes {
|
||||
Editor: BaseEditor & ReactEditor;
|
||||
Element: CustomElement;
|
||||
Text: CustomText;
|
||||
}
|
||||
}
|
||||
|
||||
export function EditorSlate() {
|
||||
const initialValue: Descendant[] = [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [{ text: 'A line of text in a paragraph.' }]
|
||||
}
|
||||
];
|
||||
|
||||
const [editor] = useState(() => withReact(createEditor()));
|
||||
return (
|
||||
// Add the editable component inside the context.
|
||||
<>
|
||||
<Card.Root
|
||||
variant={'subtle'}
|
||||
width="100%"
|
||||
maxWidth="840px"
|
||||
minHeight="sm"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Card.Header>
|
||||
<Heading textAlign="center">Editor</Heading>
|
||||
</Card.Header>
|
||||
<Card.Body
|
||||
width="90%"
|
||||
css={{
|
||||
'& *': {
|
||||
fontSize: 'revert',
|
||||
fontWeight: 'revert'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Slate editor={editor} initialValue={initialValue}>
|
||||
<Editable />
|
||||
</Slate>
|
||||
</Card.Body>
|
||||
</Card.Root>
|
||||
</>
|
||||
);
|
||||
}
|
||||
25
app/client/components/Link.tsx
Normal file
25
app/client/components/Link.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { Text } from '@chakra-ui/react';
|
||||
import { LinkProps as LinkExtended, Link as TanstackLink } from '@tanstack/react-router';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
interface LinkProps {
|
||||
children: ReactNode;
|
||||
to: LinkExtended['to'];
|
||||
link?: LinkExtended;
|
||||
}
|
||||
|
||||
export function Link(props: LinkProps) {
|
||||
return (
|
||||
<TanstackLink {...props}>
|
||||
<Text
|
||||
color="colorPalette.500"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Text>
|
||||
</TanstackLink>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
import { Button, Icon, Input, InputGroup, Stack } from '@chakra-ui/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export function WorkingListSearch({ onSubmit }: { onSubmit: (value: string) => void }) {
|
||||
export function ListSearch({
|
||||
placeholder,
|
||||
onSubmit
|
||||
}: {
|
||||
readonly placeholder?: string;
|
||||
readonly onSubmit: (value: string) => void;
|
||||
}) {
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
return (
|
||||
<form
|
||||
|
|
@ -22,7 +28,8 @@ export function WorkingListSearch({ onSubmit }: { onSubmit: (value: string) => v
|
|||
<Input
|
||||
value={searchValue}
|
||||
onChange={event => setSearchValue(event.currentTarget.value)}
|
||||
placeholder="Search works..."
|
||||
onSubmit={event => event.preventDefault()}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Button type="submit" border="none">
|
||||
17
app/client/components/PaginatedListSkeleton.tsx
Normal file
17
app/client/components/PaginatedListSkeleton.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Skeleton, Stack } from '@chakra-ui/react';
|
||||
|
||||
export function PaginatedListSkeleton({ disableSearch }: { readonly disableSearch?: boolean }) {
|
||||
return (
|
||||
<>
|
||||
{!disableSearch && (
|
||||
<Stack direction="row" justifyContent="center" alignItems="center" gap={2}>
|
||||
<Skeleton height={10} width="80%" />
|
||||
<Skeleton height={10} width="calc(20% - 6px)" />
|
||||
</Stack>
|
||||
)}
|
||||
<Skeleton height={20} />
|
||||
<Skeleton height={20} />
|
||||
<Skeleton height={20} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ export function Pagination({
|
|||
page: number;
|
||||
setPage: (value: number) => void;
|
||||
pageSize?: number;
|
||||
setPageSize?: (value: number) => void;
|
||||
}) {
|
||||
return (
|
||||
<ChakraPagination.Root
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { Card, List, Text } from '@chakra-ui/react';
|
||||
import { WorkingCardProps, WorkingListCard } from './WorkingListCard';
|
||||
import { WorkingListCard } from './WorkingListCard';
|
||||
import { BookOne } from '../interfaces';
|
||||
|
||||
export function WorkingList({ workingsList }: { workingsList: WorkingCardProps[] }) {
|
||||
export function WorkingList({ workingsList }: { workingsList: BookOne[] }) {
|
||||
return (
|
||||
<List.Root variant="plain" gap={4}>
|
||||
{workingsList.length !== 0 ? (
|
||||
|
|
|
|||
|
|
@ -1,23 +1,14 @@
|
|||
import { Badge, Box, Card, Heading, Link, Stack, Text } from '@chakra-ui/react';
|
||||
import { WorkingProgressStatus } from '@generated/types';
|
||||
import { Badge, Box, Card, Heading, Stack, Text } from '@chakra-ui/react';
|
||||
import { Link } from '@tanstack/react-router';
|
||||
import { toFormattedDate } from '../helpers';
|
||||
import { BookOne } from '../interfaces';
|
||||
import { IconBadge } from './IconBadge';
|
||||
import { WorkingProgressStatusBadge } from './WorkingProgressStatusBadge';
|
||||
import { WorkingRatingBadge } from './WorkingRatingBadge';
|
||||
|
||||
export interface WorkingCardProps {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
author: { id: string; username: string };
|
||||
progressStatus: WorkingProgressStatus;
|
||||
updatedAt: string;
|
||||
chaptersTotal: number;
|
||||
wordsTotal: number;
|
||||
ratingTotal: number;
|
||||
reviewsTotal: number;
|
||||
}
|
||||
import { WorkingReviewsBadge } from './WorkingReviewsTotalBadge';
|
||||
|
||||
export function WorkingListCard({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
author,
|
||||
|
|
@ -27,29 +18,47 @@ export function WorkingListCard({
|
|||
wordsTotal,
|
||||
ratingTotal,
|
||||
reviewsTotal
|
||||
}: WorkingCardProps) {
|
||||
}: BookOne) {
|
||||
return (
|
||||
<Card.Root width="100%" variant="elevated">
|
||||
<Card.Body>
|
||||
<Stack>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Link>
|
||||
<Heading size="lg">{title}</Heading>
|
||||
</Link>
|
||||
<Text as="div" minWidth={0}>
|
||||
<Link to={`/work/book/${id}`}>
|
||||
<Heading
|
||||
color="colorPalette.400"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Heading>
|
||||
</Link>
|
||||
</Text>
|
||||
|
||||
<Stack direction="row" justifyContent="center" alignItems="center">
|
||||
<WorkingRatingBadge ratingTotal={ratingTotal} />
|
||||
<IconBadge backgroundColor="orange.400" iconClass="nf nf-oct-comment">
|
||||
<Text>{reviewsTotal}</Text>
|
||||
</IconBadge>
|
||||
<WorkingReviewsBadge reviewsTotal={reviewsTotal} />
|
||||
<WorkingProgressStatusBadge progressStatus={progressStatus} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack>
|
||||
<Stack direction="row">
|
||||
<Text>by:</Text>
|
||||
<Link>{author.username}</Link>
|
||||
<Link>by:</Link>
|
||||
<Link to={`/profile/${author.username}`}>
|
||||
<Text
|
||||
color="colorPalette.400"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
{author.username}
|
||||
</Text>
|
||||
</Link>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row">
|
||||
|
|
@ -57,7 +66,7 @@ export function WorkingListCard({
|
|||
<Badge size="md" variant="solid">
|
||||
<Stack direction="row">
|
||||
<Text>last updated:</Text>
|
||||
<Text>{updatedAt}</Text>
|
||||
<Text>{toFormattedDate({ value: updatedAt })}</Text>
|
||||
</Stack>
|
||||
</Badge>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export function WorkingProgressStatusBadge({
|
|||
progressStatus: WorkingProgressStatus;
|
||||
}) {
|
||||
return (
|
||||
<Box flex={1} justifyContent="center" alignItems="center">
|
||||
<Box>
|
||||
<Badge
|
||||
variant="solid"
|
||||
backgroundColor={toBgColorByProgressStatus(progressStatus)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BadgeProps, Text } from '@chakra-ui/react';
|
||||
import { BadgeProps, Box, Text } from '@chakra-ui/react';
|
||||
import { IconBadge } from './IconBadge';
|
||||
|
||||
interface WorkingRatingParams {
|
||||
|
|
@ -21,8 +21,10 @@ export function WorkingRatingBadge({ ratingTotal }: { ratingTotal: number }) {
|
|||
iconClass: 'nf nf-fa-angle_down'
|
||||
};
|
||||
return (
|
||||
<IconBadge backgroundColor={ratingParams.color} iconClass={ratingParams.iconClass}>
|
||||
<Text>{ratingParams.rating}</Text>
|
||||
</IconBadge>
|
||||
<Box>
|
||||
<IconBadge backgroundColor={ratingParams.color} iconClass={ratingParams.iconClass}>
|
||||
<Text>{ratingParams.rating}</Text>
|
||||
</IconBadge>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
12
app/client/components/WorkingReviewsTotalBadge.tsx
Normal file
12
app/client/components/WorkingReviewsTotalBadge.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Box, Text } from '@chakra-ui/react';
|
||||
import { IconBadge } from './IconBadge';
|
||||
|
||||
export function WorkingReviewsBadge({ reviewsTotal }: { readonly reviewsTotal: number }) {
|
||||
return (
|
||||
<Box>
|
||||
<IconBadge backgroundColor="orange.400" iconClass="nf nf-oct-comment">
|
||||
<Text>{reviewsTotal}</Text>
|
||||
</IconBadge>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,26 +1,22 @@
|
|||
import { Link as ChakraLink } from "@chakra-ui/react";
|
||||
import { createLink, LinkComponent } from "@tanstack/react-router";
|
||||
import * as React from "react";
|
||||
import { Link as ChakraLink } from '@chakra-ui/react';
|
||||
import { createLink, LinkComponent } from '@tanstack/react-router';
|
||||
import * as React from 'react';
|
||||
|
||||
interface ChakraLinkProps
|
||||
extends Omit<React.ComponentPropsWithoutRef<typeof ChakraLink>, "href"> {}
|
||||
interface ChakraLinkProps extends Omit<React.ComponentPropsWithoutRef<typeof ChakraLink>, 'href'> {}
|
||||
|
||||
const ChakraLinkComponent = React.forwardRef<
|
||||
HTMLAnchorElement,
|
||||
ChakraLinkProps
|
||||
>((props, ref) => {
|
||||
const ChakraLinkComponent = React.forwardRef<HTMLAnchorElement, ChakraLinkProps>((props, ref) => {
|
||||
return <ChakraLink ref={ref} {...props} />;
|
||||
});
|
||||
|
||||
const CreatedLinkComponent = createLink(ChakraLinkComponent);
|
||||
|
||||
export const Link: LinkComponent<typeof ChakraLinkComponent> = (props) => {
|
||||
export const Link: LinkComponent<typeof ChakraLinkComponent> = props => {
|
||||
return (
|
||||
<CreatedLinkComponent
|
||||
textDecoration={"underline"}
|
||||
_hover={{ textDecoration: "none" }}
|
||||
_focus={{ textDecoration: "none" }}
|
||||
preload={"intent"}
|
||||
textDecoration={'underline'}
|
||||
_hover={{ textDecoration: 'none' }}
|
||||
_focus={{ textDecoration: 'none' }}
|
||||
preload={'intent'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,34 @@ export const sessionFragment = gql`
|
|||
}
|
||||
`;
|
||||
|
||||
// refreshToken {
|
||||
// token
|
||||
// maxAge
|
||||
// secure
|
||||
// httpOnly
|
||||
// sameSite
|
||||
// }
|
||||
export const bookFragment = gql`
|
||||
fragment Book on Book {
|
||||
id
|
||||
title
|
||||
description
|
||||
author {
|
||||
id
|
||||
username
|
||||
}
|
||||
createdAt
|
||||
updatedAt
|
||||
progressStatus
|
||||
wordsTotal
|
||||
chaptersTotal
|
||||
ratingTotal
|
||||
reviewsTotal
|
||||
}
|
||||
`;
|
||||
|
||||
export const bookChapterFragment = gql`
|
||||
fragment BookChapter on BookChapter {
|
||||
id
|
||||
title
|
||||
createdAt
|
||||
updatedAt
|
||||
foreword
|
||||
footnote
|
||||
sequenceNumber
|
||||
reviewsTotal
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
import { WorkingProgressStatus } from '@generated/types';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export function assertUnreachable(value: never): never {
|
||||
throw new Error(`Unhandled scenario: ${value}`);
|
||||
}
|
||||
|
||||
export function toFormattedDate({ value }: { value: string }): string {
|
||||
const date = new Date(value);
|
||||
const day = dayjs(date).format('DD.MM.YYYY');
|
||||
return day;
|
||||
}
|
||||
|
||||
export function toBgColorByProgressStatus(value: WorkingProgressStatus): string {
|
||||
if (value === WorkingProgressStatus.InProgress) return 'yellow.500';
|
||||
if (value === WorkingProgressStatus.Frozen) return 'blue.500';
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { DocumentNode } from 'graphql';
|
|||
interface UsePaginatedQueryProps {
|
||||
query: DocumentNode;
|
||||
input: {
|
||||
[key: string]: any; // for the custom inputs
|
||||
filter: {
|
||||
terms?: string | null;
|
||||
offset?: number | null;
|
||||
|
|
|
|||
18
app/client/interfaces.tsx
Normal file
18
app/client/interfaces.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { WorkingProgressStatus } from '@generated/types';
|
||||
|
||||
export interface BookOne {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly description: string;
|
||||
readonly author: {
|
||||
readonly id: string;
|
||||
readonly username: string;
|
||||
};
|
||||
readonly createdAt: string;
|
||||
readonly updatedAt: string;
|
||||
readonly progressStatus: WorkingProgressStatus;
|
||||
readonly wordsTotal: number;
|
||||
readonly chaptersTotal: number;
|
||||
readonly ratingTotal: number;
|
||||
readonly reviewsTotal: number;
|
||||
}
|
||||
|
|
@ -1,15 +1,5 @@
|
|||
import {
|
||||
Link as ChakraLink,
|
||||
Icon,
|
||||
Input,
|
||||
InputGroup,
|
||||
Kbd,
|
||||
Menu,
|
||||
Separator,
|
||||
Stack,
|
||||
Text
|
||||
} from '@chakra-ui/react';
|
||||
import { createRootRouteWithContext, Link, Outlet } from '@tanstack/react-router';
|
||||
import { Icon, Input, InputGroup, Kbd, Menu, Separator, Stack, Text } from '@chakra-ui/react';
|
||||
import { createRootRouteWithContext, Outlet, Link } from '@tanstack/react-router';
|
||||
import React from 'react';
|
||||
import { Avatar } from '../components/ui/avatar';
|
||||
import { Button } from '../components/ui/button';
|
||||
|
|
@ -18,7 +8,6 @@ import { Tooltip } from '../components/ui/tooltip';
|
|||
import { useAuth } from '../hooks/auth';
|
||||
import { useIdentity } from '../hooks/identity';
|
||||
import { appName } from '../main';
|
||||
// import { Link as ChakraLink } from '../components/ui/link';
|
||||
|
||||
const TanStackRouterDevtools =
|
||||
process.env.NODE_ENV === 'production'
|
||||
|
|
@ -67,12 +56,28 @@ export const Route = createRootRouteWithContext<RouterContext>()({
|
|||
<>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Link to="/editor">
|
||||
<ChakraLink>editor</ChakraLink>
|
||||
<Text
|
||||
color="colorPalette.400"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
editor
|
||||
</Text>
|
||||
</Link>
|
||||
</Stack>
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Link to="/new/book">
|
||||
<ChakraLink>create</ChakraLink>
|
||||
<Text
|
||||
color="colorPalette.400"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
create
|
||||
</Text>
|
||||
</Link>
|
||||
</Stack>
|
||||
</>
|
||||
|
|
@ -99,10 +104,7 @@ export const Route = createRootRouteWithContext<RouterContext>()({
|
|||
{identity != null ? (
|
||||
<Menu.Root>
|
||||
<Menu.Trigger css={{ '&:hover': { cursor: 'pointer' } }}>
|
||||
<Avatar
|
||||
size="sm"
|
||||
// src="https://avatars.githubusercontent.com/u/40203084?v=4"
|
||||
/>
|
||||
<Avatar size="sm" />
|
||||
</Menu.Trigger>
|
||||
<Menu.Positioner mt={2}>
|
||||
<Menu.Content backgroundColor={'bg.subtle'}>
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
// import { gql } from '@apollo/client';
|
||||
import { createLazyFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createLazyFileRoute('/book')({
|
||||
component: RouteComponent
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>"/book"</div>;
|
||||
}
|
||||
|
||||
// const getBookQuery = gql`
|
||||
// query BookOne {
|
||||
// booksMany {
|
||||
// id
|
||||
// author
|
||||
// title
|
||||
// }
|
||||
// }
|
||||
// `;
|
||||
|
|
@ -15,49 +15,3 @@ function RouteComponent() {
|
|||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
// const workingData1: WorkingCardProps = {
|
||||
// id: '1',
|
||||
// title: 'Worth the Candle',
|
||||
// description: `A teenager struggling after the death of his best friend finds himself in a fantasy
|
||||
// world - one which seems to be an amalgamation of every Dungeons and Dragons campaign
|
||||
// they ever played together. Now he's stuck trying to find the answers to why he's there
|
||||
// and what this world is trying to say. The most terrifying answer might be that this
|
||||
// world is an expression of the person he was back on Earth.`,
|
||||
// author: {
|
||||
// id: '1',
|
||||
// createdAt: '12.8.2025',
|
||||
// updatedAt: '4.9.2025',
|
||||
// username: 'AlexanderWhales'
|
||||
// },
|
||||
// progressStatus: WorkingProgressStatus.Finished,
|
||||
// updatedAt: '20.10.2025',
|
||||
// chaptersTotal: 10,
|
||||
// wordsTotal: 34000,
|
||||
// ratingTotal: 10,
|
||||
// reviewsTotal: 3
|
||||
// };
|
||||
//
|
||||
// const workingData2: WorkingCardProps = {
|
||||
// id: '2',
|
||||
// title: 'Test',
|
||||
// description: `Description.`,
|
||||
// author: {
|
||||
// id: '1',
|
||||
// createdAt: '12.8.2025',
|
||||
// updatedAt: '4.9.2025',
|
||||
// username: 'JohnSmith'
|
||||
// },
|
||||
// progressStatus: WorkingProgressStatus.InProgress,
|
||||
// updatedAt: '20.10.2025',
|
||||
// chaptersTotal: 10,
|
||||
// wordsTotal: 1200,
|
||||
// ratingTotal: -10,
|
||||
// reviewsTotal: 0
|
||||
// };
|
||||
//
|
||||
// const mockWorkingData = {
|
||||
// total: 4,
|
||||
// items: [workingData1, workingData2, { ...workingData1, id: '3' }, { ...workingData2, id: '4' }]
|
||||
// };
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
import { gql } from '@apollo/client';
|
||||
import { useToastMutation } from '@app/client/hooks/useToastMutation';
|
||||
import { Heading, Input, Stack, Textarea } from '@chakra-ui/react';
|
||||
import { Button } from '@client/components/ui/button';
|
||||
import { Field } from '@client/components/ui/field';
|
||||
import { CreateNewBookMutation } from '@generated/types';
|
||||
import { valibotResolver } from '@hookform/resolvers/valibot';
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router';
|
||||
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import * as v from 'valibot';
|
||||
|
||||
|
|
@ -28,10 +31,16 @@ interface FormValues {
|
|||
}
|
||||
|
||||
function NewBookForm() {
|
||||
const navigate = useNavigate();
|
||||
const { update: bookCreate } = useToastMutation<CreateNewBookMutation>({
|
||||
mutation: bookCreateMutation,
|
||||
successMsg: 'Book created successfully!'
|
||||
});
|
||||
|
||||
const schema = v.pipe(
|
||||
v.object({
|
||||
title: v.pipe(v.string(), v.nonEmpty('Title is required')),
|
||||
description: v.pipe(v.string(), v.nonEmpty('Password is required'))
|
||||
title: v.pipe(v.string(), v.nonEmpty('Title is required'), v.maxLength(60)),
|
||||
description: v.pipe(v.string(), v.nonEmpty('Description is required'), v.maxLength(900))
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -42,7 +51,23 @@ function NewBookForm() {
|
|||
} = useForm<FormValues>({ resolver: valibotResolver(schema) });
|
||||
|
||||
return (
|
||||
<form style={{ width: '80%' }} onSubmit={handleSubmit(async values => {})}>
|
||||
<form
|
||||
style={{ width: '80%', maxWidth: '840px' }}
|
||||
onSubmit={handleSubmit(async values => {
|
||||
const { data } = await bookCreate({
|
||||
variables: {
|
||||
input: {
|
||||
title: values.title,
|
||||
description: values.description
|
||||
}
|
||||
}
|
||||
});
|
||||
if (data != null)
|
||||
navigate({
|
||||
to: `/work/book/${data.bookCreate.id}`
|
||||
});
|
||||
})}
|
||||
>
|
||||
<Stack>
|
||||
<Field label="Title" invalid={errors.title != null} errorText={errors.title?.message}>
|
||||
<Input {...register('title')} />
|
||||
|
|
@ -52,7 +77,7 @@ function NewBookForm() {
|
|||
invalid={errors.description != null}
|
||||
errorText={errors.description?.message}
|
||||
>
|
||||
<Textarea width="100%" lineHeight={4} {...register('description')} />
|
||||
<Textarea width="100%" height={'sm'} {...register('description')} />
|
||||
</Field>
|
||||
<Button type="submit">Create new book</Button>
|
||||
</Stack>
|
||||
|
|
@ -60,38 +85,12 @@ function NewBookForm() {
|
|||
);
|
||||
}
|
||||
|
||||
// function LoginForm() {
|
||||
// const schema = v.pipe(
|
||||
// v.object({
|
||||
// email: v.pipe(v.string(), v.nonEmpty('Email is required'), v.email('Invalid email')),
|
||||
// password: v.pipe(v.string(), v.nonEmpty('Password is required'))
|
||||
// })
|
||||
// );
|
||||
//
|
||||
// const {
|
||||
// register,
|
||||
// handleSubmit,
|
||||
// formState: { errors }
|
||||
// } = useForm<FormValues>({ resolver: valibotResolver(schema) });
|
||||
//
|
||||
// const { login } = useAuth();
|
||||
//
|
||||
// return (
|
||||
// <form
|
||||
// >
|
||||
// <Stack>
|
||||
// <Field label="Email" invalid={errors.email != null} errorText={errors.email?.message}>
|
||||
// <Input {...register('email')} />
|
||||
// </Field>
|
||||
// <Field
|
||||
// label="Password"
|
||||
// invalid={errors.password != null}
|
||||
// errorText={errors.password?.message}
|
||||
// >
|
||||
// <PasswordInput {...register('password')} />
|
||||
// </Field>
|
||||
// <Button type="submit">Sign in</Button>
|
||||
// </Stack>
|
||||
// </form>
|
||||
// );
|
||||
// }
|
||||
const bookCreateMutation = gql`
|
||||
mutation CreateNewBook($input: BookCreateInput!) {
|
||||
bookCreate(input: $input) {
|
||||
id
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
185
app/client/pages/work/book/$bookId.tsx
Normal file
185
app/client/pages/work/book/$bookId.tsx
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import { gql, useQuery } from '@apollo/client';
|
||||
import { PaginatedListSkeleton } from '@app/client/components/PaginatedListSkeleton';
|
||||
import { Pagination } from '@app/client/components/Pagination';
|
||||
import { WorkingProgressStatusBadge } from '@app/client/components/WorkingProgressStatusBadge';
|
||||
import { WorkingRatingBadge } from '@app/client/components/WorkingRatingBadge';
|
||||
import { WorkingReviewsBadge } from '@app/client/components/WorkingReviewsTotalBadge';
|
||||
import { bookChapterFragment, bookFragment } from '@app/client/fragments';
|
||||
import { toFormattedDate } from '@app/client/helpers';
|
||||
import { usePaginatedQuery } from '@app/client/hooks/usePaginatedQuery';
|
||||
import { BookOne } from '@app/client/interfaces';
|
||||
import {
|
||||
Heading,
|
||||
HStack,
|
||||
Skeleton,
|
||||
SkeletonCircle,
|
||||
SkeletonText,
|
||||
Stack,
|
||||
Text
|
||||
} from '@chakra-ui/react';
|
||||
import { BookChapterListQuery, BookOverviewQuery } from '@generated/types';
|
||||
import { createFileRoute, Link } from '@tanstack/react-router';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const Route = createFileRoute('/work/book/$bookId')({
|
||||
component: RouteComponent
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const { bookId: id } = Route.useParams();
|
||||
const { data: bookData } = useQuery<BookOverviewQuery>(bookQuery, { variables: { id } });
|
||||
|
||||
return (
|
||||
<Stack width="100%" justifyContent="center" alignItems="center" padding={3}>
|
||||
{bookData != null ? <BookOverview {...bookData.bookOne} /> : <BookOverviewSkeleton />}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function BookOverview({
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
author,
|
||||
progressStatus,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
wordsTotal,
|
||||
chaptersTotal,
|
||||
reviewsTotal,
|
||||
ratingTotal
|
||||
}: BookOne) {
|
||||
return (
|
||||
<Stack width="80%" maxWidth="840px" gap={3}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Heading color="colorPalette.400" size="2xl" overflow="auto">
|
||||
{title}
|
||||
</Heading>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="column" gap={1}>
|
||||
<Stack direction="row">
|
||||
<Link>
|
||||
<Text fontWeight="bold">by:</Text>
|
||||
</Link>
|
||||
<Link to={`/profile/${author.username}`}>
|
||||
<Text
|
||||
color="colorPalette.400"
|
||||
_hover={{
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'colorPalette.700'
|
||||
}}
|
||||
>
|
||||
{author.username}
|
||||
</Text>
|
||||
</Link>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row">
|
||||
<Text fontWeight="bold">created at:</Text>
|
||||
<Text>{toFormattedDate({ value: createdAt })}</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row">
|
||||
<Text fontWeight="bold">last updated:</Text>
|
||||
<Text>{toFormattedDate({ value: updatedAt })}</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row">
|
||||
<Text fontWeight="bold">chapters:</Text>
|
||||
<Text>{chaptersTotal}</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row">
|
||||
<Text fontWeight="bold">words:</Text>
|
||||
<Text>{wordsTotal}</Text>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack direction="row" justifyContent="left" alignItems="center">
|
||||
<WorkingRatingBadge ratingTotal={ratingTotal} />
|
||||
<WorkingReviewsBadge reviewsTotal={reviewsTotal} />
|
||||
<WorkingProgressStatusBadge progressStatus={progressStatus} />
|
||||
</Stack>
|
||||
|
||||
<Text>{description}</Text>
|
||||
|
||||
<BookChapterList id={id} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function BookChapterList({ id }: { readonly id: string }) {
|
||||
const [chaptersListPage, setChaptersListPage] = useState<number>(1);
|
||||
const [chaptersListPageSize, setChaptersListPageSize] = useState<number>(10);
|
||||
const startIndex = (chaptersListPage - 1) * chaptersListPageSize;
|
||||
|
||||
const { data } = usePaginatedQuery<BookChapterListQuery>({
|
||||
query: bookChapterListQuery,
|
||||
input: {
|
||||
bookId: id,
|
||||
filter: {
|
||||
offset: startIndex,
|
||||
size: chaptersListPageSize
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
{data != null ? (
|
||||
<>
|
||||
<Stack>...chapters</Stack>
|
||||
{data.bookChapterMany.total > chaptersListPageSize && (
|
||||
<Stack justifyContent="center" alignItems="center">
|
||||
<Pagination
|
||||
total={data.bookChapterMany.total}
|
||||
page={chaptersListPage}
|
||||
setPage={setChaptersListPage}
|
||||
pageSize={chaptersListPageSize}
|
||||
setPageSize={setChaptersListPageSize}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<PaginatedListSkeleton disableSearch />
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
function BookOverviewSkeleton() {
|
||||
return (
|
||||
<Stack width="80%" maxWidth="840px">
|
||||
<Stack gap="6" maxWidth="100%">
|
||||
<HStack width="full">
|
||||
<SkeletonCircle size="10" />
|
||||
<SkeletonText noOfLines={5} />
|
||||
</HStack>
|
||||
<Skeleton height="200px" />
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const bookQuery = gql`
|
||||
query BookOverview($id: ID!) {
|
||||
bookOne(id: $id) {
|
||||
...Book
|
||||
}
|
||||
}
|
||||
${bookFragment}
|
||||
`;
|
||||
|
||||
const bookChapterListQuery = gql`
|
||||
query BookChapterList($input: BookChapterManyInput!) {
|
||||
bookChapterMany(input: $input) {
|
||||
items {
|
||||
...BookChapter
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
${bookChapterFragment}
|
||||
`;
|
||||
|
|
@ -8,8 +8,6 @@
|
|||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
// Import Routes
|
||||
|
||||
import { Route as rootRoute } from './pages/__root'
|
||||
|
|
@ -20,19 +18,10 @@ import { Route as AboutImport } from './pages/about'
|
|||
import { Route as IndexImport } from './pages/index'
|
||||
import { Route as ProfileUsernameImport } from './pages/profile/$username'
|
||||
import { Route as NewBookImport } from './pages/new/book'
|
||||
|
||||
// Create Virtual Routes
|
||||
|
||||
const BookLazyImport = createFileRoute('/book')()
|
||||
import { Route as WorkBookBookIdImport } from './pages/work/book/$bookId'
|
||||
|
||||
// Create/Update Routes
|
||||
|
||||
const BookLazyRoute = BookLazyImport.update({
|
||||
id: '/book',
|
||||
path: '/book',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any).lazy(() => import('./pages/book.lazy').then((d) => d.Route))
|
||||
|
||||
const RegisterRoute = RegisterImport.update({
|
||||
id: '/register',
|
||||
path: '/register',
|
||||
|
|
@ -75,6 +64,12 @@ const NewBookRoute = NewBookImport.update({
|
|||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const WorkBookBookIdRoute = WorkBookBookIdImport.update({
|
||||
id: '/work/book/$bookId',
|
||||
path: '/work/book/$bookId',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
// Populate the FileRoutesByPath interface
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
|
|
@ -114,13 +109,6 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof RegisterImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/book': {
|
||||
id: '/book'
|
||||
path: '/book'
|
||||
fullPath: '/book'
|
||||
preLoaderRoute: typeof BookLazyImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/new/book': {
|
||||
id: '/new/book'
|
||||
path: '/new/book'
|
||||
|
|
@ -135,6 +123,13 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof ProfileUsernameImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/work/book/$bookId': {
|
||||
id: '/work/book/$bookId'
|
||||
path: '/work/book/$bookId'
|
||||
fullPath: '/work/book/$bookId'
|
||||
preLoaderRoute: typeof WorkBookBookIdImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -146,9 +141,9 @@ export interface FileRoutesByFullPath {
|
|||
'/editor': typeof EditorRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/register': typeof RegisterRoute
|
||||
'/book': typeof BookLazyRoute
|
||||
'/new/book': typeof NewBookRoute
|
||||
'/profile/$username': typeof ProfileUsernameRoute
|
||||
'/work/book/$bookId': typeof WorkBookBookIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
|
|
@ -157,9 +152,9 @@ export interface FileRoutesByTo {
|
|||
'/editor': typeof EditorRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/register': typeof RegisterRoute
|
||||
'/book': typeof BookLazyRoute
|
||||
'/new/book': typeof NewBookRoute
|
||||
'/profile/$username': typeof ProfileUsernameRoute
|
||||
'/work/book/$bookId': typeof WorkBookBookIdRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
|
|
@ -169,9 +164,9 @@ export interface FileRoutesById {
|
|||
'/editor': typeof EditorRoute
|
||||
'/login': typeof LoginRoute
|
||||
'/register': typeof RegisterRoute
|
||||
'/book': typeof BookLazyRoute
|
||||
'/new/book': typeof NewBookRoute
|
||||
'/profile/$username': typeof ProfileUsernameRoute
|
||||
'/work/book/$bookId': typeof WorkBookBookIdRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
|
|
@ -182,9 +177,9 @@ export interface FileRouteTypes {
|
|||
| '/editor'
|
||||
| '/login'
|
||||
| '/register'
|
||||
| '/book'
|
||||
| '/new/book'
|
||||
| '/profile/$username'
|
||||
| '/work/book/$bookId'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
|
|
@ -192,9 +187,9 @@ export interface FileRouteTypes {
|
|||
| '/editor'
|
||||
| '/login'
|
||||
| '/register'
|
||||
| '/book'
|
||||
| '/new/book'
|
||||
| '/profile/$username'
|
||||
| '/work/book/$bookId'
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
|
|
@ -202,9 +197,9 @@ export interface FileRouteTypes {
|
|||
| '/editor'
|
||||
| '/login'
|
||||
| '/register'
|
||||
| '/book'
|
||||
| '/new/book'
|
||||
| '/profile/$username'
|
||||
| '/work/book/$bookId'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
|
|
@ -214,9 +209,9 @@ export interface RootRouteChildren {
|
|||
EditorRoute: typeof EditorRoute
|
||||
LoginRoute: typeof LoginRoute
|
||||
RegisterRoute: typeof RegisterRoute
|
||||
BookLazyRoute: typeof BookLazyRoute
|
||||
NewBookRoute: typeof NewBookRoute
|
||||
ProfileUsernameRoute: typeof ProfileUsernameRoute
|
||||
WorkBookBookIdRoute: typeof WorkBookBookIdRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
|
|
@ -225,9 +220,9 @@ const rootRouteChildren: RootRouteChildren = {
|
|||
EditorRoute: EditorRoute,
|
||||
LoginRoute: LoginRoute,
|
||||
RegisterRoute: RegisterRoute,
|
||||
BookLazyRoute: BookLazyRoute,
|
||||
NewBookRoute: NewBookRoute,
|
||||
ProfileUsernameRoute: ProfileUsernameRoute,
|
||||
WorkBookBookIdRoute: WorkBookBookIdRoute,
|
||||
}
|
||||
|
||||
export const routeTree = rootRoute
|
||||
|
|
@ -245,9 +240,9 @@ export const routeTree = rootRoute
|
|||
"/editor",
|
||||
"/login",
|
||||
"/register",
|
||||
"/book",
|
||||
"/new/book",
|
||||
"/profile/$username"
|
||||
"/profile/$username",
|
||||
"/work/book/$bookId"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
|
|
@ -265,14 +260,14 @@ export const routeTree = rootRoute
|
|||
"/register": {
|
||||
"filePath": "register.tsx"
|
||||
},
|
||||
"/book": {
|
||||
"filePath": "book.lazy.tsx"
|
||||
},
|
||||
"/new/book": {
|
||||
"filePath": "new/book.tsx"
|
||||
},
|
||||
"/profile/$username": {
|
||||
"filePath": "profile/$username.tsx"
|
||||
},
|
||||
"/work/book/$bookId": {
|
||||
"filePath": "work/book/$bookId.tsx"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,18 @@
|
|||
import { MutationResolvers, QueryResolvers } from '@generated/types';
|
||||
|
||||
/* Query */
|
||||
import { IdentityResolver } from './resolvers/identityResolver';
|
||||
import { UserOneResolver } from './resolvers/userOneResolver';
|
||||
import { BookChapterManyResolver } from './resolvers/bookChapterMany';
|
||||
import { BookChapterOneResolver } from './resolvers/bookChapterOne';
|
||||
import { BookChapterManyResolver } from './resolvers/bookChapterManyResolver';
|
||||
import { BookChapterOneResolver } from './resolvers/bookChapterOneResolver';
|
||||
import { BookManyResolver } from './resolvers/bookManyResolver';
|
||||
import { BookOneResolver } from './resolvers/bookOneResolver';
|
||||
import { IdentityResolver } from './resolvers/identityResolver';
|
||||
import { UserOneResolver } from './resolvers/userOneResolver';
|
||||
|
||||
/* Mutation */
|
||||
import { LoginResolver } from './resolvers/auth/login';
|
||||
import { LogoutResolver } from './resolvers/auth/logout';
|
||||
import { SignUpResolver } from './resolvers/auth/signUp';
|
||||
import { BookCreateResolver } from './resolvers/bookCreateResolver';
|
||||
|
||||
export const Query: QueryResolvers = {
|
||||
bookOne: BookOneResolver,
|
||||
|
|
@ -26,7 +27,8 @@ export const Query: QueryResolvers = {
|
|||
export const Mutation: MutationResolvers = {
|
||||
signUp: SignUpResolver,
|
||||
login: LoginResolver,
|
||||
logout: LogoutResolver
|
||||
logout: LogoutResolver,
|
||||
bookCreate: BookCreateResolver
|
||||
};
|
||||
|
||||
export const Subscription = {};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { IdentityContext } from '@app/server/identity';
|
||||
import { prisma } from '@app/server/prisma/client';
|
||||
import { BookChapterManyOutput, QueryBookChapterManyArgs } from '@generated/types';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { toBookChapterSchema } from './mappers/bookChapterMapper';
|
||||
|
||||
export async function BookChapterManyResolver(
|
||||
|
|
@ -9,11 +10,14 @@ export async function BookChapterManyResolver(
|
|||
{ identity }: IdentityContext
|
||||
): Promise<BookChapterManyOutput> {
|
||||
const { bookId, filter } = input;
|
||||
const terms = filter.terms?.toLowerCase();
|
||||
|
||||
const bookChapterWhere: Prisma.BookChapterWhereInput = {
|
||||
AND: [{ bookId }, { OR: terms != null ? [{ title: { contains: terms } }] : undefined }]
|
||||
};
|
||||
|
||||
const items = await prisma.bookChapter.findMany({
|
||||
where: {
|
||||
bookId
|
||||
},
|
||||
where: bookChapterWhere,
|
||||
skip: filter.offset ?? 0,
|
||||
take: filter.size ?? 10
|
||||
});
|
||||
27
app/server/graphql/resolvers/bookCreateResolver.ts
Normal file
27
app/server/graphql/resolvers/bookCreateResolver.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { ensureIdentity } from '@app/server/helpers';
|
||||
import { IdentityContext } from '@app/server/identity';
|
||||
import { prisma, uuid } from '@app/server/prisma/client';
|
||||
import { Book, MutationBookCreateArgs } from '@generated/types';
|
||||
import { toBookSchema } from './mappers/bookMapper';
|
||||
|
||||
export async function BookCreateResolver(
|
||||
_: any,
|
||||
{ input }: MutationBookCreateArgs,
|
||||
{ identity }: IdentityContext
|
||||
): Promise<Book> {
|
||||
ensureIdentity(identity);
|
||||
|
||||
console.log(input.title.length);
|
||||
if (input.title.length > 60) throw new Error('Title is too long.');
|
||||
if (input.description.length > 900) throw new Error('Description is too long.');
|
||||
|
||||
const model = await prisma.book.create({
|
||||
data: {
|
||||
id: uuid(),
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
authorId: identity.id
|
||||
}
|
||||
});
|
||||
return toBookSchema(model);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { IdentityContext } from '@app/server/identity';
|
||||
import { prisma } from '@app/server/prisma/client';
|
||||
import { BookManyOutput, QueryBookManyArgs } from '@generated/types';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { toBookSchema } from './mappers/bookMapper';
|
||||
|
||||
export async function BookManyResolver(
|
||||
|
|
@ -11,19 +12,19 @@ export async function BookManyResolver(
|
|||
const { filter } = input;
|
||||
const terms = filter.terms?.toLowerCase();
|
||||
|
||||
const bookWhere: Prisma.BookWhereInput = {
|
||||
OR:
|
||||
terms != null ? [{ title: { contains: terms } }, { author: { username: terms } }] : undefined
|
||||
};
|
||||
|
||||
const items = await prisma.book.findMany({
|
||||
where: {
|
||||
OR:
|
||||
terms != null
|
||||
? [{ title: { contains: terms } }, { author: { username: terms } }]
|
||||
: undefined
|
||||
},
|
||||
where: bookWhere,
|
||||
skip: filter.offset ?? 0,
|
||||
take: filter.size ?? 10
|
||||
});
|
||||
|
||||
return {
|
||||
items: await Promise.all(items.map(value => toBookSchema(value))),
|
||||
total: items.length
|
||||
total: await prisma.book.count({ where: bookWhere })
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ type Mutation {
|
|||
signUp(input: SignUpInput!): Void
|
||||
login(input: LoginInput!): Session!
|
||||
logout: Void
|
||||
bookCreate(input: BookCreateInput!): Book!
|
||||
}
|
||||
|
||||
input SignUpInput {
|
||||
|
|
|
|||
7
app/server/helpers.ts
Normal file
7
app/server/helpers.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { IdentityContext } from './identity';
|
||||
|
||||
export function ensureIdentity(
|
||||
identity: IdentityContext['identity']
|
||||
): asserts identity is NonNullable<IdentityContext['identity']> {
|
||||
if (identity == null) throw new Error('Unauthorized.');
|
||||
}
|
||||
289
package-lock.json
generated
289
package-lock.json
generated
|
|
@ -28,6 +28,7 @@
|
|||
"bcrypt": "^5.1.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"graphql": "^16.9.0",
|
||||
|
|
@ -2512,9 +2513,9 @@
|
|||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz",
|
||||
"integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -2528,9 +2529,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz",
|
||||
"integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2544,9 +2545,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2560,9 +2561,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2576,9 +2577,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2592,9 +2593,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2608,9 +2609,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2624,9 +2625,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2640,9 +2641,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz",
|
||||
"integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
|
|
@ -2656,9 +2657,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2672,9 +2673,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz",
|
||||
"integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -2688,9 +2689,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz",
|
||||
"integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
|
|
@ -2704,9 +2705,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz",
|
||||
"integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
|
|
@ -2720,9 +2721,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz",
|
||||
"integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
|
|
@ -2736,9 +2737,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz",
|
||||
"integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
|
|
@ -2752,9 +2753,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz",
|
||||
"integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
|
|
@ -2768,9 +2769,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2784,9 +2785,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2800,9 +2801,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2816,9 +2817,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2832,9 +2833,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2848,9 +2849,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -2864,9 +2865,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz",
|
||||
"integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
|
|
@ -2880,9 +2881,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz",
|
||||
"integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
|
|
@ -2896,9 +2897,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz",
|
||||
"integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
|
|
@ -3712,13 +3713,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": {
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
|
||||
"integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz",
|
||||
"integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@whatwg-node/node-fetch": "^0.7.19",
|
||||
"@whatwg-node/node-fetch": "^0.7.21",
|
||||
"urlpattern-polyfill": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -3726,9 +3727,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
|
||||
"integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz",
|
||||
"integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4021,13 +4022,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": {
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
|
||||
"integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz",
|
||||
"integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@whatwg-node/node-fetch": "^0.7.19",
|
||||
"@whatwg-node/node-fetch": "^0.7.21",
|
||||
"urlpattern-polyfill": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -4035,9 +4036,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
|
||||
"integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz",
|
||||
"integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4222,13 +4223,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": {
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
|
||||
"integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz",
|
||||
"integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@whatwg-node/node-fetch": "^0.7.19",
|
||||
"@whatwg-node/node-fetch": "^0.7.21",
|
||||
"urlpattern-polyfill": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -4236,9 +4237,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
|
||||
"integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz",
|
||||
"integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4589,13 +4590,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": {
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
|
||||
"integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz",
|
||||
"integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@whatwg-node/node-fetch": "^0.7.19",
|
||||
"@whatwg-node/node-fetch": "^0.7.21",
|
||||
"urlpattern-polyfill": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -4603,9 +4604,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
|
||||
"integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz",
|
||||
"integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -4733,13 +4734,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": {
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.7.tgz",
|
||||
"integrity": "sha512-sL31zX8BqZovZc38ovBFmKEfao9AzZ/24sWSHKNhDhcnzIO/PYAX2xF6vYtgU9hinrEGlvScTTyKSMynHGdfEA==",
|
||||
"version": "0.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz",
|
||||
"integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@whatwg-node/node-fetch": "^0.7.19",
|
||||
"@whatwg-node/node-fetch": "^0.7.21",
|
||||
"urlpattern-polyfill": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -4747,9 +4748,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": {
|
||||
"version": "0.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.20.tgz",
|
||||
"integrity": "sha512-2LI5HkZcdAODb6umb7MUxkWYNUi3j6apgQvIzp+X3w3AVPdvVsshvjaMo4gwLeFtfs+cyMNOLTm9Hhm/TXq6DA==",
|
||||
"version": "0.7.21",
|
||||
"resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz",
|
||||
"integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
|
@ -8800,6 +8801,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debounce": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||
|
|
@ -9389,9 +9396,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.4",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz",
|
||||
"integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==",
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
|
@ -9401,31 +9408,31 @@
|
|||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.4",
|
||||
"@esbuild/android-arm": "0.25.4",
|
||||
"@esbuild/android-arm64": "0.25.4",
|
||||
"@esbuild/android-x64": "0.25.4",
|
||||
"@esbuild/darwin-arm64": "0.25.4",
|
||||
"@esbuild/darwin-x64": "0.25.4",
|
||||
"@esbuild/freebsd-arm64": "0.25.4",
|
||||
"@esbuild/freebsd-x64": "0.25.4",
|
||||
"@esbuild/linux-arm": "0.25.4",
|
||||
"@esbuild/linux-arm64": "0.25.4",
|
||||
"@esbuild/linux-ia32": "0.25.4",
|
||||
"@esbuild/linux-loong64": "0.25.4",
|
||||
"@esbuild/linux-mips64el": "0.25.4",
|
||||
"@esbuild/linux-ppc64": "0.25.4",
|
||||
"@esbuild/linux-riscv64": "0.25.4",
|
||||
"@esbuild/linux-s390x": "0.25.4",
|
||||
"@esbuild/linux-x64": "0.25.4",
|
||||
"@esbuild/netbsd-arm64": "0.25.4",
|
||||
"@esbuild/netbsd-x64": "0.25.4",
|
||||
"@esbuild/openbsd-arm64": "0.25.4",
|
||||
"@esbuild/openbsd-x64": "0.25.4",
|
||||
"@esbuild/sunos-x64": "0.25.4",
|
||||
"@esbuild/win32-arm64": "0.25.4",
|
||||
"@esbuild/win32-ia32": "0.25.4",
|
||||
"@esbuild/win32-x64": "0.25.4"
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild-register": {
|
||||
|
|
@ -11863,9 +11870,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz",
|
||||
"integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz",
|
||||
"integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
"bcrypt": "^5.1.1",
|
||||
"cookie-parser": "^1.4.7",
|
||||
"cors": "^2.8.5",
|
||||
"dayjs": "^1.11.13",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.21.2",
|
||||
"graphql": "^16.9.0",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue