Skip to content
Merged
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
2 changes: 1 addition & 1 deletion Mine/src/api/image.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { axiosInstance } from './axios'

export const uploadImage = async (file: File): Promise<string> => {
export const uploadImage = async (file: File): Promise<{ imageUrl: string }> => {
const formData = new FormData()
formData.append('file', file)
const res = await axiosInstance.post('api/images', formData)
Expand Down
5 changes: 5 additions & 0 deletions Mine/src/api/magazine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,9 @@ export const getMagazineFeed = async ({ cursorId, limit = 10 }: FeedDto): Promis
export const createMoodboard = async (magazineId: number) => {
const res = await axiosInstance.post(`api/magazines/${magazineId}/moodboards`)
return res.data
}

export const patchMagazineCover = async (id: number, coverImageUrl: string) => {
const res = await axiosInstance.patch(`api/magazines/${id}/cover`, { coverImageUrl })
return res.data
}
15 changes: 9 additions & 6 deletions Mine/src/components/common/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ export default function ConfirmModal({
}: ConfirmModalProps) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="absolute inset-0 bg-black/50" />
<div className="relative z-10 w-120 h-53 rounded-2xl bg-gray-500 px-8 py-8 flex flex-col justify-between shadow-[0_4px_8px_rgba(0,0,0,0.12),0_16px_32px_rgba(0,0,0,0.20)]">
{/* <div className="absolute inset-0" /> */}
<div className="relative z-10 w-120 h-53 rounded-2xl bg-gray-600-op70 px-8 py-8 flex flex-col justify-between shadow-[0_4px_8px_rgba(0,0,0,0.12),0_16px_32px_rgba(0,0,0,0.20)]">
<div>
<h2 className="mb-3 font-semibold24 text-white">{title}</h2>
{description && (
<p className="font-regular20 text-white/70 whitespace-pre-line" style={{ fontFeatureSettings: "'liga' off, 'clig' off" }}>
<p
className="font-regular20 text-white/70 whitespace-pre-line"
style={{ fontFeatureSettings: "'liga' off, 'clig' off" }}
>
{description}
</p>
)}
Expand All @@ -34,18 +37,18 @@ export default function ConfirmModal({
<button
onClick={onConfirm}
disabled={isLoading}
className="flex items-center justify-center w-25 h-12 rounded-lg border border-white/30 font-medium16 text-white hover:bg-white/10 disabled:opacity-50"
className="flex items-center justify-center w-25 h-12 rounded-lg border border-white/30 font-medium16 text-white hover:bg-gray-600-op70 disabled:opacity-50"
>
{isLoading ? '생성 중...' : confirmText}
</button>
<button
onClick={onCancel}
className="flex items-center justify-center w-25 h-12 rounded-lg border border-white/30 font-medium16 text-white hover:bg-white/10"
className="flex items-center justify-center w-25 h-12 rounded-lg border border-white/30 font-medium16 text-white hover:bg-gray-600-op70"
>
{cancelText}
</button>
</div>
</div>
</div>
)
}
}
13 changes: 8 additions & 5 deletions Mine/src/components/settings/ScreenSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import IconWandStars from '../../icon/wand_stars.svg?react'
import IconAddPhoto from '../../icon/add_photo_alternate.svg?react'
import useCreateMoodboard from '../../hooks/useCreateMoodboard'
import useUploadImage from '../../hooks/useUploadImage'
import usePatchMagazineCover from '../../hooks/usePatchMagazineCover'
import ConfirmModal from '../common/ConfirmModal'
import Toast from '../common/Toast'

Expand All @@ -15,6 +16,7 @@ export default function ScreenSettings() {
const fileInputRef = useRef<HTMLInputElement>(null)
const { mutateAsync: uploadImage } = useUploadImage()
const { mutateAsync: createMoodboard } = useCreateMoodboard()
const { mutateAsync: patchCover } = usePatchMagazineCover()

const handleOpenFile = () => {
fileInputRef.current?.click()
Expand All @@ -24,7 +26,8 @@ export default function ScreenSettings() {
const file = e.target.files?.[0]
if (!file) return
try {
await uploadImage(file)
const { imageUrl } = await uploadImage(file)
await patchCover({ id: Number(magazineId), coverImageUrl: imageUrl })
setShowToast(true)
setTimeout(() => setShowToast(false), 3000)
} catch (error) {
Expand Down Expand Up @@ -52,15 +55,15 @@ export default function ScreenSettings() {
<div className="w-136.5 h-36.5 flex gap-6.5">
<button
onClick={() => setIsConfirmOpen(true)}
className="flex flex-col w-65 h-full rounded-2xl border border-white/30 bg-white/10 items-center justify-center gap-3 text-white/70 transition-colors duration-150 hover:bg-white/20 hover:border-white/50 hover:text-white"
className="flex flex-col w-65 h-full rounded-2xl border border-gray-100-op40 bg-gray-500-op40 items-center justify-center gap-3 text-gray-100-op70 transition-colors duration-150 hover:bg-white/20 hover:border-gray-100-op40 hover:text-gray-100-op70"
>
<IconWandStars className="w-6 h-6 aspect-square **:stroke-current **:fill-current" />
<IconWandStars className="w-6 h-6 aspect-square **:fill-current" />
<span className="font-regular14">AI로 무드보드 생성하기</span>
</button>

<button
onClick={handleOpenFile}
className="flex flex-col w-65 h-full rounded-2xl border border-white/30 bg-white/10 items-center justify-center gap-3 text-white/70 transition-colors duration-150 hover:bg-white/20 hover:border-white/50 hover:text-white"
className="flex flex-col w-65 h-full rounded-2xl border border-gray-100-op40 bg-gray-500-op40 items-center justify-center gap-3 text-gray-100-op70 transition-colors duration-150 hover:bg-white/20 hover:border-gray-100-op40 hover:text-gray-100-op70"
>
<IconAddPhoto className="w-6 h-6 aspect-square **:stroke-current **:fill-current" />
<span className="font-regular14">컴퓨터에서 이미지 가져오기</span>
Expand All @@ -85,4 +88,4 @@ export default function ScreenSettings() {
)}
</>
)
}
}
24 changes: 24 additions & 0 deletions Mine/src/components/settings/ScreenSettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ScreenSettings from './ScreenSettings'

interface ScreenSettingsModalProps {
isOpen: boolean
onClose: () => void
}

export default function ScreenSettingsModal({ isOpen, onClose }: ScreenSettingsModalProps) {
if (!isOpen) return null

return (
<div
className="fixed inset-0 bg-black/40 flex items-center justify-center z-999"
onClick={onClose}
>
<div
className="relative bg-gray-600-op70 rounded-2xl p-10 shadow-[0px_4px_4px_rgba(0,0,0,0.25)]"
onClick={(e) => e.stopPropagation()}
>
<ScreenSettings />
</div>
</div>
)
}
15 changes: 2 additions & 13 deletions Mine/src/components/settings/SettingsModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useRef, useState } from 'react'
import ProfileSettings from './ProfileSettings'
import ScreenSettings from './ScreenSettings'
import InterestSettings from './InterestSettings'

import X from '../../icon/X.svg?react'
Expand All @@ -15,7 +14,7 @@ interface SettingsProps {
}

export default function SettingsModal({ onClose }: SettingsProps) {
const [activeTab, setActiveTab] = useState<'profile' | 'interest' | 'screen'>('profile')
const [activeTab, setActiveTab] = useState<'profile' | 'interest'>('profile')
const [editMode, setEditMode] = useState(false)
const [showLogoutToast, setShowLogoutToast] = useState(false)
const [showSaveToast, setShowSaveToast] = useState(false)
Expand Down Expand Up @@ -88,15 +87,6 @@ export default function SettingsModal({ onClose }: SettingsProps) {
>
관심사 설정
</button>
<button
onClick={() => {
setActiveTab('screen')
setEditMode(false)
}}
className={`text-left transition-all ${activeTab === 'screen' ? 'text-[20px] text-white font-semibold20' : 'text-[16px] text-white/50'}`}
>
화면 설정
</button>
</div>

<div className="flex-1 py-14 pr-10 overflow-hidden">
Expand All @@ -112,7 +102,6 @@ export default function SettingsModal({ onClose }: SettingsProps) {
{activeTab === 'interest' && (
<InterestSettings interests={selectedInterests} onChange={setSelectedInterests} />
)}
{activeTab === 'screen' && <ScreenSettings />}
</div>

{activeTab === 'profile' && (
Expand Down Expand Up @@ -168,4 +157,4 @@ export default function SettingsModal({ onClose }: SettingsProps) {
</div>
</div>
)
}
}
13 changes: 13 additions & 0 deletions Mine/src/hooks/usePatchMagazineCover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { patchMagazineCover } from '../api/magazine'

export default function usePatchMagazineCover() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ id, coverImageUrl }: { id: number; coverImageUrl: string }) =>
patchMagazineCover(id, coverImageUrl),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['magazineDetail'] })
},
})
}
6 changes: 3 additions & 3 deletions Mine/src/icon/wand_stars.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions Mine/src/pages/magazine/components/SectionContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import SectionIndexList from './SectionIndexList'
import ParagraphPart from './ParagraphPart'
import useSidebarStore from '../../../stores/sidebar'
import { useNavigate } from 'react-router-dom'
import { useState } from 'react'
import IconWandStars from '../../../icon/wand_stars.svg?react'
import ScreenSettingsModal from '../../../components/settings/ScreenSettingsModal'

interface SectionContentProps {
sectionId: number
Expand All @@ -18,10 +21,12 @@ export default function SectionContent({ sectionId, magazineId }: SectionContent
const { data, isLoading } = useGetSectionDetail(Number(magazineId), Number(sectionId))
const user = magazinedata?.user
const content = data?.paragraphs
const [isScreenSettingsOpen, setIsScreenSettingsOpen] = useState(false)

const handleClick = (magazineId: number) => {
navigate(`/magazine/${magazineId}`)
}

if (isLoading) {
return <></>
}
Expand Down Expand Up @@ -53,7 +58,16 @@ export default function SectionContent({ sectionId, magazineId }: SectionContent
/>
))}
</div>

<button
onClick={() => setIsScreenSettingsOpen(true)}
className="fixed bottom-4 right-4 z-50 transition-all duration-200 text-gray-100"
>
<IconWandStars className="w-6 h-6" />
</button>
</div>

<ScreenSettingsModal isOpen={isScreenSettingsOpen} onClose={() => setIsScreenSettingsOpen(false)} />
</div>
)
}
Loading