Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M-6 Activity Create UI #12

Merged
merged 69 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
237380b
M-6 Activity Create page
SachiGoto Dec 3, 2023
aabcbb9
M-6 Activity Create Page
SachiGoto Dec 3, 2023
eb91e11
tailored the form to the figma design
SachiGoto Dec 5, 2023
f62e107
added zod validation and used form hook react for form
SachiGoto Dec 6, 2023
d1f92fb
added note
SachiGoto Dec 6, 2023
129db0c
Merge branch 'main' into activity-create
SachiGoto Dec 6, 2023
54dad88
fix lint error
SachiGoto Dec 6, 2023
cef4e86
updated based on feedback
SachiGoto Dec 12, 2023
5a08291
fix lint error
SachiGoto Dec 12, 2023
dc8d608
added customized day time picker
SachiGoto Dec 29, 2023
2aee48d
got rid of seconds
SachiGoto Dec 29, 2023
39fc7ef
added css style to have the calendar positioned at the bottom of each…
SachiGoto Dec 29, 2023
774986a
css adjustment
SachiGoto Dec 29, 2023
6ae4700
changed input focus color to primary color
SachiGoto Dec 29, 2023
765b074
commit
SachiGoto Jan 4, 2024
1f8125f
implemented signin
Yo-mah-Ya Dec 5, 2023
28657c1
rename folders
Yo-mah-Ya Dec 5, 2023
afadc75
fixed
Yo-mah-Ya Dec 5, 2023
7ce5437
fixed routing
Yo-mah-Ya Dec 7, 2023
b87a8bc
renamed action.ts
Yo-mah-Ya Dec 7, 2023
1b13037
added Username field to signup
Yo-mah-Ya Dec 8, 2023
10bfead
added build process to CI
Yo-mah-Ya Dec 8, 2023
0c560fd
rename variables
Yo-mah-Ya Dec 13, 2023
3d7c8b6
Add tripe details header
kanatagu Dec 17, 2023
6683985
Add roundIcon variant button
kanatagu Dec 17, 2023
ca25316
Add activity card
kanatagu Dec 19, 2023
1299b01
Deleted code and test picture
kanatagu Dec 19, 2023
94d63a0
Change to UUID
kanatagu Dec 20, 2023
4cecdda
Fix for reviews
kanatagu Dec 23, 2023
067a8f1
Fix type
kanatagu Dec 23, 2023
128fbc4
Add Todo comment for date format
kanatagu Dec 23, 2023
551d754
Enable UUID coming from user session usable everywhere
Yo-mah-Ya Dec 19, 2023
3da07b1
fix CI
Yo-mah-Ya Dec 19, 2023
7f83955
put all providers into providers folder
Yo-mah-Ya Dec 19, 2023
56d73d1
fix providers
Yo-mah-Ya Dec 21, 2023
a644b0b
fix how to append multiple cookies
Yo-mah-Ya Dec 21, 2023
fac749d
refactored middleware
Yo-mah-Ya Dec 22, 2023
2be24e0
format
SachiGoto Jan 4, 2024
edd889f
Revert "format"
SachiGoto Jan 4, 2024
0aa506e
Merge branch 'main' into activity-create
SachiGoto Jan 4, 2024
7b8b64a
format
SachiGoto Jan 4, 2024
07cad6f
fix type error
SachiGoto Jan 4, 2024
8d06e7a
adjusted a few things based on feedback
SachiGoto Jan 4, 2024
11fb113
Change Date
kanatagu Dec 23, 2023
8f213f6
Installed dependencies
kanatagu Dec 23, 2023
2596b9a
Set up test and fix tabs for different date
kanatagu Dec 24, 2023
512eabe
Add test for trip details tabs
kanatagu Dec 25, 2023
c44b7cf
Add test action
kanatagu Jan 5, 2024
a90971e
feat: Add Account layout and page components.
samuraikun Dec 2, 2023
8bb09d9
Add new GraphQL query to fetch user
samuraikun Dec 3, 2023
cf3e1de
chore: buid with turbopack
samuraikun Dec 8, 2023
c1ca210
fix: Refactor account page and remove loading component
samuraikun Dec 8, 2023
2495ae5
chore: cosmetic MenuItem color & fix npm script
samuraikun Dec 8, 2023
d815286
fix: fix UI layout based on Figma.
samuraikun Dec 22, 2023
b3de7f1
chore: disable underline when hover account link
samuraikun Dec 22, 2023
013d8ba
fix: access account page via session uuid using useUserUuid hook
samuraikun Jan 4, 2024
c5e56e8
Add margin bottom to account page & AlertButton props
samuraikun Jan 4, 2024
5826f5a
Fix style
kanatagu Jan 4, 2024
e366c06
chore: remove account/error.tsx
samuraikun Jan 4, 2024
0d7cbd1
Add password change link in account page.
samuraikun Jan 4, 2024
6a0a663
implemeneted signout
Yo-mah-Ya Dec 20, 2023
bffa2c7
changed cookie name of user UUID
Yo-mah-Ya Dec 29, 2023
19481d7
moved logout component to auth
Yo-mah-Ya Jan 5, 2024
dc61a00
replaced seiral based id with uuid using graphql-codegen
Yo-mah-Ya Jan 6, 2024
3407a71
replaced uuid with id
Yo-mah-Ya Jan 6, 2024
8dc5753
fixed query param
Yo-mah-Ya Jan 7, 2024
dfcb458
Merge branch 'main' into activity-create
SachiGoto Jan 9, 2024
105ac44
merge
SachiGoto Jan 11, 2024
2f3c1f7
Merge branch 'main' into activity-create
SachiGoto Jan 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions app/activity/create/components/form.tsx
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have made the title field required so that people can quickly add a new activity and add more information later.

