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
8 changes: 8 additions & 0 deletions public/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
User-agent: *
Allow: /

# Sitemap
Sitemap: https://www.devpals.site/sitemap.xml

# Crawl-delay (선택사항)
Crawl-delay: 1
21 changes: 21 additions & 0 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.devpals.site/</loc>
<lastmod>2024-01-01</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://www.devpals.site/main</loc>
<lastmod>2024-01-01</lastmod>
<changefreq>daily</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://www.devpals.site</loc>
<lastmod>2024-01-01</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
</urlset>
5 changes: 3 additions & 2 deletions src/components/common/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import defaultImg from '../../../assets/defaultImg.png';
export interface AvatarProps {
size: string;
image: string | ReactNode;
alt?: string;
}

function Avatar({ size, image }: AvatarProps) {
function Avatar({ size, image, alt = '사용자 프로필 이미지' }: AvatarProps) {
const releasedImg =
typeof image === 'string' && image.trim() ? image : defaultImg;
return (
<S.AvatarContainer size={size}>
{typeof image === 'string' || !image ? (
<S.AvatarImg src={releasedImg} alt='Avatar' />
<S.AvatarImg src={releasedImg} alt={alt} />
) : (
image
)}
Expand Down
26 changes: 26 additions & 0 deletions src/hooks/useIsMobile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useState, useEffect } from 'react';

const useIsMobile = () => {
const [isMobile, setIsMobile] = useState(false);

useEffect(() => {
const checkIsMobile = () => {
setIsMobile(window.innerWidth <= 768);
};

// 초기 체크
checkIsMobile();

// 리사이즈 이벤트 리스너 추가
window.addEventListener('resize', checkIsMobile);

// 클린업 함수
return () => {
window.removeEventListener('resize', checkIsMobile);
};
}, []);

return isMobile;
};

export default useIsMobile;
2 changes: 1 addition & 1 deletion src/hooks/user/CommentHooks/usePostComment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const usePostComment = (id: number) => {
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [ProjectCommentList.projectComment, id],
exact: true,
exact: false,
});
},
onError: (error) => {
Expand Down
9 changes: 9 additions & 0 deletions src/pages/user/apply/Apply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import LoadingSpinner from '../../../components/common/loadingSpinner/LoadingSpi
import PhoneComponent from '../../../components/user/applyComponents/phoneComponent/PhoneComponent';
import CareersComponent from '../../../components/user/applyComponents/careersComponent/CareersComponent';
import Input from '../../../components/user/projectFormComponents/inputComponent/InputComponent';
import ApplyStep from './ApplyStep';
import useIsMobile from '../../../hooks/useIsMobile';

const Apply = () => {
const { projectId } = useParams();
Expand All @@ -23,6 +25,7 @@ const Apply = () => {
const { data: projectData, isLoading, isFetching } = useGetProjectData(id);
const { applyProject } = useApplyProject({ id, handleModalOpen });
const userEmail = useAuthStore((state) => state.userData?.email);
const isMobile = useIsMobile();

const {
handleSubmit: onSubmitHandler,
Expand Down Expand Up @@ -65,6 +68,12 @@ const Apply = () => {
if (isLoading) return <LoadingSpinner />;
if (isFetching) return <LoadingSpinner />;

// 모바일 환경이면 ApplyStep 컴포넌트 렌더링
if (isMobile) {
return <ApplyStep />;
}

// 데스크톱 환경이면 기존 Apply 컴포넌트 렌더링
return (
<S.Container>
<S.Title>프로젝트 지원</S.Title>
Expand Down
40 changes: 36 additions & 4 deletions src/pages/user/projectDetail/ProjectDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ const ProjectDetail = () => {
}
}, [data, handleModalOpen, isLoading, isFetching]);

// 간단한 메타 정보 설정
useEffect(() => {
if (data) {
document.title = `${data.title} - 프로젝트 상세`;

// 메타 설명 추가
const metaDescription = document.querySelector(
'meta[name="description"]'
);
if (metaDescription) {
metaDescription.setAttribute(
'content',
`${data.title} 프로젝트에 대한 상세 정보입니다.`
);
} else {
const newMetaDescription = document.createElement('meta');
newMetaDescription.name = 'description';
newMetaDescription.content = `${data.title} 프로젝트에 대한 상세 정보입니다.`;
document.head.appendChild(newMetaDescription);
}
}
}, [data]);

if (isLoading || isFetching) return <LoadingSpinner />;

if (!data) {
Expand Down Expand Up @@ -63,27 +86,33 @@ const ProjectDetail = () => {
<S.Title>{data.title}</S.Title>
<S.ProfileContainer>
<S.ProfileImageContainer onClick={handleMovetoUserPage}>
<Avatar size='2.5rem' image={data.user.img} />
<Avatar
size='2.5rem'
image={data.user.img}
alt={`${data.user.nickname}의 프로필 이미지`}
/>
</S.ProfileImageContainer>
<S.UserInfo>
<S.UserName onClick={handleMovetoUserPage}>
{data.user.nickname}
</S.UserName>
<S.PostDate>{formatDate(data.recruitmentEndDate)}</S.PostDate>
<S.ViewCount>
<EyeIcon />
<EyeIcon aria-label='조회수' />
{data.views}
</S.ViewCount>
</S.UserInfo>
</S.ProfileContainer>
</S.Header>

<S.Content>
<ProjectInformation data={data} />
<br></br>
<br />
<S.ProjectDescription>
<MarkdownEditorView description={data.description} />
</S.ProjectDescription>
</S.Content>

<S.ApplyButtonContainer>
{userData &&
userData.id !== data.user.id &&
Expand All @@ -99,14 +128,17 @@ const ProjectDetail = () => {
</Button>
) : null}
</S.ApplyButtonContainer>
<hr></hr>

<hr />

<CommentLayout
projectId={data.id}
createrId={data.user.id}
loginUserId={userData?.id}
/>
</S.Wrapper>
</S.Container>

<Modal
isOpen={isOpen}
onClose={handleModalClose}
Expand Down
2 changes: 1 addition & 1 deletion src/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const Inquiry = lazy(
);
const MyPage = lazy(() => import('../pages/user/mypage/MyPage'));
const UserPage = lazy(() => import('../pages/user/userpage/UserPage'));
const Apply = lazy(() => import('../pages/user/apply/ApplyStep'));
const Apply = lazy(() => import('../pages/user/apply/Apply'));
const CreateProject = lazy(
() => import('../pages/user/createProject/CreateProject')
);
Expand Down