Skip to content

Commit

Permalink
#209 refactor: 게시글 작성일 프로퍼티명 수정, 파일 다운로드 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
pillow12360 committed Dec 23, 2024
1 parent 93c78ac commit 811e6f9
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 16 deletions.
1 change: 1 addition & 0 deletions frontend/src/config/apiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export const apiEndpoints = {
},
board: {
base: `${API_URL}/api/board`,
download: `${API_URL}/api/board/download`,
listWithPage: (page: number, size: number) =>
`${API_URL}/api/board?page=${page}&size=${size}`,
create: {
Expand Down
22 changes: 21 additions & 1 deletion frontend/src/pages/News/NoticeBoard/NoticeBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ export const CATEGORY_MAP = {
scholarship: '장학',
};

const formatDate = (dateString: string): string => {
const date = new Date(dateString);

// 유효한 날짜인지 확인
if (isNaN(date.getTime())) {
return dateString; // 유효하지 않은 날짜면 원본 문자열 반환
}

// 날짜를 YYYY-MM-DD 형식으로 변환
return date
.toLocaleDateString('ko-KR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
})
.replace(/\. /g, '-')
.replace('.', '');
};

const NoticeBoard: React.FC = () => {
const navigate = useNavigate();
const auth = useContext(AuthContext);
Expand Down Expand Up @@ -218,7 +237,8 @@ const NoticeBoard: React.FC = () => {
</S.TitleLink>
</S.Td>
<S.Td>{notice.writer}</S.Td>
<S.Td>{notice.createDate}</S.Td>
<S.Td>{formatDate(notice.createdDate)}</S.Td>{' '}
{/* 이 부분이 수정됨 */}
<S.Td>
{CATEGORY_MAP[notice.category as keyof typeof CATEGORY_MAP] ||
notice.category}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/News/NoticeBoard/NoticeCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ const NoticeCreate: React.FC = () => {
title: title.trim(),
content: content.trim(),
writer: auth.user || 'admin',
createDate: currentDate,
createdDate: currentDate,
category: category,
departmentId: 1,
}),
Expand Down
95 changes: 83 additions & 12 deletions frontend/src/pages/News/NoticeBoard/NoticeDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { AuthContext } from '../../../context/AuthContext';
import { Modal, useModal } from '../../../components/Modal';
import { AlertTriangle, CheckCircle } from 'lucide-react';
import 'react-quill/dist/quill.snow.css';

interface BoardDetail {
id: number;
title: string;
content: string;
writer: string;
createDate: string;
createdDate: string;
viewCount: number;
file?: string;
fileList?: string[];
category: string;
}

Expand All @@ -33,6 +34,9 @@ const NoticeDetail: React.FC = () => {
const auth = useContext(AuthContext);
const { openModal } = useModal();

const [downloadingFiles, setDownloadingFiles] = useState<Set<string>>(
new Set(),
);
const [post, setPost] = useState<BoardDetail | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
Expand All @@ -49,7 +53,6 @@ const NoticeDetail: React.FC = () => {
const response = await axios.get<BoardDetail>(
apiEndpoints.board.get(id),
);

setPost(response.data);
} catch (error: any) {
console.error('Failed to fetch post details:', error);
Expand All @@ -62,6 +65,64 @@ const NoticeDetail: React.FC = () => {
fetchPostDetail();
}, [id]);

const getFileName = (fileUrl: string): string => {
try {
const decodedUrl = decodeURIComponent(fileUrl);
const urlParts = decodedUrl.split('/');
return urlParts[urlParts.length - 1];
} catch {
return fileUrl;
}
};

const showErrorModal = (title: string, message: string) => {
openModal(
<>
<Modal.Header>
<AlertTriangle size={48} color="#E53E3E" />
{title}
</Modal.Header>
<Modal.Content>
<p>{message}</p>
</Modal.Content>
<Modal.Footer>
<Modal.CloseButton />
</Modal.Footer>
</>,
);
};

const handleFileDownload = async (fileUrl: string) => {
if (downloadingFiles.has(fileUrl)) return;

try {
setDownloadingFiles((prev) => new Set(prev).add(fileUrl));

const fileName = getFileName(fileUrl);

// 브라우저에서 직접 다운로드 처리
const link = document.createElement('a');
link.href = fileUrl; // S3 URL 직접 사용
link.setAttribute('download', fileName);
link.setAttribute('target', '_blank'); // 새 탭에서 열기 (필요한 경우)
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.error('파일 다운로드 실패:', error);
showErrorModal(
'파일 다운로드 실패',
'파일을 다운로드하는 중 오류가 발생했습니다.',
);
} finally {
setDownloadingFiles((prev) => {
const newSet = new Set(prev);
newSet.delete(fileUrl);
return newSet;
});
}
};

const showConfirmModal = () => {
openModal(
<>
Expand Down Expand Up @@ -152,7 +213,7 @@ const NoticeDetail: React.FC = () => {
</S.MetaItem>
<S.MetaItem>
<S.Label>작성일:</S.Label>
<span>{post.createDate}</span>
<span>{post.createdDate}</span>
</S.MetaItem>
<S.MetaItem>
<S.Label>조회수:</S.Label>
Expand Down Expand Up @@ -182,15 +243,25 @@ const NoticeDetail: React.FC = () => {
dangerouslySetInnerHTML={createMarkup(post.content)}
/>

{post.file && (
{post.fileList && post.fileList.length > 0 && (
<S.FileSection>
<S.FileLink
href={post.file}
target="_blank"
rel="noopener noreferrer"
>
📎 첨부파일 다운로드
</S.FileLink>
<S.FileListHeader>첨부파일 ({post.fileList.length})</S.FileListHeader>
<S.FileList>
{post.fileList.map((fileUrl, index) => (
<S.FileItem key={index}>
<S.FileIcon>📎</S.FileIcon>
<S.FileName>{getFileName(fileUrl)}</S.FileName>
<S.FileDownloadButton
onClick={() => handleFileDownload(fileUrl)}
disabled={downloadingFiles.has(fileUrl)}
>
{downloadingFiles.has(fileUrl)
? '다운로드 중...'
: '다운로드'}
</S.FileDownloadButton>
</S.FileItem>
))}
</S.FileList>
</S.FileSection>
)}

Expand Down
61 changes: 61 additions & 0 deletions frontend/src/pages/News/NoticeBoard/NoticeDetailStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,3 +320,64 @@ export const StatusMessage = styled.div<{ type: 'success' | 'error' | 'info' }>`
}
}}
`;

export const FileListHeader = styled.h3`
font-size: 1.1rem;
font-weight: 600;
color: #2d3748;
margin-bottom: 1rem;
`;

export const FileList = styled.div`
display: flex;
flex-direction: column;
gap: 0.75rem;
`;

export const FileItem = styled.div`
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem;
background-color: white;
border: 1px solid #e2e8f0;
border-radius: 6px;
transition: background-color 0.2s;
&:hover {
background-color: #f7fafc;
}
`;

export const FileIcon = styled.span`
font-size: 1.2rem;
`;

export const FileName = styled.span`
flex: 1;
font-size: 0.95rem;
color: #4a5568;
word-break: break-all;
`;

export const FileDownloadButton = styled.button`
padding: 0.5rem 1rem;
font-size: 0.9rem;
color: #3182ce;
background-color: transparent;
border: 1px solid #3182ce;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
&:hover {
background-color: #3182ce;
color: white;
}
${media.mobile} {
padding: 0.375rem 0.75rem;
font-size: 0.85rem;
}
`;
4 changes: 2 additions & 2 deletions frontend/src/pages/News/NoticeBoard/types/notice.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ export interface NoticeItem {
content: string;
viewCount: number;
writer: string;
createDate: string;
createdDate: string;
category: string;
file?: string;
file?: string[];
}

export type SortField =
Expand Down

0 comments on commit 811e6f9

Please sign in to comment.