I am unsure about how we are handling images. Are we storing images in the cloud and storing their CDN in the database? Currently, once images are added and the submit button is clicked, it stores image information like the example below:
Screenshot 2023-12-03 at 2 01 57 AM

Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useCallback, useState } from 'react'
import { useDropzone } from 'react-dropzone'
import { useForm } from 'react-hook-form'
import {
Box,
FormControl,
FormLabel,
FormErrorMessage,
Button,
Image,
SimpleGrid,
Text,
Flex
} from '@chakra-ui/react'
import { PrimaryButton } from '@/components/button'
import { CustomDateTimePicker } from '@/components/customDateTimePicker'
import { InputForm, TextareaForm } from '@/components/input'
import { createActivitySchema, createActivityResolver } from '../schema'

type ValuePiece = Date | null
type Value = ValuePiece | [ValuePiece, ValuePiece]

export const FormActivity = () => {
const [dateTo, setDateTo] = useState<Value>(null)
const [dateFrom, setDateFrom] = useState<Value>(null)
const [selectedImages, setSelectedImages] = useState<File[]>([])
const onDrop = useCallback((acceptedFiles: File[]) => {
setSelectedImages((prevImages) => [...prevImages, ...acceptedFiles])
}, [])
const { getRootProps, getInputProps } = useDropzone({ onDrop })
function removeImage(index: number) {
setSelectedImages((prevImages) => {
const newImages = [...prevImages]
newImages.splice(index, 1)
return newImages
})
}

const {
register,
handleSubmit,
formState: { errors }
} = useForm<createActivitySchema>({
resolver: createActivityResolver
})

const createHandler = handleSubmit(async (data: createActivitySchema) => {
console.log(data)
// TODO append 'images', 'date from', 'date to' to formData and send to backend
})

console.log('date to', dateTo, 'date from', dateFrom)

return (
<Box as="form" onSubmit={createHandler} pt={{ base: '40px', md: '40px' }}>
<FormControl isRequired isInvalid={!!errors.title}>
<FormLabel>Title</FormLabel>
<InputForm
type="text"
placeholder="Asakusa Temple"
{...register('title')}
/>
{errors.title && (
<FormErrorMessage>{errors.title.message}</FormErrorMessage>
)}
</FormControl>

<FormControl mt={{ base: '30px', md: '40px' }}>
<FormLabel>Time From</FormLabel>
<CustomDateTimePicker onChange={setDateFrom} value={dateFrom} />
</FormControl>

<FormControl mt={{ base: '30px', md: '40px' }}>
<FormLabel>Time To</FormLabel>
<CustomDateTimePicker onChange={setDateTo} value={dateTo} />
</FormControl>

<FormControl
mt={{ base: '30px', md: '40px' }}
isInvalid={!!errors.address}
>
<FormLabel>Address</FormLabel>
<InputForm
{...register('address')}
type="text"
placeholder="10-10 Shibuya, Tokyo, Japan"
/>
{errors.address && (
<FormErrorMessage>{errors.address.message}</FormErrorMessage>
)}
</FormControl>

<FormControl isInvalid={!!errors.url} mt={{ base: '30px', md: '40px' }}>
<FormLabel>URL</FormLabel>
<InputForm
{...register('url')}
type="url"
placeholder="https://www.google.com"
/>
{errors.url && (
<FormErrorMessage>{errors.url.message}</FormErrorMessage>
)}
</FormControl>

<Text
mt={{ base: '30px', md: '40px' }}
mb={selectedImages.length !== 0 ? { base: '30px', md: '40px' } : '8px'}
fontWeight="medium"
>
Image
</Text>
<SimpleGrid columns={{ base: 2, md: 3 }} spacing={4}>
{selectedImages.map((image, index) => (
<Image
key={index}
src={URL.createObjectURL(image)}
alt={`Selected Image ${image.name}`}
objectFit="cover"
width={{ base: '160px', md: '180px' }}
height={{ base: '106px', md: '120px' }}
margin="auto"
onClick={() => removeImage(index)}
/>
))}
</SimpleGrid>

<Box
{...getRootProps()}
textAlign="center"
mt={{ base: 0, md: selectedImages.length !== 0 ? '40px' : '0' }}
>
<PrimaryButton variant="outline">
<input {...getInputProps()} />
Add Image
</PrimaryButton>
</Box>

<FormControl isInvalid={!!errors.memo} mt={{ base: '30px', md: '40px' }}>
<FormLabel>Memo</FormLabel>
<TextareaForm
{...register('memo')}
name="memo"
placeholder="Type here..."
/>
{errors.memo && (
<FormErrorMessage>{errors.memo.message}</FormErrorMessage>
)}
</FormControl>

<FormControl isInvalid={!!errors.cost} mt={{ base: '30px', md: '40px' }}>
<FormLabel>Cost</FormLabel>
<InputForm {...register('cost')} type="text" placeholder="$200" />
{errors.cost && (
<FormErrorMessage>{errors.cost.message}</FormErrorMessage>
)}
</FormControl>

<Flex justifyContent="center" mt={{ base: '30px', md: '40px' }}>
<Button colorScheme="teal" type="submit" margin="auto">
Create Activity
</Button>
</Flex>
</Box>
)
}
1 change: 1 addition & 0 deletions app/activity/create/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FormActivity } from './form'
28 changes: 28 additions & 0 deletions app/activity/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client'

