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

Component/file attachment #13

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 9 additions & 3 deletions src/components/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { useState } from 'react'
import toast from 'react-hot-toast'

import type { FormColumn } from '@/types'
Expand All @@ -9,6 +10,8 @@ interface CheckboxProps {
}

export const Checkbox = ({ column }: CheckboxProps): JSX.Element => {
const [isValid, setIsValid] = useState(true)

return (
<div className='flex w-full flex-col items-start justify-start'>
<label className='relative flex w-full cursor-pointer select-none flex-row-reverse items-center justify-end'>
Expand All @@ -20,12 +23,15 @@ export const Checkbox = ({ column }: CheckboxProps): JSX.Element => {
className='size-4 rounded-md'
onInvalid={(e) => {
e.preventDefault()
setIsValid(false)
toast.error('กรุณายอมรับข้อตกลง', { id: 'invalid' })
}}
/>
<p className='absolute -bottom-10 my-2 hidden w-full text-body-2 italic text-red-600'>
จำเป็นต้องยอมรับ
</p>
{!isValid ? (
<p className='absolute -bottom-10 my-2 w-full text-body-2 italic text-red-600'>
จำเป็นต้องยอมรับ
</p>
) : null}
</label>
</div>
)
Expand Down
53 changes: 53 additions & 0 deletions src/components/file-attachment.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'use client'

import { useState } from 'react'
import toast from 'react-hot-toast'

interface FileAttachmentProps {
name?: string
required?: boolean
acceptedTypes?: string
maxSizeMB?: number
}

export const FileAttachment = ({
name,
required,
acceptedTypes = '',
maxSizeMB = 5,
}: FileAttachmentProps): JSX.Element => {
const [selectedFile, setSelectedFile] = useState<File | null>(null)

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) return

if (file.size > maxSizeMB * 1024 * 1024) {
toast.error(`File size must be less than ${maxSizeMB}MB`)
setSelectedFile(null)
return
}

setSelectedFile(file)
}

return (
<div className='relative flex w-full flex-col'>
<div className='flex h-40 w-full items-center justify-center rounded-md border'>
<div className='inline-flex items-center justify-center'>
<span className='text-detail text-neutral-400'>
{selectedFile ? selectedFile.name : 'Select File'}
</span>
</div>
<input
name={name}
required={required}
className='absolute inset-0 opacity-0'
accept={acceptedTypes}
type='file'
onChange={handleFileChange}
/>
</div>
</div>
)
}
10 changes: 9 additions & 1 deletion src/components/form/form-component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { Checkbox, Input, SingleSelect, Textarea } from '@/components'
import {
Checkbox,
FileAttachment,
Input,
SingleSelect,
Textarea,
} from '@/components'

import type { FormColumn } from '@/types'

Expand All @@ -25,6 +31,8 @@ export const FormComponent = (column: FormColumn): React.ReactNode => {
return <SingleSelect column={column} />
case 'LongText':
return <Textarea column={column} />
case 'Attachment':
return <FileAttachment name={column.name} required={column.required} />
default:
return (
<p className='text-body-2 font-light italic text-red-600'>
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './checkbox'
export * from './input'
export * from './single-select'
export * from './textarea'
export * from './file-attachment'
22 changes: 11 additions & 11 deletions src/components/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface InputProps {
description?: string
}

export function Input({
export const Input = ({
id,
name,
type,
Expand All @@ -31,12 +31,12 @@ export function Input({
className,
disabled,
description,
}: InputProps) {
const [isValid, setIsValid] = useState(false)
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setIsValid(false)
}: InputProps) => {
const [isValid, setIsValid] = useState(true)
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIsValid(true)
if (onChange) {
onChange(event)
onChange(e)
}
}

Expand All @@ -54,14 +54,14 @@ export function Input({
placeholder={placeholder || 'คำตอบ'}
onInvalid={(e) => {
e.preventDefault()
setIsValid(true)
setIsValid(false)
toast.error('กรุณากรอกคำตอบให้ครบ', { id: 'invalid' })
}}
className={cn(
'flex w-full rounded-md border border-default border-neutral-200 px-2 py-1 outline-none transition-all duration-200 focus:border',
{
'border-red-600': isValid,
'focus:border-neutral-600': !isValid,
'border-red-600': !isValid,
'focus:border-neutral-600': isValid,
},
className
)}
Expand All @@ -71,11 +71,11 @@ export function Input({
{description}
</p>
</div>
{isValid && (
{!isValid ? (
<p className='absolute -bottom-8 w-full text-body-2 italic text-red-600'>
จำเป็นต้องตอบคำถามนี้
</p>
)}
) : null}
</div>
)
}
15 changes: 11 additions & 4 deletions src/components/single-select.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
'use client'

import { useState } from 'react'
import toast from 'react-hot-toast'

import type { FormColumn } from '@/types'
Expand All @@ -7,6 +10,8 @@ interface SingleSelectProps {
}

export const SingleSelect = ({ column }: SingleSelectProps): JSX.Element => {
const [isValid, setIsValid] = useState(true)

return (
<div className='relative flex w-full'>
<select
Expand All @@ -16,7 +21,7 @@ export const SingleSelect = ({ column }: SingleSelectProps): JSX.Element => {
defaultValue=''
onInvalid={(e) => {
e.preventDefault()

setIsValid(false)
toast.error('กรุณากรอกคำตอบให้ครบ', { id: 'invalid' })
}}
>
Expand All @@ -29,9 +34,11 @@ export const SingleSelect = ({ column }: SingleSelectProps): JSX.Element => {
</option>
))}
</select>
<p className='absolute -bottom-10 my-2 hidden w-full text-body-2 italic text-red-600'>
จำเป็นต้องเลือก
</p>
{!isValid ? (
<p className='absolute -bottom-10 my-2 w-full text-body-2 italic text-red-600'>
จำเป็นต้องเลือก
</p>
) : null}
</div>
)
}