Compare commits
2 Commits
93c00298e3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f319f2d772 | |||
| bc8dc79295 |
@@ -1,59 +1,88 @@
|
|||||||
.wrapper {
|
.wrapper {
|
||||||
@apply w-full max-w-md mx-auto p-4;
|
display: flex;
|
||||||
}
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
.dropzone {
|
flex-direction: column;
|
||||||
@apply border-2 rounded-lg p-4 flex flex-col items-center justify-center transition-colors duration-150;
|
min-height: 100vh;
|
||||||
}
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
.normal {
|
|
||||||
@apply border-dashed border-gray-300 bg-white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragging {
|
|
||||||
@apply border-indigo-400 bg-indigo-50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fileInput {
|
.fileInput {
|
||||||
@apply hidden;
|
display: none;
|
||||||
}
|
|
||||||
|
|
||||||
.previewBox {
|
|
||||||
@apply w-32 h-32 rounded-md overflow-hidden bg-gray-100 flex items-center justify-center mb-3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.previewImg {
|
|
||||||
@apply object-cover w-full h-full cursor-pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
@apply text-gray-400 text-center text-sm px-2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
@apply w-full flex gap-2 items-center;
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textInput {
|
.textInput {
|
||||||
@apply flex-1 px-3 py-2 border rounded h-10 cursor-pointer bg-white text-gray-700 focus:outline-none focus:ring-2 focus:ring-indigo-500;
|
flex: 1;
|
||||||
|
height: 2.75rem;
|
||||||
|
padding: 0.65rem 0.75rem;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 0.55rem;
|
||||||
|
background-color: white;
|
||||||
|
color: #334155;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textInput:focus {
|
||||||
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
|
||||||
|
border-color: #2563eb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
@apply px-4 py-2 rounded text-white;
|
height: 2.75rem;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
border-radius: 0.55rem;
|
||||||
|
background-color: #6366F1;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #4F46E5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btnLoading {
|
.btnLoading {
|
||||||
@apply bg-indigo-300;
|
background-color: #A5B4FC;
|
||||||
|
cursor: wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btnPrimary {
|
.messageArea {
|
||||||
@apply bg-indigo-600 hover:bg-indigo-700;
|
width: 100%;
|
||||||
|
max-width: 520px;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.errorMsg {
|
.errorMsg {
|
||||||
@apply mt-3 text-sm text-red-600;
|
margin: 0.25rem 0 0;
|
||||||
|
color: #dc2626;
|
||||||
}
|
}
|
||||||
|
|
||||||
.successMsg {
|
.successMsg {
|
||||||
@apply mt-3 text-sm text-green-600;
|
margin: 0.25rem 0 0;
|
||||||
|
color: #16a34a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewContainer {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.previewImage {
|
||||||
|
width: 200px;
|
||||||
|
height: 120px;
|
||||||
|
border-radius: 0.55rem;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ const ImgInputForm = () => {
|
|||||||
const fileRef = useRef<HTMLInputElement | null>(null)
|
const fileRef = useRef<HTMLInputElement | null>(null)
|
||||||
const [fileName, setFileName] = useState('')
|
const [fileName, setFileName] = useState('')
|
||||||
const [preview, setPreview] = useState<string | null>(null)
|
const [preview, setPreview] = useState<string | null>(null)
|
||||||
|
const [showPreview, setShowPreview] = useState(false)
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [success, setSuccess] = useState<string | null>(null)
|
const [success, setSuccess] = useState<string | null>(null)
|
||||||
|
|
||||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:9001'
|
//process.env.NEXT_PUBLIC_API_URL ||
|
||||||
|
const API_BASE = 'http://localhost:9001'
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkAuth = async () => {
|
const checkAuth = async () => {
|
||||||
@@ -48,6 +50,7 @@ const ImgInputForm = () => {
|
|||||||
|
|
||||||
setFileName(file.name)
|
setFileName(file.name)
|
||||||
setPreview(URL.createObjectURL(file))
|
setPreview(URL.createObjectURL(file))
|
||||||
|
setShowPreview(false)
|
||||||
// keep the selected file in the hidden input
|
// keep the selected file in the hidden input
|
||||||
if (fileRef.current) {
|
if (fileRef.current) {
|
||||||
const dataTransfer = new DataTransfer()
|
const dataTransfer = new DataTransfer()
|
||||||
@@ -93,6 +96,7 @@ const ImgInputForm = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
setShowPreview(false)
|
||||||
try {
|
try {
|
||||||
const fd = new FormData()
|
const fd = new FormData()
|
||||||
fd.append('image', file)
|
fd.append('image', file)
|
||||||
@@ -103,6 +107,7 @@ const ImgInputForm = () => {
|
|||||||
})
|
})
|
||||||
if (!res.ok) throw new Error('업로드 실패')
|
if (!res.ok) throw new Error('업로드 실패')
|
||||||
setSuccess('업로드 성공')
|
setSuccess('업로드 성공')
|
||||||
|
setShowPreview(true)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError((err as Error).message || '업로드 중 오류가 발생했습니다')
|
setError((err as Error).message || '업로드 중 오류가 발생했습니다')
|
||||||
} finally {
|
} finally {
|
||||||
@@ -111,13 +116,8 @@ const ImgInputForm = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<>
|
||||||
<div
|
<div className={styles.wrapper}>
|
||||||
className={`${styles.dropzone} ${isDragging ? styles.dragging : styles.normal}`}
|
|
||||||
onDrop={onDrop}
|
|
||||||
onDragOver={onDragOver}
|
|
||||||
onDragLeave={onDragLeave}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
ref={fileRef}
|
ref={fileRef}
|
||||||
type="file"
|
type="file"
|
||||||
@@ -126,18 +126,11 @@ const ImgInputForm = () => {
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={styles.previewBox}>
|
{preview && (
|
||||||
{preview ? (
|
<div className={styles.previewContainer}>
|
||||||
// preview kept modest in size
|
<img src={preview} alt="selected preview" className={styles.previewImage} />
|
||||||
// eslint-disable-next-line @next/next/no-img-element
|
</div>
|
||||||
<img src={preview} alt="preview" className={styles.previewImg} onClick={onClickPick} />
|
)}
|
||||||
) : (
|
|
||||||
<div className={styles.placeholder}>
|
|
||||||
이미지를 끌어다 놓거나<br />
|
|
||||||
클릭해 선택하세요
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
<input
|
<input
|
||||||
@@ -150,17 +143,17 @@ const ImgInputForm = () => {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={upload}
|
onClick={upload}
|
||||||
disabled={loading}
|
disabled={loading || !fileName}
|
||||||
className={`${styles.btn} ${loading ? styles.btnLoading : styles.btnPrimary}`}
|
className={`${styles.btn} ${loading ? styles.btnLoading : styles.btnPrimary}`}
|
||||||
>
|
>
|
||||||
{loading ? '업로드 중...' : '업로드'}
|
{loading ? '업로드 중...' : '업로드'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{error && <p className={styles.errorMsg}>{error}</p>}
|
{error && <p className={`${styles.errorMsg}`}>{error}</p>}
|
||||||
{success && <p className={styles.successMsg}>{success}</p>}
|
{success && <p className={styles.successMsg}>{success}</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user