import { Box, Container, Heading, useColorModeValue } from '@chakra-ui/react'
import { Header, Footer } from '@/components/navigation'
import { FormActivity } from './components'

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

return (
<>
<Box as="main" minH="100vh" bg={bg} color={color}>
<Header />
<Container
pt={{ base: '20px', md: '40px' }}
pb={{ base: '40px', md: '70px' }}
>
<Heading as={'h1'} fontSize={{ base: '2xl', md: '4xl' }}>
Create Activity
</Heading>
<FormActivity />
</Container>
<Footer />
</Box>
</>
)
}
17 changes: 17 additions & 0 deletions app/activity/create/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { zodResolver } from '@hookform/resolvers/zod'
import * as z from 'zod'

// TODO: refactor zod validation

const createActivitySchema = z.object({
title: z.string().min(1).max(20).optional(),
timeFrom: z.string().optional(),
timeTo: z.string().optional(),
address: z.string().min(0).max(50).optional(),
url: z.union([z.string().url().nullish(), z.literal('')]),
memo: z.string().min(0).max(300).optional(),
cost: z.string().min(0).max(15).optional()
})

export type createActivitySchema = z.infer<typeof createActivitySchema>
export const createActivityResolver = zodResolver(createActivitySchema)
116 changes: 116 additions & 0 deletions app/components/customDateTimePicker/dateTimePickerWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { DateTimePicker as ReactDateTimePicker } from 'react-datetime-picker'
import { useColorMode } from '@chakra-ui/react'
import { MdCalendarToday, MdClose } from 'react-icons/md'
import 'react-clock/dist/Clock.css'
import 'react-datetime-picker/dist/DateTimePicker.css'
import 'react-calendar/dist/Calendar.css'

type ValuePiece = Date | null
type Value = ValuePiece | [ValuePiece, ValuePiece]

