diff --git a/frontend/package.json b/frontend/package.json index 4c85972f9..69afd5f3e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,6 +5,8 @@ "dependencies": { "axios": "^0.21.1", "classnames": "^2.3.1", + "github-markdown-css": "^5.6.1", + "highlight.js": "^11.10.0", "react": "^18.2.0", "react-datepicker": "^4.2.1", "react-dom": "^18.2.0", diff --git a/frontend/src/App.js b/frontend/src/App.js index 70d51931b..181094fa5 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -23,6 +23,7 @@ import RecruitmentProvider from "./provider/RecruitmentProvider"; import TokenProvider from "./provider/TokenProvider"; import MemberInfoProvider from "./provider/MemberInfoProvider"; import { ModalProvider } from "./hooks/useModalContext"; +import MissionView from "./pages/MissionView/MissionView"; const App = () => { return ( @@ -45,6 +46,7 @@ const App = () => { } /> } /> } /> + } /> }> diff --git a/frontend/src/api/recruitments.ts b/frontend/src/api/recruitments.ts index 1a0b6ae7f..2b0955af7 100644 --- a/frontend/src/api/recruitments.ts +++ b/frontend/src/api/recruitments.ts @@ -59,6 +59,11 @@ export type PostJudgmentRequest = RequestWithToken<{ export type PostJudgmentResponseData = Mission["judgment"]; +export type FetchMissionRequest = RequestWithToken<{ + recruitmentId: string; + missionId: number; +}>; + export const fetchRecruitmentItems = (recruitmentId: FetchRecruitmentItemsRequest) => axios.get(`/api/recruitments/${recruitmentId}/items`); @@ -117,3 +122,13 @@ export const patchAssignment = ({ assignmentData, headers({ token }) ); + +export const fetchMissionRequirements = ({ + token, + recruitmentId, + missionId, +}: FetchAssignmentRequest) => + axios.get( + `/api/recruitments/${recruitmentId}/missions/${missionId}/me`, + headers({ token }) + ); diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css b/frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css index 6505a7ecc..a3f35c5df 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/ApplicationButtonStyles.module.css @@ -1,3 +1,7 @@ +.assignment-button { + background-color: var(--blue); +} + .refresh-button { background-color: var(--blue-gray-500); } @@ -11,6 +15,7 @@ } @media screen and (max-width: 800px) { + .assignment-button, .judgment-button, .apply-button, .refresh-button { diff --git a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx index 4d7d334ee..ad0e75c4e 100644 --- a/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx +++ b/frontend/src/components/MyApplicationItem/MyMissionItem/MyMissionItem.tsx @@ -41,6 +41,15 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { ); }; + const routeToMissionView = () => { + navigate( + generatePath(PATH.MISSION_VIEW, { + recruitmentId, + missionId: String(mission.id), + }) + ); + }; + return (
@@ -49,6 +58,15 @@ const MyMissionItem = ({ mission, recruitmentId }: MyMissionItemProps) => { {missionItem.title}
    +
  • + +
  • +
  • +
  • + +
  • +
+ + ); +}; + +export default MissionView; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 456f6c90d..9c5391b28 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8422,6 +8422,11 @@ giget@^1.0.0: pathe "^1.1.0" tar "^6.1.13" +github-markdown-css@^5.6.1: + version "5.6.1" + resolved "https://registry.yarnpkg.com/github-markdown-css/-/github-markdown-css-5.6.1.tgz#8ca3d5c3d93d79ea429fddafea091347ab374f78" + integrity sha512-DItLFgHd+s7HQmk63YN4/TdvLeRqk1QP7pPKTTPrDTYoI5x7f/luJWSOZxesmuxBI2srHp8RDyoZd+9WF+WK8Q== + github-slugger@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" @@ -8660,6 +8665,11 @@ headers-polyfill@^3.1.0: resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.1.1.tgz#798e265f80edfc53fb8fd01e8963b356b12c2514" integrity sha512-ifvvIC+VDeLTEkJDwxECSI7k9rJF7sJavCh/UfhGsgJ+LXMbGILRf+NXhc4znuf+JA5X2/leQdOXk6Lq6Y2/wQ== +highlight.js@^11.10.0: + version "11.10.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92" + integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ== + history@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" diff --git a/src/main/kotlin/apply/application/MyMissionService.kt b/src/main/kotlin/apply/application/MyMissionService.kt index a5ebc59bb..833e8f784 100644 --- a/src/main/kotlin/apply/application/MyMissionService.kt +++ b/src/main/kotlin/apply/application/MyMissionService.kt @@ -45,7 +45,7 @@ class MyMissionService( private fun findMissions(memberId: Long, recruitmentId: Long): List { val evaluationIds = evaluationRepository.findAllByRecruitmentId(recruitmentId).map { it.id } val targets = evaluationTargetRepository.findAllByMemberIdAndEvaluationIdIn(memberId, evaluationIds) - return missionRepository.findAllByEvaluationIdIn(targets.map { it.id }).filterNot { it.hidden } + return missionRepository.findAllByEvaluationIdIn(targets.map { it.evaluationId }).filterNot { it.hidden } } private fun List.mapBy(assignments: List): List { diff --git a/src/main/kotlin/apply/config/DatabaseInitializer.kt b/src/main/kotlin/apply/config/DatabaseInitializer.kt index a72914b1d..bc59ff477 100644 --- a/src/main/kotlin/apply/config/DatabaseInitializer.kt +++ b/src/main/kotlin/apply/config/DatabaseInitializer.kt @@ -420,6 +420,48 @@ class DatabaseInitializer( |- 미션은 **기능 요구 사항, 프로그래밍 요구 사항, 과제 진행 요구 사항** 세 가지로 구성되어 있다. |- 세 개의 요구 사항을 만족하기 위해 노력한다. 특히 기능을 구현하기 전에 기능 목록을 만들고, 기능 단위로 커밋 하는 방식으로 진행한다. |- 기능 요구 사항에 기재되지 않은 내용은 스스로 판단하여 구현한다. + | + |--- + | + |## 🎯 프로그래밍 요구 사항 + | + |### 라이브러리 + | + |- `camp.nextstep.edu.missionutils`에서 제공하는 `Randoms` 및 `Console` API를 사용하여 구현해야 한다. + | - Random 값 추출은 `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 활용한다. + | - 사용자가 입력하는 값은 `camp.nextstep.edu.missionutils.Console`의 `readLine()`을 활용한다. + | + |#### 사용 예시 + | + |```java + |List computer = new ArrayList<>(); + |while (computer.size() < 3) { + | int randomNumber = Randoms.pickNumberInRange(1, 9); + | if (!computer.contains(randomNumber)) { + | computer.add(randomNumber); + | } + |} + |``` + | + |```javascript + |const computer = []; + |while (computer.length < 3) { + | const number = MissionUtils.Random.pickNumberInRange(1, 9); + | if (!computer.includes(number)) { + | computer.push(number); + | } + |} + |``` + | + |```kotlin + |val computer = mutableListOf() + |while (computer.size() < 3) { + | val randomNumber = Randoms.pickNumberInRange(1, 9) + | if (!computer.contains(randomNumber)) { + | computer.add(randomNumber) + | } + |} + |``` """.trimMargin(), evaluationId = 2L, startDateTime = createLocalDateTime(2020, 11, 24, 15), diff --git a/src/test/kotlin/support/MarkdownTest.kt b/src/test/kotlin/support/MarkdownTest.kt index 041663941..e7b8817f9 100644 --- a/src/test/kotlin/support/MarkdownTest.kt +++ b/src/test/kotlin/support/MarkdownTest.kt @@ -2,6 +2,8 @@ package support import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldContainInOrder import io.kotest.matchers.string.shouldNotContain class MarkdownTest : StringSpec({ @@ -61,4 +63,26 @@ class MarkdownTest : StringSpec({ val actual = markdownToEmbeddedHtml(markdownText) actual shouldNotContain "" } + + "코드 포맷을 사용하는 경우 태그가 추가된다" { + val markdownText = """ + |`buildSrc` + """.trimMargin() + val actual = markdownToEmbeddedHtml(markdownText) + actual shouldContain "buildSrc" + } + + "프로그래밍 언어를 지정하면 태그에 클래스 속성이 지정된다" { + val markdownText = """ + |```kotlin + |class Cat { + | fun purr() { + | println("Purr purr") + | } + |} + |``` + """.trimMargin() + val actual = markdownToEmbeddedHtml(markdownText) + actual.shouldContainInOrder("
", "", "", "
") + } })