diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf021b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.env +.next diff --git a/13team-project/README.md b/13team-project/README.md deleted file mode 100644 index e215bc4..0000000 --- a/13team-project/README.md +++ /dev/null @@ -1,36 +0,0 @@ -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). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -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. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [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. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on 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. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/13team-project/src/app/api/admin/route.ts b/13team-project/src/app/api/admin/route.ts deleted file mode 100644 index e79c90e..0000000 --- a/13team-project/src/app/api/admin/route.ts +++ /dev/null @@ -1,46 +0,0 @@ -let admins = [ - { - id: 1, - name: "최관리", - username: "admin1", - role: "센터장", - }, - { - id: 2, - name: "김매니저", - username: "manager2", - role: "매니저", - } -]; - -// 관리자 정보 가져오기 -export async function GET(req: Request) { - try { - const url = new URL(req.url); - const id = url.searchParams.get("id"); - - if (id) { - const admin = admins.find((admin) => admin.id === parseInt(id)); - if (!admin) { - return new Response( - JSON.stringify({ success: false, message: "해당 ID의 관리자를 찾을 수 없음" }), - { status: 404, headers: { "Content-Type": "application/json" } } - ); - } - return new Response(JSON.stringify({ success: true, admin }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } - - return new Response(JSON.stringify({ success: true, admins }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response( - JSON.stringify({ success: false, message: "관리자 데이터 로드 실패", error }), - { status: 500, headers: { "Content-Type": "application/json" } } - ); - } -} diff --git a/13team-project/src/app/api/caregiver/route.ts b/13team-project/src/app/api/caregiver/route.ts deleted file mode 100644 index 1297b3d..0000000 --- a/13team-project/src/app/api/caregiver/route.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { NextResponse } from "next/server"; - -// 요양보호사 초기 데이터 (배열 형태) -let caregiverData = [ - { - id: 1, - name: "김수은", - location: "서울시 강남구", - experience: 5, - certification: "간호조무사 자격증", - hasJobPosting: true, - isJobSeeking: false, // 구직 여부 - isActive: false, // 활성 상태 - jobInfo: { days: [], times: [], hourlyWage: 0 }, - }, - { - id: 2, - name: "이영희", - location: "경기도 성남시", - experience: 3, - certification: "요양보호사 자격증", - hasJobPosting: false, - isJobSeeking: false, // 구직 여부 - isActive: false, // 활성 상태 - jobInfo: { days: [], times: [], hourlyWage: 0 }, - }, -]; - -/** GET 요청: 특정 ID 또는 특정 어르신에게 맞는 요양보호사 추천 */ -export async function GET(req: Request) { - const { searchParams } = new URL(req.url); - const id = searchParams.get("id"); - - if (id) { - const caregiver = caregiverData.find((c) => c.id === Number(id)); - if (!caregiver) { - return NextResponse.json({ success: false, message: "요양보호사 정보를 찾을 수 없습니다." }, { status: 404 }); - } - // ✅ success: true 추가 - return NextResponse.json({ success: true, caregiver }, { status: 200 }); - } - - return NextResponse.json({ success: true, caregivers: caregiverData }, { status: 200 }); -} - -/** POST 요청: 특정 요양보호사 구직 정보 저장 */ -export async function POST(req: Request) { - try { - const { id, days, times, hourlyWage } = await req.json(); - - // 유효성 검사 - if (!id || !days?.length || !times?.length || !hourlyWage) { - return NextResponse.json({ success: false, message: "모든 정보를 입력해주세요!" }, { status: 400 }); - } - - // 특정 요양보호사 찾기 - const caregiverIndex = caregiverData.findIndex((caregiver) => caregiver.id === id); - if (caregiverIndex === -1) { - return NextResponse.json({ success: false, message: "요양보호사 정보를 찾을 수 없습니다!" }, { status: 404 }); - } - - // 해당 요양보호사의 구직 정보 업데이트 - caregiverData[caregiverIndex] = { - ...caregiverData[caregiverIndex], - isJobSeeking: true, - isActive: true, // 자동 활성화 - jobInfo: { days, times, hourlyWage }, - }; - - return NextResponse.json({ - success: true, - message: "구직 정보 저장 완료!", - data: caregiverData[caregiverIndex], - }, { status: 200 }); - - } catch (error) { - return NextResponse.json({ success: false, message: "구직 정보 저장 실패", error }, { status: 500 }); - } -} - -/** PATCH 요청: 특정 요양보호사의 구직 상태 업데이트 */ -export async function PATCH(req: Request) { - try { - const { id, isActive } = await req.json(); - - // 특정 요양보호사 찾기 - const caregiverIndex = caregiverData.findIndex((c) => c.id === Number(id)); - if (caregiverIndex === -1) { - return NextResponse.json({ success: false, message: "요양보호사 정보를 찾을 수 없습니다!" }, { status: 404 }); - } - - // 유효성 검사: 구직 정보 없이 활성화 불가 - if (!caregiverData[caregiverIndex].isJobSeeking && isActive) { - return NextResponse.json({ success: false, message: "구직 정보를 먼저 입력해주세요!" }, { status: 400 }); - } - - // 구직 상태 업데이트 - caregiverData[caregiverIndex] = { - ...caregiverData[caregiverIndex], - isActive: isActive ?? caregiverData[caregiverIndex].isActive, - }; - - return NextResponse.json({ - success: true, - message: "구직 상태 변경 완료!", - data: caregiverData[caregiverIndex], - }, { status: 200 }); - - } catch (error) { - return NextResponse.json({ success: false, message: "구직 상태 변경 실패", error }, { status: 500 }); - } -} - -/** PUT 요청: 특정 요양보호사의 구직 정보 수정 */ -export async function PUT(req: Request) { - try { - const { id, days, times, hourlyWage } = await req.json(); - - // 특정 요양보호사 찾기 - const caregiverIndex = caregiverData.findIndex((c) => c.id === Number(id)); - if (caregiverIndex === -1) { - return NextResponse.json({ success: false, message: "요양보호사 정보를 찾을 수 없습니다!" }, { status: 404 }); - } - - // 기존 정보 수정 - caregiverData[caregiverIndex] = { - ...caregiverData[caregiverIndex], - jobInfo: { days, times, hourlyWage }, - }; - - return NextResponse.json({ - success: true, - message: "구직 정보가 수정되었습니다.", - data: caregiverData[caregiverIndex], - }, { status: 200 }); - - } catch (error) { - return NextResponse.json({ success: false, message: "구직 정보 수정 실패", error }, { status: 500 }); - } -} - -/** DELETE 요청: 특정 요양보호사 삭제 */ -export async function DELETE(req: Request) { - try { - const { searchParams } = new URL(req.url); - const id = searchParams.get("id"); - - if (!id) { - return NextResponse.json({ success: false, message: "삭제할 ID를 입력해주세요." }, { status: 400 }); - } - - // 특정 요양보호사 삭제 - const initialLength = caregiverData.length; - caregiverData = caregiverData.filter((c) => c.id !== Number(id)); - - if (caregiverData.length === initialLength) { - return NextResponse.json({ success: false, message: "요양보호사를 찾을 수 없습니다." }, { status: 404 }); - } - - return NextResponse.json({ - success: true, - message: "요양보호사가 삭제되었습니다.", - }, { status: 200 }); - - } catch (error) { - return NextResponse.json({ success: false, message: "요양보호사 삭제 실패", error }, { status: 500 }); - } -} diff --git a/13team-project/src/app/api/elders/route.ts b/13team-project/src/app/api/elders/route.ts deleted file mode 100644 index 145d2aa..0000000 --- a/13team-project/src/app/api/elders/route.ts +++ /dev/null @@ -1,205 +0,0 @@ -let elders = [ - { - elid: 1, - center: "강동구 재가노인복지센터", - elderly: { - name: "김하은", - birthYear: 1945, - gender: "여", - careLevel: "3등급", - weight: 55, - diseases: "고혈압, 당뇨", - dementiaSymptoms: "약간의 기억력 저하", - cohabitant: "배우자와 함께 거주", - workplaceDetails: "아파트 3층, 엘리베이터 있음", - additionalServices: "병원 동행 필요", - location: "서울시 강동구 천호동", - description: "가족들과 함께 거주하시며, 모두 친절하십니다!", - }, - mealSupport: true, - toiletSupport: false, - mobilitySupport: true, - hasJobPosting: true, // 채용공고 있음 - conditions: { - wage: 13000, - days: ["월요일", "수요일", "금요일"], - time: "16~22시", - }, - forced: false, // 기본적으로는 근무 조건과 일치하는 제안 - }, - { - elid: 2, - center: "강동구 재가노인복지센터", - elderly: { - name: "최지한", - birthYear: 1945, - gender: "여", - careLevel: "3등급", - weight: 55, - diseases: "고혈압, 당뇨", - dementiaSymptoms: "약간의 기억력 저하", - cohabitant: "배우자와 함께 거주", - workplaceDetails: "아파트 3층, 엘리베이터 있음", - additionalServices: "병원 동행 필요", - location: "서울시 강동구 천호동", - description: "가족들과 함께 거주하시며, 모두 친절하십니다!", - }, - mealSupport: true, - toiletSupport: false, - mobilitySupport: true, - hasJobPosting: true, // 채용공고 있음 - conditions: { - wage: 13000, - days: ["월요일", "수요일", "금요일"], - time: "16~22시", - }, - forced: false, // 기본적으로는 근무 조건과 일치하는 제안 - }, - { - elid: 3, - center: "강동구 재가노인복지센터", - elderly: { - name: "최지한", - birthYear: 1945, - gender: "여", - careLevel: "3등급", - weight: 55, - diseases: "고혈압, 당뇨", - dementiaSymptoms: "약간의 기억력 저하", - cohabitant: "배우자와 함께 거주", - workplaceDetails: "아파트 3층, 엘리베이터 있음", - additionalServices: "병원 동행 필요", - location: "서울시 강동구 천호동", - description: "가족들과 함께 거주하시며, 모두 친절하십니다!", - }, - mealSupport: true, - toiletSupport: false, - mobilitySupport: true, - hasJobPosting: false, // 채용공고 있음 - conditions: { - wage: 0, - days: [], - time: "", - }, - forced: false, // 기본적으로는 근무 조건과 일치하는 제안 - }, - { - elid: 4, - center: "강동구 재가노인복지센터", - elderly: { - name: "사람3", - birthYear: 1945, - gender: "여", - careLevel: "3등급", - weight: 55, - diseases: "고혈압, 당뇨", - dementiaSymptoms: "약간의 기억력 저하", - cohabitant: "배우자와 함께 거주", - workplaceDetails: "아파트 3층, 엘리베이터 있음", - additionalServices: "병원 동행 필요", - location: "서울시 강동구 천호동", - description: "가족들과 함께 거주하시며, 모두 친절하십니다!", - }, - mealSupport: true, - toiletSupport: false, - mobilitySupport: true, - hasJobPosting: true, // 채용공고 있음 - conditions: { - wage: 13000, - days: ["월요일", "수요일", "금요일"], - time: "16~22시", - }, - forced: false, // 기본적으로는 근무 조건과 일치하는 제안 - }, - ]; - - /** 어르신 목록 가져오기 */ -export async function GET(req: Request): Promise { - const { searchParams } = new URL(req.url); - const jobPostingOnly = searchParams.get("jobPostingOnly") === "true"; // `true`이면 채용공고 등록된 어르신만 가져옴 - - // 모든 어르신의 `conditions` 필드를 보장 - const filteredElders = elders - .filter((elder) => !jobPostingOnly || elder.hasJobPosting) - .map((elder) => ({ - ...elder, - conditions: elder.conditions ?? { wage: 0, days: [], time: "" }, // 기본값 추가 - })); - - return new Response(JSON.stringify({ elders: filteredElders, hasElders: filteredElders.length > 0 }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } - - - /** 새로운 어르신 추가 */ - export async function POST(req: Request): Promise { - try { - const newElder = await req.json(); - newElder.id = elders.length + 1; - newElder.hasJobPosting = false; - newElder.forced = true; - elders.push(newElder); - - return new Response(JSON.stringify({ success: true, elder: newElder, hasElders: true }), { - status: 201, - headers: { "Content-Type": "application/json" }, - }); - - } catch (error) { - return new Response(JSON.stringify({ success: false, message: "어르신 추가 실패", error }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } - } - - /** 어르신 정보 업데이트 */ - export async function PATCH(req: Request): Promise { - try { - const { id, updates } = await req.json(); - const elderIndex = elders.findIndex((e) => e.elid === id); - - if (elderIndex === -1) { - return new Response(JSON.stringify({ success: false, message: "어르신을 찾을 수 없음" }), { - status: 404, - headers: { "Content-Type": "application/json" }, - }); - } - - elders[elderIndex] = { ...elders[elderIndex], ...updates }; - - return new Response(JSON.stringify({ success: true, elder: elders[elderIndex] }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ success: false, message: "업데이트 실패", error }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } - } - - /** 어르신 삭제 */ - export async function DELETE(req: Request): Promise { - try { - const { id } = await req.json(); - elders = elders.filter((e) => e.elid !== id); - - return new Response(JSON.stringify({ success: true, message: "삭제 완료", hasElders: elders.length > 0 }), { - status: 200, - headers: { "Content-Type": "application/json" }, - }); - } catch (error) { - return new Response(JSON.stringify({ success: false, message: "삭제 실패", error }), { - status: 500, - headers: { "Content-Type": "application/json" }, - }); - } - } - - - - \ No newline at end of file diff --git a/README.md b/README.md index ce468be..02ce1d9 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ -# Front-End \ No newline at end of file +# 잔물결 - Blaybus Team 13 + +## 기술스택 + +- 프론트엔드 / 백엔드 - Next.js +- Database - MongoDB + +## 실행방법 + +- `MongoDB`가 설치 되어 있어야 함 +- `.env` 환경변수 파일을 현재 디렉토리에 생성 +- `npm install` +- `npm start` + +### 필요 환경변수 목록 (.env) + +``` + MONGODB_USER=(MongoDB 계정) + MONGODB_PASSWORD=(MongoDB 비밀번호) + MONGODB_HOST=(MongoDB를 호스팅하는 주소) + MONGODB_PORT=(MongoDB 포트번호) + MONGODB_DB=(MongoDB Database 이름) + KAKAO_API_KEY=(Kakao 검색 API 사용을 위한 API Key) +``` diff --git a/13team-project/eslint.config.mjs b/eslint.config.mjs similarity index 100% rename from 13team-project/eslint.config.mjs rename to eslint.config.mjs diff --git a/13team-project/fonts/Pretendard-Black.woff2 b/fonts/Pretendard-Black.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Black.woff2 rename to fonts/Pretendard-Black.woff2 diff --git a/13team-project/fonts/Pretendard-Bold.woff2 b/fonts/Pretendard-Bold.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Bold.woff2 rename to fonts/Pretendard-Bold.woff2 diff --git a/13team-project/fonts/Pretendard-ExtraBold.woff2 b/fonts/Pretendard-ExtraBold.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-ExtraBold.woff2 rename to fonts/Pretendard-ExtraBold.woff2 diff --git a/13team-project/fonts/Pretendard-ExtraLight.woff2 b/fonts/Pretendard-ExtraLight.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-ExtraLight.woff2 rename to fonts/Pretendard-ExtraLight.woff2 diff --git a/13team-project/fonts/Pretendard-Light.woff2 b/fonts/Pretendard-Light.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Light.woff2 rename to fonts/Pretendard-Light.woff2 diff --git a/13team-project/fonts/Pretendard-Medium.woff2 b/fonts/Pretendard-Medium.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Medium.woff2 rename to fonts/Pretendard-Medium.woff2 diff --git a/13team-project/fonts/Pretendard-Regular.woff2 b/fonts/Pretendard-Regular.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Regular.woff2 rename to fonts/Pretendard-Regular.woff2 diff --git a/13team-project/fonts/Pretendard-SemiBold.woff2 b/fonts/Pretendard-SemiBold.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-SemiBold.woff2 rename to fonts/Pretendard-SemiBold.woff2 diff --git a/13team-project/fonts/Pretendard-Thin.woff2 b/fonts/Pretendard-Thin.woff2 similarity index 100% rename from 13team-project/fonts/Pretendard-Thin.woff2 rename to fonts/Pretendard-Thin.woff2 diff --git a/13team-project/next-env.d.ts b/next-env.d.ts similarity index 100% rename from 13team-project/next-env.d.ts rename to next-env.d.ts diff --git a/13team-project/next.config.ts b/next.config.ts similarity index 100% rename from 13team-project/next.config.ts rename to next.config.ts diff --git a/13team-project/package-lock.json b/package-lock.json similarity index 97% rename from 13team-project/package-lock.json rename to package-lock.json index 8cf7875..9be9456 100644 --- a/13team-project/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "framer-motion": "^12.4.3", "lodash": "^4.17.21", "lucide-react": "^0.475.0", + "mongodb": "^6.14.0", "next": "15.1.7", "react": "^19.0.0", "react-dom": "^19.0.0", @@ -697,6 +698,15 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.2.0.tgz", + "integrity": "sha512-+ywrb0AqkfaYuhHs6LxKWgqbh3I72EpEgESCw37o+9qPx9WTCkgDm2B+eMrwehGtHBWHFU4GXvnSCNiFhhausg==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@next/env": { "version": "15.1.7", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz", @@ -993,6 +1003,21 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.24.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.0.tgz", @@ -1694,6 +1719,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bson": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", + "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4174,6 +4208,12 @@ "node": ">= 0.4" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4252,6 +4292,62 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mongodb": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.0.tgz", + "integrity": "sha512-AlM6alTx98vcnk/jMMmoYuBrm4qpe1/VrbwvL2SXEHjdtJ1ZbVZmrpyjUx9mqS94e9HcemzpLn+CxzhmT7b0uw==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.3", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, "node_modules/motion-dom": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz", @@ -4944,7 +5040,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5445,6 +5540,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -5903,6 +6007,18 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", @@ -6121,6 +6237,28 @@ "dev": true, "license": "MIT" }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.1.tgz", + "integrity": "sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/13team-project/package.json b/package.json similarity index 97% rename from 13team-project/package.json rename to package.json index 278af8f..67d7408 100644 --- a/13team-project/package.json +++ b/package.json @@ -14,6 +14,7 @@ "framer-motion": "^12.4.3", "lodash": "^4.17.21", "lucide-react": "^0.475.0", + "mongodb": "^6.14.0", "next": "15.1.7", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/13team-project/postcss.config.js b/postcss.config.js similarity index 100% rename from 13team-project/postcss.config.js rename to postcss.config.js diff --git a/13team-project/postcss.config.mjs b/postcss.config.mjs similarity index 100% rename from 13team-project/postcss.config.mjs rename to postcss.config.mjs diff --git a/13team-project/public/assets/logo.png b/public/assets/logo.png similarity index 100% rename from 13team-project/public/assets/logo.png rename to public/assets/logo.png diff --git "a/13team-project/public/assets/\352\261\260\354\240\210\354\225\204\354\235\264\354\275\230.png" "b/public/assets/\352\261\260\354\240\210\354\225\204\354\235\264\354\275\230.png" similarity index 100% rename from "13team-project/public/assets/\352\261\260\354\240\210\354\225\204\354\235\264\354\275\230.png" rename to "public/assets/\352\261\260\354\240\210\354\225\204\354\235\264\354\275\230.png" diff --git "a/13team-project/public/assets/\352\265\254\354\247\201\354\240\225\353\263\264\352\264\200\353\246\254.png" "b/public/assets/\352\265\254\354\247\201\354\240\225\353\263\264\352\264\200\353\246\254.png" similarity index 100% rename from "13team-project/public/assets/\352\265\254\354\247\201\354\240\225\353\263\264\352\264\200\353\246\254.png" rename to "public/assets/\352\265\254\354\247\201\354\240\225\353\263\264\352\264\200\353\246\254.png" diff --git "a/13team-project/public/assets/\353\202\264\354\240\225\353\263\264_OFF.png" "b/public/assets/\353\202\264\354\240\225\353\263\264_OFF.png" similarity index 100% rename from "13team-project/public/assets/\353\202\264\354\240\225\353\263\264_OFF.png" rename to "public/assets/\353\202\264\354\240\225\353\263\264_OFF.png" diff --git "a/13team-project/public/assets/\353\214\200\354\213\234\353\263\264\353\223\234_OFF.png" "b/public/assets/\353\214\200\354\213\234\353\263\264\353\223\234_OFF.png" similarity index 100% rename from "13team-project/public/assets/\353\214\200\354\213\234\353\263\264\353\223\234_OFF.png" rename to "public/assets/\353\214\200\354\213\234\353\263\264\353\223\234_OFF.png" diff --git "a/13team-project/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_OFF.png" "b/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_OFF.png" similarity index 100% rename from "13team-project/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_OFF.png" rename to "public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_OFF.png" diff --git "a/13team-project/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_ON.png" "b/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_ON.png" similarity index 100% rename from "13team-project/public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_ON.png" rename to "public/assets/\353\214\200\355\231\224\355\225\230\352\270\260_ON.png" diff --git "a/13team-project/public/assets/\354\226\264\353\245\264\354\213\240\352\264\200\353\246\254_OFF.png" "b/public/assets/\354\226\264\353\245\264\354\213\240\352\264\200\353\246\254_OFF.png" similarity index 100% rename from "13team-project/public/assets/\354\226\264\353\245\264\354\213\240\352\264\200\353\246\254_OFF.png" rename to "public/assets/\354\226\264\353\245\264\354\213\240\352\264\200\353\246\254_OFF.png" diff --git "a/13team-project/public/assets/\354\240\234\354\225\210\354\225\204\354\235\264\354\275\230.png" "b/public/assets/\354\240\234\354\225\210\354\225\204\354\235\264\354\275\230.png" similarity index 100% rename from "13team-project/public/assets/\354\240\234\354\225\210\354\225\204\354\235\264\354\275\230.png" rename to "public/assets/\354\240\234\354\225\210\354\225\204\354\235\264\354\275\230.png" diff --git "a/13team-project/public/assets/\354\241\260\352\261\264 \353\266\210\354\235\274\354\271\230.png" "b/public/assets/\354\241\260\352\261\264 \353\266\210\354\235\274\354\271\230.png" similarity index 100% rename from "13team-project/public/assets/\354\241\260\352\261\264 \353\266\210\354\235\274\354\271\230.png" rename to "public/assets/\354\241\260\352\261\264 \353\266\210\354\235\274\354\271\230.png" diff --git "a/13team-project/public/assets/\354\241\260\352\261\264 \354\235\274\354\271\230.png" "b/public/assets/\354\241\260\352\261\264 \354\235\274\354\271\230.png" similarity index 100% rename from "13team-project/public/assets/\354\241\260\352\261\264 \354\235\274\354\271\230.png" rename to "public/assets/\354\241\260\352\261\264 \354\235\274\354\271\230.png" diff --git "a/13team-project/public/assets/\354\241\260\352\261\264 \355\230\221\354\235\230\352\260\200\353\212\245.png" "b/public/assets/\354\241\260\352\261\264 \355\230\221\354\235\230\352\260\200\353\212\245.png" similarity index 100% rename from "13team-project/public/assets/\354\241\260\352\261\264 \355\230\221\354\235\230\352\260\200\353\212\245.png" rename to "public/assets/\354\241\260\352\261\264 \355\230\221\354\235\230\352\260\200\353\212\245.png" diff --git "a/13team-project/public/assets/\355\224\204\353\241\234\355\225\204.png" "b/public/assets/\355\224\204\353\241\234\355\225\204.png" similarity index 100% rename from "13team-project/public/assets/\355\224\204\353\241\234\355\225\204.png" rename to "public/assets/\355\224\204\353\241\234\355\225\204.png" diff --git "a/13team-project/public/assets/\355\231\210\355\231\224\353\251\264_OFF.png" "b/public/assets/\355\231\210\355\231\224\353\251\264_OFF.png" similarity index 100% rename from "13team-project/public/assets/\355\231\210\355\231\224\353\251\264_OFF.png" rename to "public/assets/\355\231\210\355\231\224\353\251\264_OFF.png" diff --git "a/13team-project/public/assets/\355\231\210\355\231\224\353\251\264_ON.png" "b/public/assets/\355\231\210\355\231\224\353\251\264_ON.png" similarity index 100% rename from "13team-project/public/assets/\355\231\210\355\231\224\353\251\264_ON.png" rename to "public/assets/\355\231\210\355\231\224\353\251\264_ON.png" diff --git a/13team-project/public/file.svg b/public/file.svg similarity index 100% rename from 13team-project/public/file.svg rename to public/file.svg diff --git a/13team-project/public/fonts/Pretendard-Black.woff2 b/public/fonts/Pretendard-Black.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Black.woff2 rename to public/fonts/Pretendard-Black.woff2 diff --git a/13team-project/public/fonts/Pretendard-Bold.woff2 b/public/fonts/Pretendard-Bold.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Bold.woff2 rename to public/fonts/Pretendard-Bold.woff2 diff --git a/13team-project/public/fonts/Pretendard-ExtraBold.woff2 b/public/fonts/Pretendard-ExtraBold.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-ExtraBold.woff2 rename to public/fonts/Pretendard-ExtraBold.woff2 diff --git a/13team-project/public/fonts/Pretendard-ExtraLight.woff2 b/public/fonts/Pretendard-ExtraLight.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-ExtraLight.woff2 rename to public/fonts/Pretendard-ExtraLight.woff2 diff --git a/13team-project/public/fonts/Pretendard-Light.woff2 b/public/fonts/Pretendard-Light.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Light.woff2 rename to public/fonts/Pretendard-Light.woff2 diff --git a/13team-project/public/fonts/Pretendard-Medium.woff2 b/public/fonts/Pretendard-Medium.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Medium.woff2 rename to public/fonts/Pretendard-Medium.woff2 diff --git a/13team-project/public/fonts/Pretendard-Regular.woff2 b/public/fonts/Pretendard-Regular.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Regular.woff2 rename to public/fonts/Pretendard-Regular.woff2 diff --git a/13team-project/public/fonts/Pretendard-SemiBold.woff2 b/public/fonts/Pretendard-SemiBold.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-SemiBold.woff2 rename to public/fonts/Pretendard-SemiBold.woff2 diff --git a/13team-project/public/fonts/Pretendard-Thin.woff2 b/public/fonts/Pretendard-Thin.woff2 similarity index 100% rename from 13team-project/public/fonts/Pretendard-Thin.woff2 rename to public/fonts/Pretendard-Thin.woff2 diff --git a/13team-project/public/globe.svg b/public/globe.svg similarity index 100% rename from 13team-project/public/globe.svg rename to public/globe.svg diff --git a/13team-project/public/next.svg b/public/next.svg similarity index 100% rename from 13team-project/public/next.svg rename to public/next.svg diff --git a/13team-project/public/vercel.svg b/public/vercel.svg similarity index 100% rename from 13team-project/public/vercel.svg rename to public/vercel.svg diff --git a/13team-project/public/window.svg b/public/window.svg similarity index 100% rename from 13team-project/public/window.svg rename to public/window.svg diff --git a/13team-project/src/app/about/page.tsx b/src/app/about/page.tsx similarity index 100% rename from 13team-project/src/app/about/page.tsx rename to src/app/about/page.tsx diff --git a/13team-project/src/app/admin/page.tsx b/src/app/admin/page.tsx similarity index 94% rename from 13team-project/src/app/admin/page.tsx rename to src/app/admin/page.tsx index 54499f6..d51f48e 100644 --- a/13team-project/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -199,37 +199,6 @@ export default function AdminDashboard() { } }; - /** ✅ 제안하기 API 요청 */ - const handleProposal = async (caregiverId: number, elderId: number) => { - try { - const response = await fetch("/api/propose", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - caregiverId, - elderId, - status: "pending", // 대기 상태 - }), - }); - - const data = await response.json(); - console.log("✅ 제안 전송 결과:", data); // 🔥 응답 확인 - - if (response.ok) { - alert("제안이 성공적으로 전송되었습니다."); - // 제안 목록을 다시 불러오기 위해 상태 업데이트 - setProposals(prev => [...prev, { caregiverId, elderId, status: "pending" }]); - } else { - alert(`제안 전송 실패: ${data.message}`); - } - } catch (error) { - console.error("제안 전송 오류:", error); - alert("제안 전송 중 오류가 발생했습니다."); - } -}; - - - /** 프로필 페이지 넘기기 */ const handleNext = () => { if (filteredElders.length > 0) { diff --git a/src/app/api/admin/route.ts b/src/app/api/admin/route.ts new file mode 100644 index 0000000..5a12221 --- /dev/null +++ b/src/app/api/admin/route.ts @@ -0,0 +1,77 @@ +import { MongoClient } from "mongodb"; + +const { uri, dbName } = require("../../../utils/db_connect"); + +let admins = [ + { + id: 1, + name: "최관리", + username: "admin1", + role: "센터장", + }, + { + id: 2, + name: "김매니저", + username: "manager2", + role: "매니저", + }, +]; + +async function getAdmins() { + try { + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("admins"); + + const admins = (await collection.find({}).toArray()).sort( + (a, b) => a.id - b.id + ); + client.close(); + + return admins; + } catch (e) { + console.log(e); + return admins; + } +} + +// 관리자 정보 가져오기 +export async function GET(req: Request) { + try { + const url = new URL(req.url); + const id = url.searchParams.get("id"); + + const admins = await getAdmins(); + + if (id) { + const admin = admins.find((admin) => admin.id === parseInt(id)); + if (!admin) { + return new Response( + JSON.stringify({ + success: false, + message: "해당 ID의 관리자를 찾을 수 없음", + }), + { status: 404, headers: { "Content-Type": "application/json" } } + ); + } + return new Response(JSON.stringify({ success: true, admin }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + + return new Response(JSON.stringify({ success: true, admins }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } catch (error) { + return new Response( + JSON.stringify({ + success: false, + message: "관리자 데이터 로드 실패", + error, + }), + { status: 500, headers: { "Content-Type": "application/json" } } + ); + } +} diff --git a/src/app/api/auth/route.ts b/src/app/api/auth/route.ts new file mode 100644 index 0000000..beb350a --- /dev/null +++ b/src/app/api/auth/route.ts @@ -0,0 +1,43 @@ +import { MongoClient } from "mongodb"; +import { NextResponse } from "next/server"; + +const { uri, dbName } = require("../../../utils/db_connect"); + +/** POST 요청: 로그인 요청 */ +export async function POST(req: Request) { + const { id, password } = await req.json(); + + try { + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("carer"); + + const existingCarer = await collection + .find({ id, password }) + .limit(1) + .toArray(); + + if (existingCarer.length === 0) { + return NextResponse.json( + { success: false, message: "계정 정보를 확인하세요." }, + { status: 401 } + ); + } + + client.close(); + + return NextResponse.json( + { + user: existingCarer[0], + message: "로그인 성공", + token: true, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "구직 정보 저장 실패", error }, + { status: 500 } + ); + } +} diff --git a/src/app/api/caregiver/route.ts b/src/app/api/caregiver/route.ts new file mode 100644 index 0000000..7b478ac --- /dev/null +++ b/src/app/api/caregiver/route.ts @@ -0,0 +1,245 @@ +import { NextResponse } from "next/server"; +import { MongoClient } from "mongodb"; + +const { uri, dbName } = require("../../../utils/db_connect"); + +// 요양보호사 초기 데이터 (배열 형태) +let caregiverData = [ + { + id: 1, + name: "김수은", + location: "서울시 강남구", + experience: 5, + certification: "간호조무사 자격증", + hasJobPosting: true, + isJobSeeking: false, // 구직 여부 + isActive: false, // 활성 상태 + jobInfo: { days: [], times: [], hourlyWage: 0 }, + }, + { + id: 2, + name: "이영희", + location: "경기도 성남시", + experience: 3, + certification: "요양보호사 자격증", + hasJobPosting: false, + isJobSeeking: false, // 구직 여부 + isActive: false, // 활성 상태 + jobInfo: { days: [], times: [], hourlyWage: 0 }, + }, +]; + +async function getCaregivers() { + try { + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("caregivers"); + + const caregivers = (await collection.find({}).toArray()).sort( + (a, b) => a.id - b.id + ); + client.close(); + + return caregivers; + } catch (e) { + console.log(e); + return caregiverData; + } +} + +/** GET 요청: 특정 ID 또는 특정 어르신에게 맞는 요양보호사 추천 */ +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const id = searchParams.get("id"); + + const caregiverData = await getCaregivers(); + + if (id) { + const caregiver = caregiverData.find((c) => c.id === Number(id)); + if (!caregiver) { + return NextResponse.json( + { success: false, message: "요양보호사 정보를 찾을 수 없습니다." }, + { status: 404 } + ); + } + // ✅ success: true 추가 + return NextResponse.json({ success: true, caregiver }, { status: 200 }); + } + + return NextResponse.json( + { success: true, caregivers: caregiverData }, + { status: 200 } + ); +} + +/** POST 요청: 특정 요양보호사 구직 정보 저장 */ +export async function POST(req: Request) { + try { + const { id, days, times, hourlyWage } = await req.json(); + + // 유효성 검사 + if (!id || !days?.length || !times?.length || !hourlyWage) { + return NextResponse.json( + { success: false, message: "모든 정보를 입력해주세요!" }, + { status: 400 } + ); + } + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("caregivers"); + + const caregiver = await collection.updateOne( + { id: Number(id) }, + { + $set: { + isJobSeeking: true, + isActive: true, + jobInfo: { days, times, hourlyWage }, + }, + } + ); + client.close(); + + return NextResponse.json( + { + success: true, + message: "구직 정보 저장 완료!", + data: caregiver, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "구직 정보 저장 실패", error }, + { status: 500 } + ); + } +} + +/** PATCH 요청: 특정 요양보호사의 구직 상태 업데이트 */ +export async function PATCH(req: Request) { + try { + const { id, isActive } = await req.json(); + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("caregivers"); + + const caregiver = await collection.findOne({ id: Number(id) }); + if (!caregiver) { + return NextResponse.json( + { success: false, message: "요양보호사 정보를 찾을 수 없습니다." }, + { status: 404 } + ); + } + + // 유효성 검사: 구직 정보 없이 활성화 불가 + if (!caregiver.isJobSeeking && isActive) { + return NextResponse.json( + { success: false, message: "구직 정보를 먼저 입력해주세요!" }, + { status: 400 } + ); + } + + // 구직 상태 업데이트 + caregiver["isActive"] = isActive; + + collection.updateOne({ id: Number(id) }, { $set: { isActive } }); + client.close(); + + return NextResponse.json( + { + success: true, + message: "구직 상태 변경 완료!", + data: caregiver, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "구직 상태 변경 실패", error }, + { status: 500 } + ); + } +} + +/** PUT 요청: 특정 요양보호사의 구직 정보 수정 */ +export async function PUT(req: Request) { + try { + const { id, days, times, hourlyWage } = await req.json(); + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("caregivers"); + + const caregiver = await collection.findOne({ id: Number(id) }); + if (!caregiver) { + return NextResponse.json( + { success: false, message: "요양보호사 정보를 찾을 수 없습니다." }, + { status: 404 } + ); + } + + caregiver["jobInfo"] = { days, times, hourlyWage }; + + await collection.updateOne( + { id: Number(id) }, + { $set: { jobInfo: { days, times, hourlyWage } } } + ); + client.close(); + + return NextResponse.json( + { + success: true, + message: "구직 정보가 수정되었습니다.", + data: caregiver, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "구직 정보 수정 실패", error }, + { status: 500 } + ); + } +} + +/** DELETE 요청: 특정 요양보호사 삭제 */ +export async function DELETE(req: Request) { + try { + const { searchParams } = new URL(req.url); + const id = searchParams.get("id"); + + if (!id) { + return NextResponse.json( + { success: false, message: "삭제할 ID를 입력해주세요." }, + { status: 400 } + ); + } + + // 특정 요양보호사 삭제 + const initialLength = caregiverData.length; + caregiverData = caregiverData.filter((c) => c.id !== Number(id)); + + if (caregiverData.length === initialLength) { + return NextResponse.json( + { success: false, message: "요양보호사를 찾을 수 없습니다." }, + { status: 404 } + ); + } + + return NextResponse.json( + { + success: true, + message: "요양보호사가 삭제되었습니다.", + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "요양보호사 삭제 실패", error }, + { status: 500 } + ); + } +} diff --git a/src/app/api/carer/route.ts b/src/app/api/carer/route.ts new file mode 100644 index 0000000..9edb591 --- /dev/null +++ b/src/app/api/carer/route.ts @@ -0,0 +1,49 @@ +import { NextResponse } from "next/server"; +import { MongoClient } from "mongodb"; + +const { uri, dbName } = require("../../../utils/db_connect"); + +/** POST 요청: 회원등록 */ +export async function POST(req: Request) { + const { name, phone, position, id, password } = await req.json(); + + try { + // 유효성 검사 + if (!name || !phone || !position || !id || !password) { + return NextResponse.json( + { success: false, message: "모든 정보를 입력해주세요" }, + { status: 400 } + ); + } + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("carer"); + + const existingCarer = await collection.find({ id }).limit(1).toArray(); + + if (existingCarer.length) { + return NextResponse.json( + { success: false, message: "이미 등록된 ID입니다." }, + { status: 409 } + ); + } + + await collection.insertOne({ name, phone, position, id, password }); + client.close(); + + return NextResponse.json( + { + success: true, + message: "회원 가입 성공", + data: { name, phone, position, id }, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { success: false, message: "구직 정보 저장 실패", error }, + { status: 500 } + ); + } +} diff --git a/src/app/api/elders/route.ts b/src/app/api/elders/route.ts new file mode 100644 index 0000000..dcea20c --- /dev/null +++ b/src/app/api/elders/route.ts @@ -0,0 +1,265 @@ +import { MongoClient } from "mongodb"; + +const { uri, dbName } = require("../../../utils/db_connect"); + +let elders = [ + { + elid: 1, + center: "강동구 재가노인복지센터", + elderly: { + name: "김하은", + birthYear: 1945, + gender: "여", + careLevel: "3등급", + weight: 55, + diseases: "고혈압, 당뇨", + dementiaSymptoms: "약간의 기억력 저하", + cohabitant: "배우자와 함께 거주", + workplaceDetails: "아파트 3층, 엘리베이터 있음", + additionalServices: "병원 동행 필요", + location: "서울시 강동구 천호동", + description: "가족들과 함께 거주하시며, 모두 친절하십니다!", + }, + mealSupport: true, + toiletSupport: false, + mobilitySupport: true, + hasJobPosting: true, // 채용공고 있음 + conditions: { + wage: 13000, + days: ["월요일", "수요일", "금요일"], + time: "16~22시", + }, + forced: false, // 기본적으로는 근무 조건과 일치하는 제안 + }, + { + elid: 2, + center: "강동구 재가노인복지센터", + elderly: { + name: "최지한", + birthYear: 1945, + gender: "여", + careLevel: "3등급", + weight: 55, + diseases: "고혈압, 당뇨", + dementiaSymptoms: "약간의 기억력 저하", + cohabitant: "배우자와 함께 거주", + workplaceDetails: "아파트 3층, 엘리베이터 있음", + additionalServices: "병원 동행 필요", + location: "서울시 강동구 천호동", + description: "가족들과 함께 거주하시며, 모두 친절하십니다!", + }, + mealSupport: true, + toiletSupport: false, + mobilitySupport: true, + hasJobPosting: true, // 채용공고 있음 + conditions: { + wage: 13000, + days: ["월요일", "수요일", "금요일"], + time: "16~22시", + }, + forced: false, // 기본적으로는 근무 조건과 일치하는 제안 + }, + { + elid: 3, + center: "강동구 재가노인복지센터", + elderly: { + name: "최지한", + birthYear: 1945, + gender: "여", + careLevel: "3등급", + weight: 55, + diseases: "고혈압, 당뇨", + dementiaSymptoms: "약간의 기억력 저하", + cohabitant: "배우자와 함께 거주", + workplaceDetails: "아파트 3층, 엘리베이터 있음", + additionalServices: "병원 동행 필요", + location: "서울시 강동구 천호동", + description: "가족들과 함께 거주하시며, 모두 친절하십니다!", + }, + mealSupport: true, + toiletSupport: false, + mobilitySupport: true, + hasJobPosting: false, // 채용공고 있음 + conditions: { + wage: 0, + days: [], + time: "", + }, + forced: false, // 기본적으로는 근무 조건과 일치하는 제안 + }, + { + elid: 4, + center: "강동구 재가노인복지센터", + elderly: { + name: "사람3", + birthYear: 1945, + gender: "여", + careLevel: "3등급", + weight: 55, + diseases: "고혈압, 당뇨", + dementiaSymptoms: "약간의 기억력 저하", + cohabitant: "배우자와 함께 거주", + workplaceDetails: "아파트 3층, 엘리베이터 있음", + additionalServices: "병원 동행 필요", + location: "서울시 강동구 천호동", + description: "가족들과 함께 거주하시며, 모두 친절하십니다!", + }, + mealSupport: true, + toiletSupport: false, + mobilitySupport: true, + hasJobPosting: true, // 채용공고 있음 + conditions: { + wage: 13000, + days: ["월요일", "수요일", "금요일"], + time: "16~22시", + }, + forced: false, // 기본적으로는 근무 조건과 일치하는 제안 + }, +]; + +async function getElders() { + try { + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("elders"); + + const elders = (await collection.find({}).toArray()).sort( + (a, b) => a.id - b.id + ); + client.close(); + + return elders; + } catch (e) { + console.log(e); + return elders; + } +} + +/** 어르신 목록 가져오기 */ +export async function GET(req: Request): Promise { + const { searchParams } = new URL(req.url); + const jobPostingOnly = searchParams.get("jobPostingOnly") === "true"; // `true`이면 채용공고 등록된 어르신만 가져옴 + + const elders = await getElders(); + // 모든 어르신의 `conditions` 필드를 보장 + const filteredElders = elders + .filter((elder) => !jobPostingOnly || elder.hasJobPosting) + .map((elder) => ({ + ...elder, + conditions: elder.conditions ?? { wage: 0, days: [], time: "" }, // 기본값 추가 + })); + + return new Response( + JSON.stringify({ + elders: filteredElders, + hasElders: filteredElders.length > 0, + }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); +} + +/** 새로운 어르신 추가 */ +export async function POST(req: Request): Promise { + try { + const newElder = await req.json(); + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("elders"); + + const elderCount = await collection.countDocuments(); + + newElder.id = elderCount + 1; + newElder.hasJobPosting = false; + newElder.forced = true; + + await collection.insertOne(newElder); + client.close(); + + return new Response( + JSON.stringify({ success: true, elder: newElder, hasElders: true }), + { + status: 201, + headers: { "Content-Type": "application/json" }, + } + ); + } catch (error) { + return new Response( + JSON.stringify({ success: false, message: "어르신 추가 실패", error }), + { + status: 500, + headers: { "Content-Type": "application/json" }, + } + ); + } +} + +/** 어르신 정보 업데이트 */ +export async function PATCH(req: Request): Promise { + try { + const { id, updates } = await req.json(); + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("elders"); + + await collection.updateOne({ elid: id }, { $set: updates }); + client.close(); + + return new Response( + JSON.stringify({ success: true, elder: { elid: id, ...updates } }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } catch (error) { + return new Response( + JSON.stringify({ success: false, message: "업데이트 실패", error }), + { + status: 500, + headers: { "Content-Type": "application/json" }, + } + ); + } +} + +/** 어르신 삭제 */ +export async function DELETE(req: Request): Promise { + try { + const { id } = await req.json(); + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("elders"); + + await collection.deleteOne({ elid: id }); + + const elderCount = await collection.countDocuments(); + + client.close(); + + return new Response( + JSON.stringify({ + success: true, + message: "삭제 완료", + hasElders: elderCount > 0, + }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } catch (error) { + return new Response( + JSON.stringify({ success: false, message: "삭제 실패", error }), + { + status: 500, + headers: { "Content-Type": "application/json" }, + } + ); + } +} diff --git a/13team-project/src/app/api/kakao-search/route.js b/src/app/api/kakao-search/route.js similarity index 68% rename from 13team-project/src/app/api/kakao-search/route.js rename to src/app/api/kakao-search/route.js index b20f584..6fb9a08 100644 --- a/13team-project/src/app/api/kakao-search/route.js +++ b/src/app/api/kakao-search/route.js @@ -1,14 +1,17 @@ import { NextResponse } from "next/server"; // ✅ NextRequest 삭제 -export async function GET(req) { - const KAKAO_API_KEY = process.env.KAKAO_API_KEY; // 환경 변수 사용 +const KAKAO_API_KEY = process.env.KAKAO_API_KEY; // 환경 변수 사용 +export async function GET(req) { try { const { searchParams } = new URL(req.url); const query = searchParams.get("query"); if (!query) { - return NextResponse.json({ message: "Missing query parameter" }, { status: 400 }); + return NextResponse.json( + { message: "Missing query parameter" }, + { status: 400 } + ); } const response = await fetch( @@ -27,6 +30,9 @@ export async function GET(req) { const data = await response.json(); return NextResponse.json(data); } catch (error) { - return NextResponse.json({ message: "서버 오류", error: error.message }, { status: 500 }); + return NextResponse.json( + { message: "서버 오류", error: error.message }, + { status: 500 } + ); } } diff --git a/13team-project/src/app/api/kakao/route.ts b/src/app/api/kakao/route.ts similarity index 77% rename from 13team-project/src/app/api/kakao/route.ts rename to src/app/api/kakao/route.ts index d4ab182..de3f72f 100644 --- a/13team-project/src/app/api/kakao/route.ts +++ b/src/app/api/kakao/route.ts @@ -1,14 +1,17 @@ import { NextRequest, NextResponse } from "next/server"; -export async function GET(req: NextRequest) { - const KAKAO_API_KEY = "50f92a6f823acfa0e0636e329463bb8d"; +const KAKAO_API_KEY = process.env.KAKAO_API_KEY || ""; +export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); const query = searchParams.get("query"); if (!query) { - return NextResponse.json({ message: "Missing query parameter" }, { status: 400 }); + return NextResponse.json( + { message: "Missing query parameter" }, + { status: 400 } + ); } const response = await fetch( diff --git a/13team-project/src/app/api/propose/route.ts b/src/app/api/propose/route.ts similarity index 74% rename from 13team-project/src/app/api/propose/route.ts rename to src/app/api/propose/route.ts index 2afd21c..11b5e9e 100644 --- a/13team-project/src/app/api/propose/route.ts +++ b/src/app/api/propose/route.ts @@ -1,5 +1,8 @@ +import { MongoClient } from "mongodb"; import { NextResponse } from "next/server"; +const { uri, dbName } = require("../../../utils/db_connect"); + // 전역 변수 대신 상태를 유지할 저장소 사용 (임시 메모리 저장) let proposals: Proposal[] = []; @@ -22,13 +25,18 @@ export async function GET(req: Request) { ); } - // 특정 요양보호사에 해당하는 제안 필터링 - const filteredProposals = proposals.filter( - (proposal) => proposal.caregiverId === caregiverId - ); + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("proposals"); + + const proposals = collection.find({ + caregiverId: caregiverId, + }); + + client.close(); return NextResponse.json( - { success: true, proposals: filteredProposals }, + { success: true, proposals: proposals }, { status: 200 } ); } catch (error) { @@ -64,8 +72,14 @@ export async function POST(req: Request) { } // 새로운 제안 객체 추가 + + const client = await MongoClient.connect(uri); + const database = client.db(dbName); + const collection = database.collection("proposals"); + const newProposal: Proposal = { caregiverId, elderId, status }; - proposals.push(newProposal); + + await collection.insertOne(newProposal); return NextResponse.json( { diff --git a/13team-project/src/app/caregiver/page.tsx b/src/app/caregiver/page.tsx similarity index 100% rename from 13team-project/src/app/caregiver/page.tsx rename to src/app/caregiver/page.tsx diff --git a/13team-project/src/app/carergiver-register/certificate/page.tsx b/src/app/carergiver-register/certificate/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/certificate/page.tsx rename to src/app/carergiver-register/certificate/page.tsx diff --git a/13team-project/src/app/carergiver-register/page.tsx b/src/app/carergiver-register/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/page.tsx rename to src/app/carergiver-register/page.tsx diff --git a/13team-project/src/app/carergiver-register/phone/page.tsx b/src/app/carergiver-register/phone/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/phone/page.tsx rename to src/app/carergiver-register/phone/page.tsx diff --git a/13team-project/src/app/carergiver-register/residence/page.tsx b/src/app/carergiver-register/residence/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/residence/page.tsx rename to src/app/carergiver-register/residence/page.tsx diff --git a/13team-project/src/app/carergiver-register/selectiveInformation/page.tsx b/src/app/carergiver-register/selectiveInformation/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/selectiveInformation/page.tsx rename to src/app/carergiver-register/selectiveInformation/page.tsx diff --git a/13team-project/src/app/carergiver-register/success/page.tsx b/src/app/carergiver-register/success/page.tsx similarity index 100% rename from 13team-project/src/app/carergiver-register/success/page.tsx rename to src/app/carergiver-register/success/page.tsx diff --git a/13team-project/src/app/chat/page.tsx b/src/app/chat/page.tsx similarity index 100% rename from 13team-project/src/app/chat/page.tsx rename to src/app/chat/page.tsx diff --git a/13team-project/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx similarity index 100% rename from 13team-project/src/app/dashboard/page.tsx rename to src/app/dashboard/page.tsx diff --git a/13team-project/src/app/globals.css b/src/app/globals.css similarity index 100% rename from 13team-project/src/app/globals.css rename to src/app/globals.css diff --git a/13team-project/src/app/job-info/page.tsx b/src/app/job-info/page.tsx similarity index 100% rename from 13team-project/src/app/job-info/page.tsx rename to src/app/job-info/page.tsx diff --git a/13team-project/src/app/layout.tsx b/src/app/layout.tsx similarity index 100% rename from 13team-project/src/app/layout.tsx rename to src/app/layout.tsx diff --git a/13team-project/src/app/login/page.tsx b/src/app/login/page.tsx similarity index 100% rename from 13team-project/src/app/login/page.tsx rename to src/app/login/page.tsx diff --git a/13team-project/src/app/onboarding/page.tsx b/src/app/onboarding/page.tsx similarity index 100% rename from 13team-project/src/app/onboarding/page.tsx rename to src/app/onboarding/page.tsx diff --git a/13team-project/src/app/page.tsx b/src/app/page.tsx similarity index 100% rename from 13team-project/src/app/page.tsx rename to src/app/page.tsx diff --git a/13team-project/src/app/register/account/page.tsx b/src/app/register/account/page.tsx similarity index 94% rename from 13team-project/src/app/register/account/page.tsx rename to src/app/register/account/page.tsx index e03bd8a..218b69a 100644 --- a/13team-project/src/app/register/account/page.tsx +++ b/src/app/register/account/page.tsx @@ -36,7 +36,7 @@ export default function AccountPage() { setError(null); try { - const response = await fetch("https://0a69-121-134-41-93.ngrok-free.app/auth/carer/register", { + const response = await fetch("/api/carer", { method: "POST", headers: { "Content-Type": "application/json", @@ -89,7 +89,9 @@ export default function AccountPage() { {/* 아이디 입력 필드 */}
- + - + ) => setPassword(e.target.value)} + onChange={(e: ChangeEvent) => + setPassword(e.target.value) + } className="w-full p-3 border border-gray-300 rounded-lg mt-2 focus:outline-none focus:ring-2 focus:ring-orange-500" />
diff --git a/13team-project/src/app/register/additional-info/page.tsx b/src/app/register/additional-info/page.tsx similarity index 100% rename from 13team-project/src/app/register/additional-info/page.tsx rename to src/app/register/additional-info/page.tsx diff --git a/13team-project/src/app/register/custom-center/page.tsx b/src/app/register/custom-center/page.tsx similarity index 100% rename from 13team-project/src/app/register/custom-center/page.tsx rename to src/app/register/custom-center/page.tsx diff --git a/13team-project/src/app/register/name/page.tsx b/src/app/register/name/page.tsx similarity index 100% rename from 13team-project/src/app/register/name/page.tsx rename to src/app/register/name/page.tsx diff --git a/13team-project/src/app/register/page.tsx b/src/app/register/page.tsx similarity index 100% rename from 13team-project/src/app/register/page.tsx rename to src/app/register/page.tsx diff --git a/13team-project/src/app/register/phone/page.tsx b/src/app/register/phone/page.tsx similarity index 100% rename from 13team-project/src/app/register/phone/page.tsx rename to src/app/register/phone/page.tsx diff --git a/13team-project/src/app/register/position/page.tsx b/src/app/register/position/page.tsx similarity index 100% rename from 13team-project/src/app/register/position/page.tsx rename to src/app/register/position/page.tsx diff --git a/13team-project/src/app/register/success/page.tsx b/src/app/register/success/page.tsx similarity index 100% rename from 13team-project/src/app/register/success/page.tsx rename to src/app/register/success/page.tsx diff --git a/13team-project/src/app/settings/page.tsx b/src/app/settings/page.tsx similarity index 100% rename from 13team-project/src/app/settings/page.tsx rename to src/app/settings/page.tsx diff --git a/13team-project/src/app/test/page.tsx b/src/app/test/page.tsx similarity index 100% rename from 13team-project/src/app/test/page.tsx rename to src/app/test/page.tsx diff --git a/13team-project/src/app/utils/api.ts b/src/app/utils/api.ts similarity index 97% rename from 13team-project/src/app/utils/api.ts rename to src/app/utils/api.ts index a167934..e85aa22 100644 --- a/13team-project/src/app/utils/api.ts +++ b/src/app/utils/api.ts @@ -25,7 +25,7 @@ interface ApiResponse { // 🔹 로그인 API 요청 export async function loginUser(data: LoginData): Promise { try { - const response = await fetch(`${BASE_URL}/auth/login`, { + const response = await fetch(`/api/auth`, { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/app/utils/db_connect.ts b/src/app/utils/db_connect.ts new file mode 100644 index 0000000..4b79eb8 --- /dev/null +++ b/src/app/utils/db_connect.ts @@ -0,0 +1,9 @@ +const mongoUser = process.env.MONGODB_USER || ""; +const mongoPassword = process.env.MONGODB_PASSWORD || ""; +const mongoPort = process.env.MONGODB_PORT || ""; +const mongoHost = process.env.MONGODB_HOST || ""; + +const uri = `mongodb://${mongoUser}:${mongoPassword}@${mongoHost}:${mongoPort}`; +const dbName = process.env.MONGODB_DB || ""; + +export { uri, dbName }; diff --git a/13team-project/src/components/CheckIcon.tsx b/src/components/CheckIcon.tsx similarity index 100% rename from 13team-project/src/components/CheckIcon.tsx rename to src/components/CheckIcon.tsx diff --git a/13team-project/src/store/useAuthStore.ts b/src/store/useAuthStore.ts similarity index 100% rename from 13team-project/src/store/useAuthStore.ts rename to src/store/useAuthStore.ts diff --git a/13team-project/tailwind.config.js b/tailwind.config.js similarity index 100% rename from 13team-project/tailwind.config.js rename to tailwind.config.js diff --git a/13team-project/tailwind.config.ts b/tailwind.config.ts similarity index 100% rename from 13team-project/tailwind.config.ts rename to tailwind.config.ts diff --git a/13team-project/tsconfig.json b/tsconfig.json similarity index 100% rename from 13team-project/tsconfig.json rename to tsconfig.json