export const CustomDateTimePicker = ({
onChange,
value
}: {
onChange: (value: Value) => void
value: Value
}) => {
const { colorMode } = useColorMode()

return (
<>
<style>{`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For darkmode, the background color is too white when you hover?
Screenshot 2024-01-04 at 12 51 40 PM

And if you can, I think it's good to place under the input so that the user can see the input at the same time. like this!
Screenshot 2024-01-04 at 12 52 48 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screen.Recording.2024-01-04.at.16.34.59.mov

It's fixed :)


.react-calendar{
margin-top: 12%;
}
.react-calendar__tile--now {
background-color: ${colorMode === 'dark' ? '#2D3748' : '#CBD5E0'};
}

.react-calendar__tile--active:enabled:hover {
background-color: ${colorMode === 'dark' && '#086F83'};
}

.react-calendar__navigation__label:focus {
color: ${colorMode === 'dark' && 'black'};
}

.react-calendar__tile:enabled:hover {
color: ${colorMode === 'dark' && 'black'};
}

.react-calendar__navigation__arrow:focus,
.react-calendar__navigation__arrow:hover,
.react-calendar__navigation__label__labelText:hover {
color: ${colorMode === 'dark' && 'black'};
}

.react-calendar {
background-color: ${colorMode === 'dark' && '#1a202c'};
}

.react-datetime-picker {
width: 100%;
height: 2.5rem;
}

.react-datetime-picker__wrapper {
background-color: ${colorMode === 'dark' ? '#2d3748' : '#f8f8f8'};
border: 1px solid ${colorMode === 'dark' ? '#718096' : '#CBD5E0'};
border-radius: 0.375rem;
}

.react-calendar__tile--active {
background-color: #086F83;
}

.react-calendar__tile--hasActive,
.react-calendar__tile--hasActive:enabled:hover {
background: ${colorMode === 'dark' ? 'white' : '#0987a0'};
color: ${colorMode === 'dark' ? 'black' : 'white'};
}

.react-calendar__navigation button:disabled {
background: ${colorMode === 'dark' ? 'white' : '#0987a0'};
}

.react-calendar__navigation button:enabled:hover {
background: ${colorMode === 'dark' ? 'white' : '#0987a0'};
color: ${colorMode === 'dark' ? 'black' : 'white'};
}

.react-datetime-picker__inputGroup__input:invalid {
background: ${colorMode === 'dark' ? '#2d3748' : '#f8f8f8'};
}

.react-calendar__tile--now:enabled:hover {
background-color: ${colorMode === 'dark' ? '#CBD5E0' : '#EDF2F7'};
}

.react-datetime-picker__wrapper{
position:relative;
}

.react-datetime-picker__calendar--open {
inset:0 !important;
}

.react-datetime-picker__inputGroup{
padding:0 16px;
}

`}</style>

<div style={{ position: 'relative' }}>
<ReactDateTimePicker
onChange={onChange}
value={value}
calendarIcon={<MdCalendarToday />}
clearIcon={<MdClose />}
format={'y-MM-dd h:mm a'}
/>
</div>
</>
)
}
1 change: 1 addition & 0 deletions app/components/customDateTimePicker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CustomDateTimePicker } from './dateTimePickerWrapper'
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { StoryObj, Meta } from '@storybook/react'
import { CustomDateTimePicker } from '../index' // Adjust the import path accordingly

const meta: Meta<typeof CustomDateTimePicker> = {
title: 'CustomDateTimePicker',
component: CustomDateTimePicker
}

export default meta

type Story = StoryObj<typeof CustomDateTimePicker>

export const Default: Story = {
args: {
onChange: (value) => console.log(value),
value: new Date()
}
}
2 changes: 2 additions & 0 deletions app/components/input/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { InputSearch } from './input-search'
export { InputForm } from './input-form'
export { TextareaForm } from './textarea-form'
30 changes: 30 additions & 0 deletions app/components/input/input-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
Input as ChakraFormInput,
InputProps as ChakraInputProps,
InputGroup,
forwardRef,
useColorModeValue
} from '@chakra-ui/react'

export const InputForm = forwardRef(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you also add to storybook as this is the UI for everywhere you can use? Textarea is same!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a storybook for the custom date time picker, input form, text area as well as input search!
It's rather simple. Let me know what you think!

(props: ChakraInputProps, ref: React.Ref<HTMLInputElement>) => {
const bgColor = useColorModeValue('white', 'gray.700')
const borderColor = useColorModeValue('gray.300', 'gray.500')
const placeholdercolor = useColorModeValue('gray.400', 'gray.600')

return (
<InputGroup minW={{ base: '100%', md: '23.75rem' }}>
<ChakraFormInput
{...props}
ref={ref}
focusBorderColor={'primary.600'}
borderColor={borderColor}
bgColor={bgColor}
_placeholder={{
color: placeholdercolor
}}
/>
</InputGroup>
)
}
)
Loading