350 lines
10 KiB
TypeScript
350 lines
10 KiB
TypeScript
import dayjs, { Dayjs } from 'dayjs'
|
|
import { useCallback, useState } from 'react'
|
|
import { updatePageMutation } from '../../../lib/networking/mutations/updatePageMutation'
|
|
import { ArticleAttributes } from '../../../lib/networking/library_items/useLibraryItems'
|
|
import { LibraryItem } from '../../../lib/networking/library_items/useLibraryItems'
|
|
import { showErrorToast, showSuccessToast } from '../../../lib/toastHelpers'
|
|
import { CloseButton } from '../../elements/CloseButton'
|
|
import { FormInput } from '../../elements/FormElements'
|
|
import { Box, HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
|
|
import {
|
|
ModalButtonBar,
|
|
ModalContent,
|
|
ModalOverlay,
|
|
ModalRoot,
|
|
} from '../../elements/ModalPrimitives'
|
|
import { StyledText } from '../../elements/StyledText'
|
|
import { StyledTextArea } from '../../elements/StyledTextArea'
|
|
type EditLibraryItemModalProps = {
|
|
onOpenChange: (open: boolean) => void
|
|
item: LibraryItem
|
|
updateItem: (item: LibraryItem) => Promise<void>
|
|
}
|
|
|
|
export function EditLibraryItemModal(
|
|
props: EditLibraryItemModalProps
|
|
): JSX.Element {
|
|
const onSave = useCallback(
|
|
(
|
|
title: string,
|
|
author: string | undefined,
|
|
description: string,
|
|
savedAt: Dayjs,
|
|
publishedAt: Dayjs | undefined
|
|
) => {
|
|
;(async () => {
|
|
if (title !== '') {
|
|
const res = await updatePageMutation({
|
|
pageId: props.item.node.id,
|
|
title,
|
|
description,
|
|
byline: author,
|
|
savedAt: savedAt.toISOString(),
|
|
publishedAt: publishedAt ? publishedAt.toISOString() : undefined,
|
|
})
|
|
|
|
if (res) {
|
|
await props.updateItem({
|
|
cursor: props.item.cursor,
|
|
node: {
|
|
...props.item.node,
|
|
title: title,
|
|
author: author,
|
|
description: description,
|
|
},
|
|
})
|
|
showSuccessToast('Link updated succesfully', {
|
|
position: 'bottom-right',
|
|
})
|
|
props.onOpenChange(false)
|
|
} else {
|
|
showErrorToast('There was an error updating your link', {
|
|
position: 'bottom-right',
|
|
})
|
|
}
|
|
} else {
|
|
showErrorToast('Title must be a non-empty value', {
|
|
position: 'bottom-right',
|
|
})
|
|
}
|
|
})()
|
|
},
|
|
[props]
|
|
)
|
|
|
|
return (
|
|
<EditItemModal
|
|
title={props.item.node.title}
|
|
author={props.item.node.author}
|
|
description={props.item.node.description}
|
|
savedAt={dayjs(props.item.node.savedAt)}
|
|
publishedAt={
|
|
props.item.node.publishedAt
|
|
? dayjs(props.item.node.publishedAt)
|
|
: undefined
|
|
}
|
|
onOpenChange={props.onOpenChange}
|
|
onSave={onSave}
|
|
/>
|
|
)
|
|
}
|
|
|
|
type EditArticleModalProps = {
|
|
onOpenChange: (open: boolean) => void
|
|
article: ArticleAttributes
|
|
updateArticle: (
|
|
title: string,
|
|
author: string | undefined,
|
|
description: string,
|
|
savedAt: string,
|
|
publishedAt: string | undefined
|
|
) => void
|
|
}
|
|
|
|
export function EditArticleModal(props: EditArticleModalProps): JSX.Element {
|
|
const onSave = useCallback(
|
|
(
|
|
title: string,
|
|
author: string | undefined,
|
|
description: string,
|
|
savedAt: Dayjs,
|
|
publishedAt: Dayjs | undefined
|
|
) => {
|
|
;(async () => {
|
|
if (title !== '') {
|
|
const res = await updatePageMutation({
|
|
pageId: props.article.id,
|
|
title,
|
|
description,
|
|
byline: author,
|
|
savedAt: savedAt.toISOString(),
|
|
publishedAt: publishedAt ? publishedAt.toISOString() : undefined,
|
|
})
|
|
if (res) {
|
|
props.updateArticle(
|
|
title,
|
|
author,
|
|
description,
|
|
savedAt.toISOString(),
|
|
publishedAt ? publishedAt.toISOString() : undefined
|
|
)
|
|
showSuccessToast('Link updated succesfully', {
|
|
position: 'bottom-right',
|
|
})
|
|
props.onOpenChange(false)
|
|
} else {
|
|
showErrorToast('There was an error updating your link', {
|
|
position: 'bottom-right',
|
|
})
|
|
}
|
|
} else {
|
|
showErrorToast('Title must be a non-empty value', {
|
|
position: 'bottom-right',
|
|
})
|
|
}
|
|
})()
|
|
},
|
|
[props]
|
|
)
|
|
|
|
return (
|
|
<EditItemModal
|
|
title={props.article.title}
|
|
author={props.article.author}
|
|
description={props.article.description || ''}
|
|
savedAt={dayjs(props.article.savedAt)}
|
|
publishedAt={
|
|
props.article.publishedAt ? dayjs(props.article.publishedAt) : undefined
|
|
}
|
|
onOpenChange={props.onOpenChange}
|
|
onSave={onSave}
|
|
/>
|
|
)
|
|
}
|
|
|
|
type EditItemModalProps = {
|
|
title: string
|
|
author: string | undefined
|
|
description: string
|
|
|
|
savedAt: Dayjs
|
|
publishedAt: Dayjs | undefined
|
|
onOpenChange: (open: boolean) => void
|
|
|
|
onSave: (
|
|
title: string,
|
|
author: string | undefined,
|
|
description: string,
|
|
savedAt: Dayjs,
|
|
publishedAt: Dayjs | undefined
|
|
) => void
|
|
}
|
|
|
|
function EditItemModal(props: EditItemModalProps): JSX.Element {
|
|
const [title, setTitle] = useState(props.title)
|
|
const [author, setAuthor] = useState(props.author)
|
|
const [savedAt, setSavedAt] = useState(props.savedAt)
|
|
const [publishedAt, setPublishedAt] = useState(props.publishedAt)
|
|
const [description, setDescription] = useState(props.description)
|
|
|
|
const titleStyle = {
|
|
mt: '22px',
|
|
mb: '2px',
|
|
fontFamily: '$display',
|
|
fontWeight: '600',
|
|
fontSize: '11px',
|
|
color: '#898989',
|
|
}
|
|
|
|
const inputStyle = {
|
|
mt: '1px',
|
|
borderRadius: '5px',
|
|
border: '1px solid $thBorderColor',
|
|
fontFamily: 'Inter',
|
|
fontWeight: '500',
|
|
fontSize: '16px',
|
|
height: '38px',
|
|
p: '5px',
|
|
color: '$thTextContrast2',
|
|
'&:focus': {
|
|
outline: 'none !important',
|
|
border: '1px solid $omnivoreCtaYellow',
|
|
},
|
|
}
|
|
|
|
return (
|
|
<ModalRoot
|
|
defaultOpen
|
|
modal={true}
|
|
onOpenChange={() => {
|
|
props.onOpenChange(false)
|
|
}}
|
|
css={{}}
|
|
>
|
|
<ModalOverlay />
|
|
<ModalContent
|
|
css={{ bg: '$grayBg', p: '20px', maxWidth: '480px' }}
|
|
onInteractOutside={(event) => {
|
|
event.preventDefault()
|
|
}}
|
|
onEscapeKeyDown={(event) => {
|
|
props.onOpenChange(false)
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
}}
|
|
>
|
|
<VStack distribution="start" css={{ p: '0px' }}>
|
|
<Header onOpenChange={props.onOpenChange} />
|
|
<Box css={{ width: '100%' }}>
|
|
<form
|
|
onSubmit={(event) => {
|
|
event.preventDefault()
|
|
props.onSave(title, author, description, savedAt, publishedAt)
|
|
}}
|
|
>
|
|
<HStack distribution="start" css={{ width: '100%' }}>
|
|
<VStack css={{ width: '45%' }}>
|
|
<StyledText css={titleStyle}>SAVED AT</StyledText>
|
|
<FormInput
|
|
type="datetime-local"
|
|
value={savedAt.format('YYYY-MM-DDTHH:mm')}
|
|
placeholder="Edit Date"
|
|
onChange={(event) => {
|
|
const dateStr = event.target.value
|
|
setSavedAt(dayjs(dateStr))
|
|
}}
|
|
css={{
|
|
...inputStyle,
|
|
fontSize: '14px',
|
|
textIndent: '0px',
|
|
}}
|
|
/>
|
|
</VStack>
|
|
<VStack css={{ width: '45%', marginLeft: 'auto' }}>
|
|
<StyledText css={titleStyle}>PUBLISHED AT</StyledText>
|
|
<FormInput
|
|
type="datetime-local"
|
|
value={
|
|
publishedAt
|
|
? publishedAt.format('YYYY-MM-DDTHH:mm')
|
|
: undefined
|
|
}
|
|
placeholder="Edit Published Date"
|
|
onChange={(event) => {
|
|
const dateStr = event.target.value
|
|
setPublishedAt(dayjs(dateStr))
|
|
}}
|
|
css={{
|
|
...inputStyle,
|
|
fontSize: '14px',
|
|
textIndent: '0px',
|
|
}}
|
|
/>
|
|
</VStack>
|
|
</HStack>
|
|
<StyledText css={titleStyle}>TITLE</StyledText>
|
|
<FormInput
|
|
type="text"
|
|
value={title}
|
|
autoFocus
|
|
placeholder="Edit Title"
|
|
onChange={(event) => setTitle(event.target.value)}
|
|
onFocus={(event) => {
|
|
event.target.select()
|
|
}}
|
|
css={inputStyle}
|
|
/>
|
|
<StyledText css={titleStyle}>AUTHOR</StyledText>
|
|
<FormInput
|
|
type="author"
|
|
value={author}
|
|
placeholder="Edit Author"
|
|
onChange={(event) => setAuthor(event.target.value)}
|
|
onFocus={(event) => {
|
|
event.target.select()
|
|
}}
|
|
css={inputStyle}
|
|
/>
|
|
<StyledText css={titleStyle}>DESCRIPTION</StyledText>
|
|
<StyledTextArea
|
|
css={{
|
|
...inputStyle,
|
|
mt: '2px',
|
|
width: '100%',
|
|
height: '120px',
|
|
}}
|
|
placeholder="Edit Description"
|
|
value={description}
|
|
onChange={(event) => setDescription(event.target.value)}
|
|
onFocus={(event) => {
|
|
event.target.select()
|
|
}}
|
|
maxLength={4000}
|
|
/>
|
|
<ModalButtonBar
|
|
onOpenChange={props.onOpenChange}
|
|
acceptButtonLabel="Save Changes"
|
|
/>
|
|
</form>
|
|
</Box>
|
|
</VStack>
|
|
</ModalContent>
|
|
</ModalRoot>
|
|
)
|
|
}
|
|
|
|
type HeaderProps = {
|
|
onOpenChange: (open: boolean) => void
|
|
}
|
|
|
|
function Header(props: HeaderProps): JSX.Element {
|
|
return (
|
|
<HStack distribution="start" alignment="center" css={{ width: '100%' }}>
|
|
<StyledText style="modalHeadline">Edit Title & Description</StyledText>
|
|
<SpanBox css={{ marginLeft: 'auto' }}>
|
|
<CloseButton close={() => props.onOpenChange(false)} />
|
|
</SpanBox>
|
|
</HStack>
|
|
)
|
|
}
|