Skip to content

Commit

Permalink
Merge pull request #169 from GDSC-Hongik/dev
Browse files Browse the repository at this point in the history
[Deploy] 2차 mvp main 배포
  • Loading branch information
SeieunYoo authored Jan 5, 2025
2 parents ce85322 + ed5171a commit 32c68f8
Show file tree
Hide file tree
Showing 93 changed files with 2,154 additions and 331 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @ghdtjgus76 @eugene028 @hamo-o @SeieunYoo
* @eugene028 @hamo-o @SeieunYoo @soulchicken
129 changes: 48 additions & 81 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,48 @@
# Turborepo starter

This is an official starter Turborepo.

## Using this example

Run the following command:

```sh
npx create-turbo@latest
```

## What's inside?

This Turborepo includes the following packages/apps:

### Apps and Packages

- `docs`: a [Next.js](https://nextjs.org/) app
- `web`: another [Next.js](https://nextjs.org/) app
- `@repo/ui`: a stub React component library shared by both `web` and `docs` applications
- `@repo/eslint-config`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
- `@repo/typescript-config`: `tsconfig.json`s used throughout the monorepo

Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).

### Utilities

This Turborepo has some additional tools already setup for you:

- [TypeScript](https://www.typescriptlang.org/) for static type checking
- [ESLint](https://eslint.org/) for code linting
- [Prettier](https://prettier.io) for code formatting

### Build

To build all apps and packages, run the following command:

```
cd my-turborepo
pnpm build
```

### Develop

To develop all apps and packages, run the following command:

```
cd my-turborepo
pnpm dev
```

### Remote Caching

Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.

By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands:

```
cd my-turborepo
npx turbo login
```

This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).

Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your Turborepo:

```
npx turbo link
```

## Useful Links

Learn more about the power of Turborepo:

- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)
# wow-class

GDSC Hongik 스터디 서비스, 와우 클래스

![image](https://github.com/user-attachments/assets/2557aa14-4593-4cb0-a323-57dd8f1a5cd5)

## Link

- [client 서비스](https://study.gdschongik.com/)
- [admin 서비스](https://mentor.study.gdschongik.com/)

## 서비스 소개

### 클라이언트 서비스(학생용)

| 나의 스터디 | 나의 과제 | 수강신청 |
|-------------------------------------------|-------------------------------------------|-------------------------------------------|
| <img src="https://github.com/user-attachments/assets/646ff757-f10b-439e-b941-b19cb965861d" width="1000"/> | <img src="https://github.com/user-attachments/assets/571f9539-727c-4a02-84cc-bf86ca1f209c" width="1000"/> | <img src="https://github.com/user-attachments/assets/d13e020b-fa7c-4dc2-b187-2ad7a5cd4a09" width="1000"/> |
| 스터디 과제 및 출석 상황을 한눈에 확인할 수 있습니다. | 제출해야 할 과제 목록과 과제 히스토리를 확인합니다. | 수강신청한 스터디를 확인할 수 있습니다. |

### 어드민 서비스(멘토용)

| 개설된 스터디 페이지 | 스터디 개설 페이지 | 스터디 정보 확인 페이지 |
|-------------------------------------------|-------------------------------------------|-------------------------------------------|
| <img src="https://github.com/user-attachments/assets/d33493d0-f598-4183-87bc-b4ef55d6e175" width="1000"/> | <img src="https://github.com/user-attachments/assets/d4984e5a-90e1-464c-8b7f-4345c484401e" width="1000"/> | <img src="https://github.com/user-attachments/assets/aabfe146-aa06-440b-aa2c-1e2c7d4872ef" width="1000" height='200'/> |
| 현재 개설되어 있는 스터디 목록을 확인할 수 있습니다. | 새 스터디를 개설하고 세부 정보를 입력하는 페이지입니다. | 스터디의 상세 정보를 확인할 수 있습니다. |


## 기술 스택
<div align="left">
<div>
<img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white">
<img src="https://img.shields.io/badge/React-61DAFB?style=flat-square&logo=react&logoColor=black">
<img src="https://img.shields.io/badge/Next-000000?style=flat-square&logo=Next.js&logoColor=white">
<img src="https://img.shields.io/badge/Storybook-FF4785?style=flat-square&logo=storybook&logoColor=white">
</div>
<div>
<img src="https://img.shields.io/badge/Turbo-333333?style=flat-square&logo=turbo&logoColor=white"/>
<img src="https://img.shields.io/badge/Panda CSS-EF7C8E?style=flat-square&logo=pandacss&logoColor=white"/>
<img src="https://img.shields.io/badge/Jotai-00C7B7?style=flat-square&logo=react&logoColor=white">
</div>
<div>
<img src="https://img.shields.io/badge/ESlint-4B32C3?style=flat-square&logo=eslint&logoColor=white">
<img src="https://img.shields.io/badge/Prettier-F7B93E?style=flat-square&logo=prettier&logoColor=white">
</div>
</div>


28 changes: 28 additions & 0 deletions apps/admin/apis/study/studyAchievementApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { fetcher } from "@wow-class/utils";
import type { OutstandingStudentApiRequestDto } from "types/dtos/outstandingStudent";

const studyAchievementApi = {
postStudyAchievement: async (
studyId: number,
data: OutstandingStudentApiRequestDto
) => {
const response = await fetcher.post(
`/mentor/study-achievements?studyId=${studyId}`,
data
);
return { success: response.ok };
},

deleteStudyAchievement: async (
studyId: number,
data: OutstandingStudentApiRequestDto
) => {
const response = await fetcher.delete(
`/mentor/study-achievements?studyId=${studyId}`,
data
);
return { success: response.ok };
},
};

export default studyAchievementApi;
32 changes: 29 additions & 3 deletions apps/admin/apis/study/studyApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import type {
import type { AttendanceApiResponseDto } from "types/dtos/attendance";
import type { CurriculumApiResponseDto } from "types/dtos/curriculumList";
import type { StudyBasicInfoApiResponseDto } from "types/dtos/studyBasicInfo";
import type { StudyStudentApiResponseDto } from "types/dtos/studyStudent";
import type { StudyStatisticsApiResponseDto } from "types/dtos/studyStatistics";
import type {
PaginatedStudyStudentResponseDto,
StudyStudentApiResponseDto,
} from "types/dtos/studyStudent";
import type { PageableType } from "types/entities/page";
import type { StudyAnnouncementType } from "types/entities/study";

import type { StudyListApiResponseDto } from "../../types/dtos/studyList";
Expand Down Expand Up @@ -149,12 +154,33 @@ export const studyApi = {
);
return response.data;
},
getStudyStudents: async (studyId: number) => {
const response = await fetcher.get<StudyStudentApiResponseDto[]>(
getStudyStudents: async (studyId: number, pageable: PageableType) => {
const response = await fetcher.get<PaginatedStudyStudentResponseDto>(
`/mentor/studies/${studyId}/students`,
{
next: { tags: [tags.students] },
cache: "force-cache",
},
pageable
);
return response.data;
},
getStudyStudentsExcel: async (studyId: number) => {
const response = await fetcher.get(
`/mentor/studies/${studyId}/students/excel`,
{
next: { tags: [tags.studentsExcel] },
cache: "force-cache",
}
);

return response.data;
},
getStudyStatistics: async (studyId: number) => {
const response = await fetcher.get<StudyStatisticsApiResponseDto>(
`/mentor/study-details/statistics?studyId=${studyId}`,
{
next: { tags: [tags.statistics] },
}
);
return response.data;
Expand Down
19 changes: 19 additions & 0 deletions apps/admin/apis/study/studyCompleteApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fetcher } from "@wow-class/utils";
import type { StudyCompleteRequestDto } from "types/dtos/studyComplete";

const studyCompleteApi = {
postStudyComplete: async (data: StudyCompleteRequestDto) => {
const response = await fetcher.post(`/mentor/study-history/complete`, data);
return { success: response.ok };
},

postStudyCompleteWithdraw: async (data: StudyCompleteRequestDto) => {
const response = await fetcher.post(
`/mentor/study-history/withdraw-completion`,
data
);
return { success: response.ok };
},
};

export default studyCompleteApi;
6 changes: 0 additions & 6 deletions apps/admin/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ const RootLayout = ({
return (
<html lang="ko">
<body>
<ToastContainer
hideProgressBar
autoClose={4000}
closeButton={false}
limit={1}
/>
<JotaiProvider>
<ToastContainer
hideProgressBar
Expand Down
7 changes: 7 additions & 0 deletions apps/admin/app/students/@modal/(.)status/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import StudentStatusModal from "../_components/StudentStatusModal";

const StudentStatusModalPage = () => {
return <StudentStatusModal />;
};

export default StudentStatusModalPage;
55 changes: 55 additions & 0 deletions apps/admin/app/students/@modal/_components/CompleteModalButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client";

import studyCompleteApi from "apis/study/studyCompleteApi";
import { useAtomValue } from "jotai";
import Button from "wowds-ui/Button";

import {
enabledOutstandingStudentsAtom,
outstandingStudentsAtom,
selectedStudentsAtom,
studyAtom,
} from "@/students/_contexts/StudyProvider";

import useCloseStudentStatusModal from "../_hooks/useCloseStudentStatusModal";

const CompleteModalButton = () => {
const study = useAtomValue(studyAtom);
const { enabled } = useAtomValue(enabledOutstandingStudentsAtom);
const { type } = useAtomValue(outstandingStudentsAtom);
const { students } = useAtomValue(selectedStudentsAtom);

const {
handleClickCloseModal,
closeModalWithSuccess,
closeModalWithFailure,
} = useCloseStudentStatusModal();

const handleClickComplete = async () => {
if (!study || !type) return;

const apiMap = {
처리: studyCompleteApi.postStudyComplete,
철회: studyCompleteApi.postStudyCompleteWithdraw,
};

const fetchApi = () => {
const fetch = apiMap[type];
return fetch({
studyId: study.studyId,
studentIds: Array.from(students),
});
};
const result = await fetchApi();
if (result.success) closeModalWithSuccess();
else closeModalWithFailure();
};

return enabled ? (
<Button onClick={handleClickComplete}>선택한 회원 수료 {type}</Button>
) : (
<Button onClick={handleClickCloseModal}>확인하기</Button>
);
};

export default CompleteModalButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"use client";

import studyAchievementApi from "apis/study/studyAchievementApi";
import { useAtomValue } from "jotai";
import type { AchievementType } from "types/entities/achievement";
import Button from "wowds-ui/Button";

import {
enabledOutstandingStudentsAtom,
outstandingStudentsAtom,
selectedStudentsAtom,
studyAtom,
} from "@/students/_contexts/StudyProvider";

import useCloseStudentStatusModal from "../_hooks/useCloseStudentStatusModal";

const OutstandingModalButton = () => {
const study = useAtomValue(studyAtom);
const { enabled } = useAtomValue(enabledOutstandingStudentsAtom);
const { type, achievement } = useAtomValue(outstandingStudentsAtom);
const { students } = useAtomValue(selectedStudentsAtom);

const {
handleClickCloseModal,
closeModalWithSuccess,
closeModalWithFailure,
} = useCloseStudentStatusModal();

const handleClickOutstanding = async () => {
if (!study || !achievement || !type) return;

const apiMap = {
처리: studyAchievementApi.postStudyAchievement,
철회: studyAchievementApi.deleteStudyAchievement,
};

const fetchApi = () => {
const fetch = apiMap[type];
return fetch(study.studyId, {
studentIds: Array.from(students),
achievementType: achievement as AchievementType,
});
};
const result = await fetchApi();
if (result.success) closeModalWithSuccess();
else closeModalWithFailure();
};

return enabled ? (
<Button onClick={handleClickOutstanding}>선택한 회원 우수 {type}</Button>
) : (
<Button onClick={handleClickCloseModal}>확인하기</Button>
);
};

export default OutstandingModalButton;
Loading

0 comments on commit 32c68f8

Please sign in to comment.