Skip to content

Commit

Permalink
Add create trip form
Browse files Browse the repository at this point in the history
  • Loading branch information
kanatagu committed Jan 30, 2024
1 parent 7f8142f commit 6d3dda1
Show file tree
Hide file tree
Showing 15 changed files with 344 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'react-calendar/dist/Calendar.css'
type ValuePiece = Date | null
type Value = ValuePiece | [ValuePiece, ValuePiece]

// TODO Delete this and use react date picker for time as well
export const CustomDateTimePicker = ({
onChange,
value
Expand All @@ -20,7 +21,7 @@ export const CustomDateTimePicker = ({
return (
<>
<style>{`
.react-calendar{
margin-top: 12%;
}
Expand Down
20 changes: 20 additions & 0 deletions app/components/date/stories/custom-date-picker.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useState } from 'react'
import { Meta } from '@storybook/react'
import { CustomDatePicker } from '@/components/date'

const meta: Meta<typeof CustomDatePicker> = {
title: 'CustomDatePicker',
component: CustomDatePicker
}
export default meta

export const Default = () => {
const [selectedDate, setSelectedDate] = useState<Date | null>(new Date())
return (
<CustomDatePicker
selectedDate={selectedDate}
onChange={(date) => setSelectedDate(date)}
placeholderText="Select Date"
/>
)
}
58 changes: 33 additions & 25 deletions app/trip/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default function TripEditPage({ params }: { params: { id: string } }) {

const userId = useUserId()

// Trip Details
const {
data: tripData,
loading: tripLoading,
Expand All @@ -36,23 +37,10 @@ export default function TripEditPage({ params }: { params: { id: string } }) {
}
const tripDetailsRefetchLoading = tripNetWorkStatus === NetworkStatus.refetch

const {
data: tagsData,
loading: tagsLoading,
refetch: tagsRefetch,
networkStatus: tagsNetWorkStatus
} = useTagsCollectionQuery({
variables: { userId },
notifyOnNetworkStatusChange: true
})

const tagsCollectionRefetch = () => {
tagsRefetch()
}
const tagsRefetchLoading = tagsNetWorkStatus === NetworkStatus.refetch

// Trip Tags
const {
data: tripTagsData,
loading: tripTagsLoading,
refetch: tripTagsRefetch,
networkStatus: tripTagsNetWorkStatus
} = useTripTagsCollectionQuery({
Expand All @@ -64,6 +52,7 @@ export default function TripEditPage({ params }: { params: { id: string } }) {
notifyOnNetworkStatusChange: true
})

// Tags
const tripsTagsDataArray = tripTagsData?.trip_tagsCollection?.edges.map(
(tag) => ({
id: tag.node.id,
Expand All @@ -76,7 +65,26 @@ export default function TripEditPage({ params }: { params: { id: string } }) {
}
const tripTagsRefetchLoading = tripTagsNetWorkStatus === NetworkStatus.refetch

if ((!tripData && !tripLoading) || (!tagsData && !tagsLoading))
const {
data: tagsData,
loading: tagsLoading,
refetch: tagsRefetch,
networkStatus: tagsNetWorkStatus
} = useTagsCollectionQuery({
variables: { userId },
notifyOnNetworkStatusChange: true
})

const tagsCollectionRefetch = () => {
tagsRefetch()
}
const tagsRefetchLoading = tagsNetWorkStatus === NetworkStatus.refetch

if (
(!tripData && !tripLoading) ||
(!tripTagsData && !tripTagsLoading) ||
(!tagsData && !tagsLoading)
)
throw new Error('No trip data found')

return (
Expand Down Expand Up @@ -107,15 +115,6 @@ export default function TripEditPage({ params }: { params: { id: string } }) {
refetch: tripDetailsRefetch,
refetchLoading: tripDetailsRefetchLoading
}}
tags={{
data:
tagsData?.tagsCollection?.edges.map((tag) => ({
id: tag.node.id,
name: tag.node.name
})) || [],
refetch: tagsCollectionRefetch,
refetchLoading: tagsRefetchLoading
}}
tripTags={{
data:
tripTagsData?.trip_tagsCollection?.edges.map((tripTag) => ({
Expand All @@ -126,6 +125,15 @@ export default function TripEditPage({ params }: { params: { id: string } }) {
refetch: tripTagsCollectionRefetch,
refetchLoading: tripTagsRefetchLoading
}}
tags={{
data:
tagsData?.tagsCollection?.edges.map((tag) => ({
id: tag.node.id,
name: tag.node.name
})) || [],
refetch: tagsCollectionRefetch,
refetchLoading: tagsRefetchLoading
}}
/>
)}
</Box>
Expand Down
116 changes: 79 additions & 37 deletions app/trip/components/trip-form.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useForm, Controller } from 'react-hook-form'
import {
FormControl,
FormLabel,
Expand All @@ -16,9 +17,10 @@ import { PrimaryButton, SecondaryButton } from '@/components/button'
import { CustomDatePicker } from '@/components/date'
import { InputForm } from '@/components/input'
import { Loading } from '@/components/loading'

import { getDateObj } from '@/libs/utils'
import { TagFormModal } from '../components'
import { useTripUpdate, useTripTagCreateDelete } from '../hooks'
import { useTripCreate, useTripUpdate, useTripTagCreateDelete } from '../hooks'
import { TripSchema, tripSchemaResolver } from '../schema'

export type TripDetailsArgs = {
id: string
Expand All @@ -32,22 +34,22 @@ export type TripDetailsArgs = {
refetchLoading: boolean
}

export type TagsArgs = {
data: { id: string; name: string }[]
export type TripTagsArgs = {
data: { id: string; tag_id: string; trip_id: string }[]
refetch: () => void
refetchLoading: boolean
}

export type TripTagsArgs = {
data: { id: string; tag_id: string; trip_id: string }[]
export type TagsArgs = {
data: { id: string; name: string }[]
refetch: () => void
refetchLoading: boolean
}

type TripFormProps = {
tripDetails: TripDetailsArgs
tripDetails?: TripDetailsArgs
tripTags?: TripTagsArgs
tags: TagsArgs
tripTags: TripTagsArgs
}

export const TripForm = ({ tripDetails, tags, tripTags }: TripFormProps) => {
Expand All @@ -57,15 +59,45 @@ export const TripForm = ({ tripDetails, tags, tripTags }: TripFormProps) => {
)
const { isOpen, onOpen, onClose } = useDisclosure()

const { onMutate, isTripUpdating, register, control, errors, Controller } =
useTripUpdate(tripDetails)
const { createTrip, isTripCreating } = useTripCreate()

const { updateTrip, isTripUpdating } = useTripUpdate(tripTags, tripDetails)

const { tagClickHandler, isTripTagCreating, isTripTagDeleting } =
useTripTagCreateDelete(tripTags, tripDetails.id)
const { isTripTagCreating, isTripTagDeleting } = useTripTagCreateDelete(
String(tripDetails?.id),
tripTags
)

const {
register,
handleSubmit,
control,
formState: { errors }
} = useForm<TripSchema>({
defaultValues: {
title: tripDetails?.title,
date_from: tripDetails?.dateFrom
? getDateObj(tripDetails.dateFrom)
: undefined,
date_to: tripDetails?.dateTo ? getDateObj(tripDetails.dateTo) : null,
image_storage_object_id: tripDetails?.image || null,
selectedTags: tripTags ? tripTags.data.map((tag) => tag.tag_id) : [],
cost: tripDetails?.cost ? tripDetails.cost.toString() : '',
cost_unit: tripDetails?.costUnit
},
resolver: tripSchemaResolver
})

const mutateFun = tripDetails ? updateTrip : createTrip
const isMutating = isTripCreating || isTripUpdating

return (
<>
<VStack as="form" gap={{ base: '30px', md: '40px' }} onSubmit={onMutate}>
<VStack
as="form"
gap={{ base: '30px', md: '40px' }}
onSubmit={handleSubmit(mutateFun)}
>
<FormControl isInvalid={!!errors.title}>
<FormLabel>Title</FormLabel>
<InputForm
Expand Down Expand Up @@ -115,44 +147,54 @@ export const TripForm = ({ tripDetails, tags, tripTags }: TripFormProps) => {
<FormControl isInvalid={!!errors.image_storage_object_id}>
<FormLabel>Image</FormLabel>
<HStack gap={{ base: '20px', md: '34px' }}>
<Image alt="" src={tripDetails.image || imageSrc} width="50%" />
<Image alt="" src={tripDetails?.image || imageSrc} width="50%" />
<PrimaryButton variant="outline">Select Image </PrimaryButton>
</HStack>
<FormErrorMessage>
{errors?.image_storage_object_id?.message}
</FormErrorMessage>
</FormControl>

<Flex justify="start" flexDir="column" width="100%">
<FormControl isInvalid={!!errors?.selectedTags}>
<FormLabel>Tag</FormLabel>

<CheckboxGroup defaultValue={tripTags.data.map((tag) => tag.tag_id)}>
<Flex columnGap={'20px'} rowGap={'10px'} flexWrap={'wrap'}>
{tags.refetchLoading ? (
<Loading p="4px" size="md" />
) : (
<>
{tags.data.map((tag) => (
<Checkbox
key={tag.id}
value={tag.id}
isDisabled={isTripTagCreating || isTripTagDeleting}
onChange={() => tagClickHandler(tag.id)}
>
{tag.name}
</Checkbox>
))}
</>
)}
</Flex>
</CheckboxGroup>
<Controller
name="selectedTags"
control={control}
render={({ field: { onChange } }) => (
<CheckboxGroup
defaultValue={tripTags?.data.map((tag) => tag.tag_id)}
onChange={onChange}
>
<Flex columnGap={'20px'} rowGap={'10px'} flexWrap={'wrap'}>
{tags.refetchLoading ? (
<Loading p="4px" size="md" />
) : (
<>
{tags.data.map((tag) => (
<Checkbox
key={tag.id}
value={tag.id}
isDisabled={isTripTagCreating || isTripTagDeleting}
>
{tag.name}
</Checkbox>
))}
</>
)}
</Flex>
</CheckboxGroup>
)}
/>

<FormErrorMessage>{errors?.selectedTags?.message}</FormErrorMessage>

<Box mt="20px">
<SecondaryButton variant={'outline'} size="sm" onClick={onOpen}>
Manage Tags
</SecondaryButton>
</Box>
</Flex>
</FormControl>

<Flex gap="20px" justify={'start'} width="100%">
<FormControl w={'180px'} isInvalid={!!errors.cost}>
Expand All @@ -178,7 +220,7 @@ export const TripForm = ({ tripDetails, tags, tripTags }: TripFormProps) => {
</FormControl>
</Flex>

<PrimaryButton size="lg" type="submit" isDisabled={isTripUpdating}>
<PrimaryButton size="lg" type="submit" isDisabled={isMutating}>
Save Trip
</PrimaryButton>
</VStack>
Expand Down
35 changes: 34 additions & 1 deletion app/trip/create/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
'use client'

import { NetworkStatus } from '@apollo/client'
import { Heading, Box, Container, useColorModeValue } from '@chakra-ui/react'
import { Header, Footer } from '@/components/navigation'
import { useUserId } from '@/providers/session-provider'
import { TripForm } from '../components'
import { useTagsCollectionQuery } from '@generated/api'

export default function CreateTripPage() {
const bg = useColorModeValue('white', 'gray.800')
const color = useColorModeValue('black', 'gray.300')

const userId = useUserId()

const {
data: tagsData,
loading: tagsLoading,
refetch: tagsRefetch,
networkStatus: tagsNetWorkStatus
} = useTagsCollectionQuery({
variables: { userId },
notifyOnNetworkStatusChange: true
})

const tagsCollectionRefetch = () => {
tagsRefetch()
}
const tagsRefetchLoading = tagsNetWorkStatus === NetworkStatus.refetch

if (!tagsData && !tagsLoading) throw new Error('No tags data found')

return (
<>
<Header />
Expand All @@ -20,7 +43,17 @@ export default function CreateTripPage() {
Create Trip
</Heading>
<Box mt="40px">
<TripForm />
<TripForm
tags={{
data:
tagsData?.tagsCollection?.edges.map((tag) => ({
id: tag.node.id,
name: tag.node.name
})) || [],
refetch: tagsCollectionRefetch,
refetchLoading: tagsRefetchLoading
}}
/>
</Box>
</Container>
</Box>
Expand Down
Loading

0 comments on commit 6d3dda1

Please sign in to comment.