Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
73 changes: 50 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# Jobnote: 취업을 위한 올인원 대시보드
<br />

## Getting Started
## 서비스 설명
- Jobnote는 복잡한 취업 준비 과정을 한곳에서 체계적으로 관리할 수 있도록 도와주는 웹 애플리케이션입니다.
- 여러 개의 이력서 버전, 지원한 채용 공고, 일정들을 한 곳에서 관리할 수 있습니다.

First, run the development server:
<br/>

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
## 주요 기능
1. **문서 관리**:
- 이력서, 자기소개서, 포트폴리오 등 다양한 문서를 업로드하고 관리하세요.
- 문서별 버전 관리를 통해 어떤 회사에 어떤 버전의 서류를 제출했는지 쉽게 추적할 수 있습니다.

2. **지원 현황 대시보드:**
- 지원한 회사들의 상태(지원 완료, 서류 합격, 최종 합격 등)를 한눈에 파악할 수 있는 보드 스타일의 대시보드를 제공합니다.
- 회사별 지원 현황과 제출 문서를 손쉽게 연결하고 관리하세요.

3. **스마트 일정 관리:**
- 서류 마감일, 면접 일정 등 중요한 스케줄을 캘린더에 등록하고 놓치지 않도록 관리하세요.
- 다가오는 일정을 D-Day 형식으로 알려주어 중요한 일정을 잊지 않도록 도와줍니다.

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
4. **간편하고 안전한 인증:**
- 이메일 기반의 회원가입 및 로그인 기능을 제공합니다.
- Google, Naver, Kakao를 통한 소셜 로그인으로 더욱 빠르고 간편하게 서비스를 시작할 수 있습니다.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
<br/>

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## 기술 스택
- 프레임워크: Next.js (App Router)

## Learn More
- 언어: TypeScript

To learn more about Next.js, take a look at the following resources:
- 상태 관리: Zustand, React Query

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- 스타일링: Tailwind CSS

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
- 데이터 페칭: Axios

## Deploy on Vercel
- 배포: Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
<br/>

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
## 프로젝트 구조
## 📂 프로젝트 구조

```
├── public/ # 정적 에셋 (이미지, 폰트 등)
├── src/
│ ├── app/ # Next.js App Router 기반 라우팅 및 페이지
│ │ ├── (afterLogin)/ # 로그인 후 접근 가능한 페이지 그룹
│ │ ├── api/ # API Routes (백엔드 프록시)
│ │ ├── login/ # 로그인 관련 페이지
│ │ └── layout.tsx # 전역 레이아웃
│ ├── assets/ # SVG 아이콘
│ ├── hooks/ # React Query 커스텀 훅
│ ├── lib/ # API 호출 및 유틸리티 함수
│ ├── store/ # Zustand 상태 관리 스토어
│ └── types/ # TypeScript 타입 정의
├── .github/ # GitHub Actions 워크플로우
├── next.config.ts # Next.js 설정
└── package.json # 프로젝트 의존성 및 스크립트
```
94 changes: 51 additions & 43 deletions src/app/(afterLogin)/schedule/_components/sideSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,50 @@ import CalendarCheck from "@/assets/CalendarCheck.svg";
import { useSchedule } from "@/hooks/useSchedule";
import { Schedule } from "@/type/schedule";
import LoadingSpinner from "@/app/_components/loadingSpinner";
import { useMemo } from "react";

export default function SideSection() {
function formatDateToApiString(date: Date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
}

const startDate = formatDateToApiString(new Date());
const endDate = formatDateToApiString(
new Date(new Date().setDate(new Date().getDate() + 7))
);
function formatDateToApiString(date: Date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
const seconds = String(date.getSeconds()).padStart(2, "0");
return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
}

function getDDay(dateTime: string) {
const today = new Date();
const targetDate = new Date(dateTime);
function getDDay(dateTime: string) {
const today = new Date();
const targetDate = new Date(dateTime);

const todayOnly = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
);
const targetOnly = new Date(
targetDate.getFullYear(),
targetDate.getMonth(),
targetDate.getDate()
);
const todayOnly = new Date(
today.getFullYear(),
today.getMonth(),
today.getDate()
);
const targetOnly = new Date(
targetDate.getFullYear(),
targetDate.getMonth(),
targetDate.getDate()
);

const diffTime = targetOnly.getTime() - todayOnly.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
const diffTime = targetOnly.getTime() - todayOnly.getTime();
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));

if (diffDays > 0) return `D-${diffDays}`;
else if (diffDays === 0) return `D-Day`;
else return `D+${Math.abs(diffDays)}`;
}
if (diffDays > 0) return `D-${diffDays}`;
else if (diffDays === 0) return `D-Day`;
else return `D+${Math.abs(diffDays)}`;
}
export default function SideSection() {
const { startDate, endDate } = useMemo(() => {
const now = new Date();
const futureDate = new Date(now);
futureDate.setDate(now.getDate() + 7);
return {
startDate: formatDateToApiString(now),
endDate: formatDateToApiString(futureDate),
};
}, []);

const { data: scheduleData, isLoading: scheduleLoading } = useSchedule(
startDate,
Expand All @@ -53,16 +58,19 @@ export default function SideSection() {

return (
<div className="flex-1/3 flex flex-col gap-5">
{scheduleData?.data.content.map((schedule: Schedule) => (
<div key={schedule.id} className="py-4 px-5 border border-[#E7E7E7] rounded-lg">
<div className="flex flex-row gap-2 items-center">
<CalendarCheck />
<span className="text-xl font-medium">{schedule.title}</span>
<span className="text-xs ml-2 bg-[#FFF2E3] text-main rounded-full py-px px-2">
{getDDay(schedule.dateTime)}
</span>
{scheduleData?.data.content.map((schedule: Schedule) => (
<div
key={schedule.id}
className="py-4 px-5 border border-[#E7E7E7] rounded-lg"
>
<div className="flex flex-row gap-2 items-center">
<CalendarCheck />
<span className="text-xl font-medium">{schedule.title}</span>
<span className="text-xs ml-2 bg-[#FFF2E3] text-main rounded-full py-px px-2">
{getDDay(schedule.dateTime)}
</span>
</div>
</div>
</div>
))}
</div>
);
Expand Down