diff --git a/README.md b/README.md index f0619b5..194e030 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,105 @@ -# Team2-TiramisuCake-FE +# ๐Ÿฐ Team2-TiramisuCake-FE +### ๐Ÿš™ The new IONIQ 5 ์ด๋ฒคํŠธ ํŽ˜์ด์ง€ ๐Ÿš™ +
-**๋ฐฐํฌ URL** +## ๐Ÿ”— ๋ฐฐํฌ URL -๐Ÿ”— service : https://softeer.site/ +[![Service](https://img.shields.io/badge/Service-55A7BA.svg?style=for-the-badge)](https://softeer.site/) https://softeer.site +

+[![Admin](https://img.shields.io/badge/Admin-C0C7C9.svg?style=for-the-badge)](https://d3qmq1ffhp5il9.cloudfront.net) https://d3qmq1ffhp5il9.cloudfront.net -๐Ÿ”— admin : https://d3qmq1ffhp5il9.cloudfront.net/ +
-**์ปค๋ฐ‹ ์ปจ๋ฒค์…˜** -- label : subject -label์€ issue label์„ ์ฐธ๊ณ , -subject์€ ํ•ด๋‹น ์ปค๋ฐ‹์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ž˜ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ฒŒ ์š”์•ฝ ํ•  ๊ฒƒ +## ๐Ÿš€ ํ”„๋กœ์ ํŠธ ์ธ์› ๋ฐ ๊ธฐ๊ฐ„ -**๋ธŒ๋žœ์น˜ ์ „๋žต - [์•„๋ž˜ ์ฐธ๊ณ ]/issue number** +- **๊ฐœ๋ฐœ ์ธ์›**: FE 2๋ช… & BE 2๋ช… +- **ํ”„๋กœ์ ํŠธ ๊ธฐ๊ฐ„**: 2024.07.29 ~ 2024.08.26 +
-- feat - ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ๊ตฌํ˜„ -- fix - ๋ณ€๊ฒฝ์‚ฌํ•ญ(๋ณ€์ˆ˜, css ๋“ฑ) -- refactor - ๊ตฌ์กฐ ๋ณ€๊ฒฝ ex) api ์ „ํ›„ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ณ€๊ฒฝ? -- Test - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ +## ๐Ÿ‘ค ํŒ€์› + + + + + + + + + + + + + +
avataravataravataravatar
๊น€์ง€๋ฏผ๋ฐ•์ง€ํ˜„์†์ฐฌํ˜์ด์„๋ฏผ
-# ๊ทธ๋ผ์šด๋“œ๋ฃฐ +
-## ํšŒ์˜ +## ๐Ÿ’ก ๊ธฐ๋Šฅ ์†Œ๊ฐœ -- ๋งค์ผ ์•„์นจ์— **`30๋ถ„`** ์Šคํฌ๋Ÿผ - - ๊ฐœ๋ฐœ ์ƒํ™ฉ, ์˜ค๋ฅ˜, ์˜ค๋Š˜ ํ•  ์ผ -- ๋งค์ผ ์˜คํ›„ 6์‹œ์— ๊ฐ€๋Šฅํ•˜๋ฉด PR ํ•˜๊ธฐ -- ํšŒ๊ณ  ์ž‘์„ฑ - - ๋งค์ผ ์˜คํ›„ **`6์‹œ 30๋ถ„`** - - ์•Œ๊ฒŒ๋œ ์ , ์ข‹์•˜๋˜ ์ , ๋ณด์™„ํ•  ์  +### ์„œ๋น„์Šค -## ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ +### 1. ๋ฉ”์ธ ํŽ˜์ด์ง€ +๋ฉ”์ธ ํŽ˜์ด์ง€๋Š” ํฌ๊ฒŒ ๋žœ๋”ฉ ์„น์…˜, ์ด๋ฒคํŠธ ์„น์…˜, ๊ทธ๋ฆฌ๊ณ  ์ž๋™์ฐจ ์†Œ๊ฐœ ์„น์…˜์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
+์‚ฌ์šฉ์ž๊ฐ€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์„น์…˜ ๊ฐ„ ์ด๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์Šค๋ƒ… ๊ธฐ๋Šฅ์„ ์ ์šฉํ–ˆ์œผ๋ฉฐ, ํŽ˜์ด์ง€ ์ƒ๋‹จ์˜ ํ—ค๋”๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” ์„น์…˜์œผ๋กœ ๋ฐ”๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
+๋žœ๋”ฉ ์„น์…˜, ์ด๋ฒคํŠธ ์†Œ๊ฐœ ์„น์…˜, ์„ ์ฐฉ์ˆœ ์ด๋ฒคํŠธ ์†Œ๊ฐœ ์„น์…˜, ์ถ”์ฒจ ์ด๋ฒคํŠธ ์†Œ๊ฐœ ์„น์…˜, ๋งˆ์ง€๋ง‰์œผ๋กœ ์ฐจ๋Ÿ‰ ์ƒ์„ธ ์ •๋ณด ์„น์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.
+๊ฐ ์ด๋ฒคํŠธ ์„น์…˜์—์„œ ์ด๋ฒคํŠธ ๊ด€๋ จ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์ด๋ฒคํŠธ ์ฐธ์—ฌ์— ํ•„์š”ํ•œ ์•„์ด์˜ค๋‹‰ 5์˜ ์ฐจ๋Ÿ‰ ์ •๋ณด๋Š” ๋™์˜์ƒ๊ณผ ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ๊ฐ€ ์žˆ๋Š” ์ด๋ฏธ์ง€ ์บ๋Ÿฌ์…€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -- ๋งค์ผ ๊ฐ™์ด ๋ฐฅ๋จน๊ธฐ - - ๋ฐฅ ๋จน์„ ๋•Œ ์ผ ์–˜๊ธฐ ํ•˜์ง€ ์•Š๊ธฐ -- ํšŒ์˜ ์ค‘์— ์„œ๋กœ ํฐ์†Œ๋ฆฌ ๋‚ด์ง€ ์•Š๊ธฐ -- ๋ฐ˜๋ฐ• ์˜๊ฒฌ ๋‚ด๊ธฐ ์ „์— ์ข‹์€ ์˜๊ฒฌ์ด๋ผ๊ณ  ๋จผ์ € ์นญ์ฐฌํ•˜๊ธฐ -- ๋น„๋‚œํ•˜์ง€ ๋ง๊ธฐ -- ๋งˆ์Œ์— ์•ˆ๋“œ๋Š”๊ฒŒ ์žˆ์–ด๋„ ์ข‹์€ ๋ง์„ ๋จผ์ €ํ•˜๊ณ  ๊ทธ ๋‹ค์Œ์— ๋งˆ์Œ์— ์•ˆ๋“œ๋Š” ๊ฒƒ์„ ๋งํ•˜๊ธฐ -- ๋ฆฌ์•ก์…˜ ์ž˜ํ•ด์ฃผ๊ธฐ -- ๋ฐ˜๋งํ•˜๊ธฐ -- ์„œ๋กœ ์ธ์‚ฌ ์ž˜ ํ•˜๊ธฐ -- ๋™์˜ํ•  ๋•Œ โ€œ๊ทธ๋ž˜โ€๊ฐ€ ์•„๋‹ˆ๋ผ โ€œ์ข‹์•„โ€๋ผ๊ณ  ๋งํ•˜๊ธฐ -- ์ฃผ๋ง์— ๊ธ‰ํ•œ ์‚ฌํ•ญ์ด ์•„๋‹ˆ๋ฉด Discord๋กœ ์—ฐ๋ฝํ•˜์ง€ ๋ง๊ธฐ(์ตœ๋Œ€ํ•œ ํ‰์ผ์— ~^^) -- ์ง€๊ฐ, ์กฐํ‡ด ์‹œ ๋ฏธ๋ฆฌ ์•Œ๋ ค์ฃผ๊ธฐ +### 2. ์„ ์ฐฉ์ˆœ ์ด๋ฒคํŠธ +์„ ์ฐฉ์ˆœ ํŽ˜์ด์ง€์—์„œ ์ œ์‹œ๋œ ๋ฌธ์ œ์˜ ๋นˆ์นธ์— ์ ์ ˆํ•œ ๋‹จ์–ด๋“ค์„ ๋“œ๋ž˜๊ทธํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
+๋ชจ๋“  ๋นˆ์นธ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฑ„์šฐ๋ฉด ์ž๋™์œผ๋กœ ์„ ์ฐฉ์ˆœ ์ด๋ฒคํŠธ์— ์‘๋ชจ๋˜๋ฉฐ, ์ง€์ •๋œ ์„ ์ฐฉ์ˆœ ์ธ์› ์•ˆ์— ๋“ค๋ฉด ๋” ๋‰ด ์•„์ด์˜ค๋‹‰ 5 24์‹œ๊ฐ„ ๋ ŒํŠธ ์ด์šฉ๊ถŒ๊ณผ ์‹ ์ฐจ ํ• ์ธ ์ฝ”๋“œ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” QR ์ฝ”๋“œ๋ฅผ ํš๋“ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +### 3. ์ถ”์ฒจ ์ด๋ฒคํŠธ +๋ณต๊ถŒ ์ด๋ฒคํŠธ์—์„œ๋Š” ์บ”๋ฒ„์Šค๋ฅผ ๊ธ์–ด ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋งŒ์•ฝ ์„ธ ๊ฐœ์˜ ๋‹ค์ด์•„ ์ด๋ฏธ์ง€๊ฐ€ ๊ฐ™์€ ๋ฐฉํ–ฅ์œผ๋กœ ๋‚˜์˜ค๋ฉด ๋‹น์ฒจ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. +ํŽ˜์ด์ง€ ์ƒ๋‹จ์—์„œ๋Š” ์‚ฌ์šฉ์ž ๊ณต์œ  URL์„ ํ†ตํ•ด ์ดˆ๋Œ€ํ•œ ์นœ๊ตฌ ์ˆ˜์™€ ๋‚จ์€ ๋ณต๊ถŒ ๊ธฐํšŒ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ํ•˜๋‹จ์—์„œ๋Š” ์—ฐ์† ์ถœ์„ ์ผ์ˆ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 7์ผ ์—ฐ์† ์ถœ์„ํ•˜๋ฉด ์ƒํ’ˆ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +### 4. ๊ธฐ๋Œ€ํ‰ ํŽ˜์ด์ง€ +5๊ฐ€์ง€ ๊ธฐ๋Œ€ํ‰ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํ•ด๋‹นํ•˜๋Š” ๊ธฐ๋Œ€ํ‰์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
+๊ธฐ๋Œ€ํ‰์€ ์ปค์„œ ๊ธฐ๋ฐ˜ ๋ฌดํ•œ ์Šคํฌ๋กค ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„๋˜์–ด, ์‚ฌ์šฉ์ž๊ฐ€ ์Šคํฌ๋กค์„ ์˜ฌ๋ฆฌ๋ฉด ์ด์ „ ๊ธฐ๋Œ€ํ‰์„ ๊ณ„์†ํ•ด์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +
+ +### ์–ด๋“œ๋ฏผ + +### 1. ๋ฉ”์ธ ํŽ˜์ด์ง€ + +### 2. ์ด๋ฒคํŠธ ๊ด€๋ฆฌ ํŽ˜์ด์ง€ +์ด๋ฒคํŠธ ๊ด€๋ฆฌ ํŽ˜์ด์ง€์—์„œ ์ˆ˜์ •ํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์„ ์ฐฉ์ˆœ ์ด๋ฒคํŠธ์™€ ์ถ”์ฒจ ์ด๋ฒคํŠธ์˜ ๋‚ ์งœ ๋ฐ ์‹œ๊ฐ„์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### 3. ๋‹น์ฒจ ๊ด€๋ฆฌ ํŽ˜์ด์ง€ +์ด๋ฒคํŠธ ๋‹น์ฒจ ๊ด€๋ฆฌ ํŽ˜์ด์ง€์—์„œ๋Š” ์„ ์ฐฉ์ˆœ ๋ฐ ์ถ”์ฒจ ๋‹น์ฒจ ์ธ์› ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๊ฐ ์ด๋ฒคํŠธ์˜ ๋‹น์ฒจ์ž ๋ชฉ๋ก์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. + +### 4. ์ด๋ฒคํŠธ ์ง€ํ‘œ ํŽ˜์ด์ง€ +์ด๋ฒคํŠธ ๊ธฐ๊ฐ„ ๋™์•ˆ ๋‚ ์งœ๋ณ„ ๋ฉ”์ธ ํŽ˜์ด์ง€ ์ ‘์† ํšŸ์ˆ˜๋ฅผ ๋ง‰๋Œ€๊ทธ๋ž˜ํ”„๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์„ ์ฐฉ์ˆœ ๋ฐ ์ถ”์ฒจ ์ด๋ฒคํŠธ์˜ ์ฐธ์—ฌ์œจ์„ ์›๊ทธ๋ž˜ํ”„ ํ˜•ํƒœ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. +
+ +## โš’๏ธ ๊ธฐ์ˆ  ์Šคํƒ + +### DEVELOPMENT ENVIRONMENT +![Vite](https://img.shields.io/badge/vite-%23646CFF.svg?style=for-the-badge&logo=vite&logoColor=white) ![Yarn](https://img.shields.io/badge/yarn-%232C8EBB.svg?style=for-the-badge&logo=yarn&logoColor=white) + +### SKILL & UI + +![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) +### HOSTING & CDN + +![AWS](https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white) + + +### SERVER & CLIENT STATE MANAGEMENT + + ![Context-API](https://img.shields.io/badge/Context--Api-000000?style=for-the-badge&logo=react) + +### SCROLL & ANIMATION + + + + +
+ +## ๐Ÿ™Œ ํŒ€ ๊ทœ์น™ + +
+ ์ปค๋ฐ‹ & ๋ธŒ๋žœ์น˜ + # ์ปค๋ฐ‹ ์ปจ๋ฒค์…˜ ``` @@ -94,70 +150,43 @@ feat/issue_num feat/issue_num - refactor - ๊ตฌ์กฐ ๋ณ€๊ฒฝ ex) api ์ „ํ›„ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋ณ€๊ฒฝ? - test - ํ…Œ์ŠคํŠธ ์ฝ”๋“œ - chore - ํ™˜๊ฒฝ ์„ค์ •, ์ฃผ์„ ์ œ๊ฑฐ, ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ถ”๊ฐ€ ๋“ฑ + +
-Process - -- ํ”ผ๋“œ๋ฐฑ์ด๋‚˜ ๋„์›€์ด ํ•„์š”ํ•  ๋•Œ, ๊ทธ๋ฆฌ๊ณ  merge ์ค€๋น„๊ฐ€ ์™„๋ฃŒ๋˜์—ˆ์„ ๋•Œ๋Š” develop ๋ธŒ๋žœ์น˜๋กœ Pull Request๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. -- ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ๋ฆฌ๋ทฐ์™€ ๋…ผ์˜๊ฐ€ ๋๋‚˜๋ฉด develop ๋ธŒ๋žœ์น˜๋กœ mergeํ•œ๋‹ค. - -# PR ํ…œํ”Œ๋ฆฟ - -```jsx -## ์š”์•ฝ - -// PR ์ž‘์—… ์š”์•ฝ - -## ์ž‘์—… ๋‚ด์šฉ - -// PR ์ž‘์—… ๋‚ด์šฉ - -## ๊ด€๋ จ ์ด์Šˆ - - -// PR๊ณผ ๊ด€๋ จ๋œ ์ด์Šˆ ๋ฒˆํ˜ธ - -## ์ฒจ๋ถ€ ์ž๋ฃŒ (์„ ํƒ์‚ฌํ•ญ) - -// ์ฐธ๊ณ ํ•œ ์ž๋ฃŒ ๋งํฌ -``` - -# Issue ํ…œํ”Œ๋ฆฟ - -- Bug Report - -```jsx -## ์–ด๋–ค ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‚˜์š”? ๐Ÿง - -// ๋ฐœ์ƒํ•œ ๋ฒ„๊ทธ ์„ค๋ช… - -## ๋ฐœ์ƒ ์กฐ๊ฑด โš™๏ธ - -// Given-When-Then์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”. - -## ์˜ˆ์ธก ๊ฒฐ๊ณผ๊ฐ’ ๐Ÿ” - -// ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ–ˆ์„ ๋•Œ, ๋„์ถœ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ’ -``` - -- Feature Request - -```jsx -## What is this issue? ๐Ÿ›  - -// ์–ด๋–ค ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ• ๊ฑด์ง€ ์ž‘์„ฑ +
+ ๊ทธ๋ผ์šด๋“œ ๋ฃฐ + +## ํšŒ์˜ -## Progress ๐Ÿƒโ€โ™€๏ธ +- ๋งค์ผ ์•„์นจ์— **`30๋ถ„`** ์Šคํฌ๋Ÿผ + - ๊ฐœ๋ฐœ ์ƒํ™ฉ, ์˜ค๋ฅ˜, ์˜ค๋Š˜ ํ•  ์ผ +- ๋งค์ผ ์˜คํ›„ 6์‹œ์— ๊ฐ€๋Šฅํ•˜๋ฉด PR ํ•˜๊ธฐ +- ํšŒ๊ณ  ์ž‘์„ฑ + - ๋งค์ผ ์˜คํ›„ **`6์‹œ 30๋ถ„`** + - ์•Œ๊ฒŒ๋œ ์ , ์ข‹์•˜๋˜ ์ , ๋ณด์™„ํ•  ์  -// ํ•  ์ผ ๋ชฉ๋ก ์ž‘์„ฑ +## ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ -- [ ] +- ๋งค์ผ ๊ฐ™์ด ๋ฐฅ๋จน๊ธฐ + - ๋ฐฅ ๋จน์„ ๋•Œ ์ผ ์–˜๊ธฐ ํ•˜์ง€ ์•Š๊ธฐ +- ํšŒ์˜ ์ค‘์— ์„œ๋กœ ํฐ์†Œ๋ฆฌ ๋‚ด์ง€ ์•Š๊ธฐ +- ๋ฐ˜๋ฐ• ์˜๊ฒฌ ๋‚ด๊ธฐ ์ „์— ์ข‹์€ ์˜๊ฒฌ์ด๋ผ๊ณ  ๋จผ์ € ์นญ์ฐฌํ•˜๊ธฐ +- ๋น„๋‚œํ•˜์ง€ ๋ง๊ธฐ +- ๋งˆ์Œ์— ์•ˆ๋“œ๋Š”๊ฒŒ ์žˆ์–ด๋„ ์ข‹์€ ๋ง์„ ๋จผ์ €ํ•˜๊ณ  ๊ทธ ๋‹ค์Œ์— ๋งˆ์Œ์— ์•ˆ๋“œ๋Š” ๊ฒƒ์„ ๋งํ•˜๊ธฐ +- ๋ฆฌ์•ก์…˜ ์ž˜ํ•ด์ฃผ๊ธฐ +- ๋ฐ˜๋งํ•˜๊ธฐ +- ์„œ๋กœ ์ธ์‚ฌ ์ž˜ ํ•˜๊ธฐ +- ๋™์˜ํ•  ๋•Œ โ€œ๊ทธ๋ž˜โ€๊ฐ€ ์•„๋‹ˆ๋ผ โ€œ์ข‹์•„โ€๋ผ๊ณ  ๋งํ•˜๊ธฐ +- ์ฃผ๋ง์— ๊ธ‰ํ•œ ์‚ฌํ•ญ์ด ์•„๋‹ˆ๋ฉด Discord๋กœ ์—ฐ๋ฝํ•˜์ง€ ๋ง๊ธฐ(์ตœ๋Œ€ํ•œ ํ‰์ผ์— ~^^) +- ์ง€๊ฐ, ์กฐํ‡ด ์‹œ ๋ฏธ๋ฆฌ ์•Œ๋ ค์ฃผ๊ธฐ +
-## Moreโ“ +
+ ํšŒ๊ณ  + https://www.notion.so/bside/3f4a3606067143fbb54bd5e584afe762 +
-// ์ถ”๊ฐ€ ์ •๋ณด -# ํšŒ๊ณ  -https://www.notion.so/bside/3f4a3606067143fbb54bd5e584afe762 diff --git a/admin/index.html b/admin/index.html index 059023d..f5dc919 100644 --- a/admin/index.html +++ b/admin/index.html @@ -2,11 +2,7 @@ - + Green Light admin diff --git a/admin/public/svg/favicon.ico b/admin/public/svg/favicon.ico new file mode 100644 index 0000000..22dbbbe Binary files /dev/null and b/admin/public/svg/favicon.ico differ diff --git a/admin/src/types/eventDataType.ts b/admin/src/types/eventDataType.ts deleted file mode 100644 index 58e6545..0000000 --- a/admin/src/types/eventDataType.ts +++ /dev/null @@ -1,30 +0,0 @@ -export interface FcFsEvent { - round: number; - startTime: string; // (pattern = "yyyy-MM-dd hh:mm:ss") - endTime: string; - winnerNum: number; -} - -export interface DrawInfo { - rank: number; - winnerNum: number; - probability: string; // ("xx.xx%") -} - -export interface DrawEvent { - startDate: string; // (pattern = "yyyy-MM-dd") - endDate: string; - startTime: string; // (pattern = "hh:mm:ss") - endTime: string; - //drawInfoList: DrawInfo[]; -} - -export interface EventDataResponse { - isSuccess: boolean; - code: string; - message: string; - result?: { - fcfsEventList: FcFsEvent[]; - drawEvent: DrawEvent; - }; -} diff --git a/service/index.html b/service/index.html index d3b293d..d01eb47 100644 --- a/service/index.html +++ b/service/index.html @@ -2,11 +2,7 @@ - + { - //const counts = 3; - const defaultStyle = { background: 'rgba(255, 255, 255, 0.6)', // ํฐ์ƒ‰์˜ ํˆฌ๋ช…๋„ ์กฐ์ ˆ width: '5rem', @@ -17,61 +15,51 @@ const Attendance = ({ counts }: AttendanceProps) => { backdropFilter: 'blur(30px)', }; - const dynamicStyle = { + const createDynamicStyle = (degree: number) => ({ width: '5rem', height: '5rem', borderRadius: '50%', - background: `conic-gradient(#FFFFFF 0%, #BBE0E6 100%)`, + background: `conic-gradient(from ${degree}deg, #FFFFFF 0%, #BBE0E6 100%)`, animation: `rotateCircle 1s linear 1`, - }; + }); + + const getTextColorClass = (index: number) => + index < counts ? 'text-primary' : 'text-gray-300'; + + const getAttendanceStyle = (index: number) => + index < counts ? createDynamicStyle(index * 60) : defaultStyle; + + const lastTextColorClass = counts > 6 ? 'text-primary' : 'text-gray-300'; + const lastStyle = counts > 6 ? createDynamicStyle(0) : defaultStyle; - const lastTextColorClass = 6 < counts ? 'text-primary' : 'text-gray-300'; - const lastStyle = 6 < counts ? dynamicStyle : defaultStyle; return (
์—ฐ์† 7์ผ ์‘๋ชจํ•˜๋ฉด ํŠน๋ณ„ํ•œ ์„ ๋ฌผ์„ ๋“œ๋ ค์š”! (1์ธ 1ํšŒ ํ•œ์ •)
- {texts.map((text, index) => { - const degree = index * 60; - - const dynamicStyle = { - width: '5rem', - height: '5rem', - borderRadius: '50%', - background: `conic-gradient(from ${degree}deg, #FFFFFF 0%, #BBE0E6 100%)`, - animation: `rotateCircle 1s linear 1`, - }; - - const textColorClass = - index < counts ? 'text-primary' : 'text-gray-300'; - - const attendanceClass = index < counts ? dynamicStyle : defaultStyle; - - return ( -
-
-
- {index < counts ? ( -
- {text} -
- ) : ( -
- {text} -
- )} -
-
- {index + 1}์ผ์ฐจ + {texts.map((text, index) => ( +
+
+
+
+ {text}
- ); - })} +
+ {index + 1}์ผ์ฐจ +
+
+ ))}
@@ -83,7 +71,7 @@ const Attendance = ({ counts }: AttendanceProps) => { ? '/svg/์ถœ์„์™„๋ฃŒ/์Šค๋ฒ….svg' : '/svg/์ถœ์„๋ฏธ์™„/์Šค๋ฒ….svg' } - > + />
7์ผ์ฐจ
diff --git a/service/src/components/LotteryLounge/LotteryCanvas.tsx b/service/src/components/LotteryLounge/LotteryCanvas.tsx index 20f31fc..57571ee 100644 --- a/service/src/components/LotteryLounge/LotteryCanvas.tsx +++ b/service/src/components/LotteryLounge/LotteryCanvas.tsx @@ -1,16 +1,13 @@ -import { useRef, useEffect, useState } from 'react'; +import { useState } from 'react'; import LoseModal from './Modal/LoseModal'; -import { craftFireworks } from '@/utils/confettiCrafter'; import { useMutationDrawData } from '@/apis/draw/query'; import { getCookie } from '@/utils/cookie'; import { useUrl } from '@/store/context/useUrl'; import EventModal from '@/components/common/Modal/EventModal/EventModal'; import { DrawResultResponse, WinModal } from '@/types/lottery/type'; -import { useModalContext } from '@/store/context/useModalContext'; import { QueryClient } from '@tanstack/react-query'; import Button from '../common/Button/Button'; -import { useNavigate } from 'react-router-dom'; -import { ROUTER_PATH } from '@/constants/lib/constants'; +import { useCanvasDrawing } from '@/hooks/LotteryLounge/useCanvasDrawing'; interface LotteryCanvasProps { onScratch: (result: DrawResultResponse) => void; @@ -23,46 +20,25 @@ const LotteryCanvas = ({ remainDrawCount, handleRemainDrawCount, }: LotteryCanvasProps) => { - const { isOpen, setIsOpen } = useModalContext(); - const [isGameEnded, setIsGameEnded] = useState(false); const [isScratched, setIsScratched] = useState(false); // ์ตœ์ดˆ ๊ธ๊ธฐ ์—ฌ๋ถ€ ํ™•์ธ const [isWin, setIsWin] = useState(false); const [result, setResult] = useState(null); - const queryClient = new QueryClient(); - - const isCompeletRef = useRef(false); - - const navigation = useNavigate(); const { setUrl } = useUrl(); - - const closeModal = () => { - setIsOpen(false); - setIsGameEnded(true); - }; - const token = getCookie('accessToken'); const mutation = useMutationDrawData(token); + const queryClient = new QueryClient(); - const canvasRef = useRef(null); - const drawing = useRef(false); - - useEffect(() => { - const canvas = canvasRef.current; - const ctx = canvas?.getContext('2d'); - if (!canvas || !ctx) return; - - canvas.width = 784; - canvas.height = 400; - - ctx.fillStyle = '#FFFFFF'; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - // ์ง€์šฐ๊ธฐ ์„ค์ • - ctx.globalCompositeOperation = 'destination-out'; - ctx.lineWidth = 50; - ctx.lineJoin = 'round'; - ctx.lineCap = 'round'; - }, []); + const { + canvasRef, + draw, + endDrawing, + isOpen, + closeModal, + isGameEnded, + drawing, + handleRetryButton, + handleBackToMain, + } = useCanvasDrawing(); const startDrawing = ( e: React.MouseEvent | React.TouchEvent @@ -101,89 +77,6 @@ const LotteryCanvas = ({ draw(e); }; - const endDrawing = () => { - drawing.current = false; - if (canvasRef.current) { - canvasRef.current.getContext('2d')?.beginPath(); - } - }; - - const draw = ( - e: React.MouseEvent | React.TouchEvent - ) => { - if (!drawing.current) return; - - const canvas = canvasRef.current; - const ctx = canvas?.getContext('2d'); - if (!canvas || !ctx) return; - - const rect = canvas.getBoundingClientRect(); - const scaleX = canvas.width / rect.width; - const scaleY = canvas.height / rect.height; - - let x, y; - if ('clientX' in e) { - x = (e.clientX - rect.left) * scaleX; - y = (e.clientY - rect.top) * scaleY; - } else { - x = (e.touches[0].clientX - rect.left) * scaleX; - y = (e.touches[0].clientY - rect.top) * scaleY; - } - - ctx.lineTo(x, y); - ctx.stroke(); - ctx.beginPath(); - ctx.moveTo(x, y); - - checkErasePercentage(); - }; - - const checkErasePercentage = () => { - const canvas = canvasRef.current; - const ctx = canvas?.getContext('2d'); - if (!canvas || !ctx) return; - - const startX = 100; - const startY = 100; - const width = canvas.width - startY * 2; - const height = canvas.height - startX * 2; - const imageData = ctx.getImageData(startX, startY, width, height); - const data = imageData.data; - let erasedPixels = 0; - - for (let i = 3; i < data.length; i += 4) { - if (data[i] === 0) { - erasedPixels++; - } - } - - const erasePercentage = (erasedPixels / (width * height)) * 100; - - if (erasePercentage >= 75 && !isCompeletRef.current) { - fadeOutCanvas(); - craftFireworks(1); - isCompeletRef.current = true; - setTimeout(() => { - setIsOpen(true); - }, 1500); - } - }; - - const fadeOutCanvas = () => { - if (canvasRef.current) { - canvasRef.current.style.transition = 'opacity 1s'; - canvasRef.current.style.opacity = '0'; - canvasRef.current.style.pointerEvents = 'none'; - } - }; - - const handleRetryButton = () => { - window.location.reload(); - }; - - const handleBackToMain = () => { - navigation(ROUTER_PATH.MAIN); - }; return ( <>
@@ -238,4 +131,5 @@ const LotteryCanvas = ({ ); }; + export default LotteryCanvas; diff --git a/service/src/components/MainPage/DrawSection/DrawSection.tsx b/service/src/components/MainPage/DrawSection/DrawSection.tsx index e69f232..13534a7 100644 --- a/service/src/components/MainPage/DrawSection/DrawSection.tsx +++ b/service/src/components/MainPage/DrawSection/DrawSection.tsx @@ -6,9 +6,13 @@ import Button from '@/components/common/Button/Button'; import { motion } from 'framer-motion'; import { SCROLL_MOTION } from '@/constants/animation'; import { EventInfo } from '@/types/main/type'; -import { memo, useCallback, useEffect, useState } from 'react'; -import { useEventDateContext } from '@/store/context/useEventDateContext'; +import { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { + useEventDateContext, + useEventDateSetterContext, +} from '@/store/context/useEventDateContext'; import NotEventPeriodPage from '@/components/ErrorPage/NotEventPeriodPage'; +import { checkDrawPeriod } from '@/utils/checkDrawPeriod'; const backgroundImage = 'https://d1wv99asbppzjv.cloudfront.net/main-page/event_bg_3.webp'; @@ -30,40 +34,28 @@ const DrawSection = ({ drawEndTime, }: EventProps) => { const { startDate, endDate } = useEventDateContext(); - const today = new Date(); + const { setStartTime, setEndTime } = useEventDateSetterContext(); + const today = useMemo(() => new Date(), []); const navigator = useNavigate(); const { isLogined } = useLoginContext(); - const checkDrawPeriod = useCallback(() => { - const startPeriod = new Date(startDate); - const endPeriod = new Date(endDate); - const drawStartDateTime = new Date(today); - const [startHour, startMinute] = drawStartTime.split(':').map(Number); - drawStartDateTime.setHours(startHour, startMinute, 0, 0); - - const drawEndDateTime = new Date(today); - const [endHour, endMinute] = drawEndTime.split(':').map(Number); - drawEndDateTime.setHours(endHour, endMinute, 0, 0); - - // ์กฐ๊ฑด์„ ์ฒดํฌํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ - if ( - today >= startPeriod && - today <= endPeriod && - today >= drawStartDateTime && - today <= drawEndDateTime - ) { - return true; - } else { - return false; - } - }, [startDate, endDate, drawStartTime, drawEndTime, today]); - const [isDrawPeriod, setIsDrawPeriod] = useState(false); useEffect(() => { - const isPeriod = checkDrawPeriod(); + setStartTime(drawStartTime); + setEndTime(drawEndTime); + }, [drawEndTime, drawStartTime]); + + useEffect(() => { + const isPeriod = checkDrawPeriod( + startDate, + endDate, + drawStartTime, + drawEndTime, + today + ); setIsDrawPeriod(isPeriod); - }, [checkDrawPeriod]); + }, [today]); const goLotteryLounge = useCallback(() => { if (isDrawPeriod) { diff --git a/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx b/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx index 6406829..211263f 100644 --- a/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx +++ b/service/src/components/MainPage/EventIntroductionSection/EventIntroductionSection.tsx @@ -34,7 +34,7 @@ const EventIntroductionSection = () => { > {isOpen && (
- +
)}
diff --git a/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx b/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx index bd9d7b7..f726c41 100644 --- a/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx +++ b/service/src/components/MainPage/EventSection/LoginModal/LoginModal.tsx @@ -1,236 +1,43 @@ import './LoginModal.css'; import Button from '@/components/common/Button/Button'; import Input from '@/components/common/Input/Input'; -import { ChangeEvent, useEffect, useState, memo, useCallback } from 'react'; -import { useLoginContext } from '@/store/context/useLoginContext'; +import { memo } from 'react'; import Modal from '@/components/common/Modal/Modal'; - -import { - useMutationCode, - useMutationCodeVerification, - useMutationLogin, - useMutationTestCode, -} from '@/apis/login/query'; -import { - ConfirmVerificationRequestBody, - LoginRequestBody, -} from '@/types/Authorization/type'; -import { ErrorCode, ERROR_CODES, ERROR_MESSAGES } from '@/constants/error'; -import { parseISO, differenceInSeconds } from 'date-fns'; -import { validatePhoneNumber } from '@/utils/checkPhoneNumber'; -import { checkAuthCode } from '@/utils/checkAuthCode'; -import { useQueryClient } from '@tanstack/react-query'; -import { setCookie } from '@/utils/cookie'; -import { useUrl } from '@/store/context/useUrl'; -import { getSharedUrl } from '@/apis/shareurl/api'; - -interface CloseProps { - onClose: () => void; -} - +import { useLoginLogic } from '@/hooks/MainPage/useLoginLogic'; +import { useModalContext } from '@/store/context/useModalContext'; const checkbox = 'svg/check-off.svg'; const checked = 'svg/check-on.svg'; -const LoginModal = ({ onClose }: CloseProps) => { - const codeMutation = useMutationCode(); - //const codeMutation = useMutationTestCode(); - const codeVerificationMutation = useMutationCodeVerification(); - const loginMutation = useMutationLogin(); - - const [time, setTime] = useState({ timer: 0, minutes: 0, seconds: 0 }); - const [name, setName] = useState(''); - const [code, setCode] = useState(''); - const [phoneNumber, setPhoneNumber] = useState(''); - const [validPhoneNumber, setValidPhoneNumber] = useState(false); - const [validCode, setValidCode] = useState(false); - const [canSendCode, setCanSendCode] = useState(true); - const [codeVerified, setCodeVerified] = useState(false); - const [privacyConsent, setPrivacyConsent] = useState(false); - const [marketingConsent, setMarketingConsent] = useState(false); - - const [allValid, setAllValid] = useState(false); - - const [codeErrorMsg, setCodeErrorMsg] = useState(''); - const [validateErrorMsg, setValidateErrorMsg] = useState(''); - - const { setUrl } = useUrl(); - - const queryClient = useQueryClient(); - - const handleNameInputChange = (e: ChangeEvent) => { - e.preventDefault(); - const newValue = e.target.value.replace(/\s+/g, ''); // ๊ณต๋ฐฑ ์ œ๊ฑฐ - setName(newValue); - }; +const LoginModal = () => { + const { isOpen, setIsOpen } = useModalContext(); - const handlePhoneNumberChange = (e: ChangeEvent) => { - const rawValue = e.target.value.replace(/\D/g, '').slice(0, 11); // ์ˆซ์ž๋งŒ ์ถ”์ถœ - setPhoneNumber(rawValue); - setValidPhoneNumber(validatePhoneNumber(rawValue)); - }; - const handlePrivacyConsentChange = () => { - setPrivacyConsent((prevConsent) => !prevConsent); + const onClose = () => { + setIsOpen(!isOpen); }; - const handleMarketingConsentChange = () => { - setMarketingConsent((prevConsent) => !prevConsent); - }; - - const { setIsLogined } = useLoginContext(); - - const handleCodeInputChange = (e: ChangeEvent) => { - const filteredValue = e.target.value.slice(0, 6); // 6์ž๋ฆฌ๊นŒ์ง€ ์ œํ•œ - setValidCode(checkAuthCode(filteredValue)); - setCode(filteredValue); - }; - - const handleSendAuthCode = useCallback( - async (phoneNumber: string) => { - if (!codeVerified) { - codeMutation.mutate(phoneNumber, { - onSuccess: (response) => { - console.log('์ธ์ฆ๋ฒˆํ˜ธ ์ „์†ก ์„ฑ๊ณต:', response); - if (response.isSuccess && response.result) { - setTime({ - timer: response.result.timeLimit, - minutes: Math.floor(response.result.timeLimit / 60), - seconds: response.result.timeLimit % 60, - }); - setValidateErrorMsg(''); - const interval = setInterval(() => { - setTime((prevTime) => { - const newTimer = prevTime.timer - 1; - if (newTimer <= 0) { - clearInterval(interval); - return { ...prevTime, timer: 0, minutes: 0, seconds: 0 }; - } - return { - ...prevTime, - timer: newTimer, - minutes: Math.floor(newTimer / 60), - seconds: newTimer % 60, - }; - }); - }, 1000); - } else if (!response.isSuccess && response.code in ERROR_MESSAGES) { - setCodeErrorMsg(''); - setTime({ timer: 0, minutes: 0, seconds: 0 }); - setCanSendCode(true); - alert(ERROR_MESSAGES[response.code as ErrorCode]); - } - }, - onError: () => { - setCanSendCode(true); - }, - }); - } - }, - [codeVerified, codeMutation] - ); - - const handleVerification = useCallback( - async (body: ConfirmVerificationRequestBody) => { - codeVerificationMutation.mutate(body, { - onSuccess: async (response) => { - if (response.isSuccess) { - setCodeErrorMsg(''); - setValidateErrorMsg(''); - setCodeVerified(true); - setTime({ timer: 0, minutes: 0, seconds: 0 }); - } else { - console.log(response); - - if (response.code in ERROR_MESSAGES) { - const errorMessage = ERROR_MESSAGES[response.code as ErrorCode]; - - if (response.code === ERROR_CODES.TIMEOUT) { - setCodeErrorMsg(errorMessage); - } else if (response.code !== ERROR_CODES.RESEND_REQUIRED) { - setValidateErrorMsg(errorMessage); - } else if (response.code === ERROR_CODES.RESEND_REQUIRED) { - setValidateErrorMsg(errorMessage); - setTime({ timer: 0, minutes: 0, seconds: 0 }); - setCodeErrorMsg(''); - } - } - } - }, - }); - }, - [codeVerificationMutation] - ); - - const handleLogin = useCallback( - async (body: LoginRequestBody) => { - loginMutation.mutate(body, { - onSuccess: async (response) => { - console.log(response); - - if (response.isSuccess && response.result) { - setCodeErrorMsg(''); - setValidateErrorMsg(''); - - const expiresAt = parseISO(response.result.expiredTime); - const maxAge = differenceInSeconds(expiresAt, new Date()); - - setCookie('accessToken', response.result.accessToken, { - path: '/', - maxAge: maxAge, - secure: true, - sameSite: 'strict', - }); - - setCookie('refreshToken', response.result.refreshToken, { - path: '/', - maxAge: 604800, - secure: true, - sameSite: 'strict', - }); - - // ๋ฌดํšจํ™” ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ด - await queryClient.invalidateQueries({ - queryKey: ['sharedUrl', response.result.accessToken], - }); - - const sharedUrlData = await queryClient.fetchQuery({ - queryKey: ['sharedUrl', response.result.accessToken], - queryFn: () => getSharedUrl(response.result?.accessToken), - }); - - if (sharedUrlData.isSuccess) { - setUrl(sharedUrlData.result.shareUrl); - } - setIsLogined(true); - onClose(); - } else if (!response.isSuccess && response.code in ERROR_MESSAGES) { - alert(ERROR_MESSAGES[response.code as ErrorCode]); - resetForm(); - } - }, - }); - }, - [loginMutation, queryClient, setIsLogined, onClose] - ); - - const resetForm = () => { - setName(''); - setPhoneNumber(''); - setCode(''); - setValidCode(false); - setCodeVerified(false); - setAllValid(false); - setMarketingConsent(false); - setPrivacyConsent(false); - setCanSendCode(true); - }; - - useEffect(() => { - if (privacyConsent && codeVerified) { - setAllValid(true); - } else { - setAllValid(false); - } - }, [privacyConsent, codeVerified]); + const { + time, + name, + code, + phoneNumber, + validPhoneNumber, + validCode, + canSendCode, + codeVerified, + privacyConsent, + marketingConsent, + allValid, + codeErrorMsg, + validateErrorMsg, + handleNameInputChange, + handlePhoneNumberChange, + handlePrivacyConsentChange, + handleMarketingConsentChange, + handleCodeInputChange, + handleSendAuthCode, + handleVerification, + handleLogin, + } = useLoginLogic(onClose); return ( @@ -348,4 +155,5 @@ const LoginModal = ({ onClose }: CloseProps) => { ); }; + export default memo(LoginModal); diff --git a/service/src/components/QuizLounge/QuizContainer.tsx b/service/src/components/QuizLounge/QuizContainer.tsx index ffa11ae..020d9fc 100644 --- a/service/src/components/QuizLounge/QuizContainer.tsx +++ b/service/src/components/QuizLounge/QuizContainer.tsx @@ -11,7 +11,7 @@ import { useMutationPostAnswer } from '@/apis/quizLounge/query'; import TutorialResultModal from './TutorialResultModal'; import { ModalData, QuizContainerProps } from '@/types/quizLounge/type'; import { useModalContext } from '@/store/context/useModalContext'; -//import { ERROR_MESSAGES, ErrorCode } from '@/constants/error'; + const QuizContainer = ({ answer, mode, diff --git a/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx b/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx new file mode 100644 index 0000000..d2866d0 --- /dev/null +++ b/service/src/hooks/LotteryLounge/useCanvasDrawing.tsx @@ -0,0 +1,137 @@ +import { useRef, useEffect, useState, useCallback } from 'react'; +import { craftFireworks } from '@/utils/confettiCrafter'; +import { useModalContext } from '@/store/context/useModalContext'; +import { useNavigate } from 'react-router-dom'; +import { ROUTER_PATH } from '@/constants/lib/constants'; + +export const useCanvasDrawing = () => { + const { isOpen, setIsOpen } = useModalContext(); + + const [isGameEnded, setIsGameEnded] = useState(false); + + const isCompeletRef = useRef(false); + + const navigation = useNavigate(); + + const closeModal = () => { + setIsOpen(false); + setIsGameEnded(true); + }; + + const canvasRef = useRef(null); + const drawing = useRef(false); + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + if (!canvas || !ctx) return; + + canvas.width = 784; + canvas.height = 400; + + ctx.fillStyle = '#FFFFFF'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // ์ง€์šฐ๊ธฐ ์„ค์ • + ctx.globalCompositeOperation = 'destination-out'; + ctx.lineWidth = 50; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + }, []); + + const endDrawing = () => { + drawing.current = false; + if (canvasRef.current) { + canvasRef.current.getContext('2d')?.beginPath(); + } + }; + + const draw = ( + e: React.MouseEvent | React.TouchEvent + ) => { + if (!drawing.current) return; + + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + if (!canvas || !ctx) return; + + const rect = canvas.getBoundingClientRect(); + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + + let x, y; + if ('clientX' in e) { + x = (e.clientX - rect.left) * scaleX; + y = (e.clientY - rect.top) * scaleY; + } else { + x = (e.touches[0].clientX - rect.left) * scaleX; + y = (e.touches[0].clientY - rect.top) * scaleY; + } + + ctx.lineTo(x, y); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(x, y); + + checkErasePercentage(); + }; + + const checkErasePercentage = () => { + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + if (!canvas || !ctx) return; + + const startX = 100; + const startY = 100; + const width = canvas.width - startY * 2; + const height = canvas.height - startX * 2; + const imageData = ctx.getImageData(startX, startY, width, height); + const data = imageData.data; + let erasedPixels = 0; + + for (let i = 3; i < data.length; i += 4) { + if (data[i] === 0) { + erasedPixels++; + } + } + + const erasePercentage = (erasedPixels / (width * height)) * 100; + + if (erasePercentage >= 75 && !isCompeletRef.current) { + fadeOutCanvas(); + craftFireworks(1); + isCompeletRef.current = true; + setTimeout(() => { + setIsOpen(true); + }, 1500); + } + }; + + const fadeOutCanvas = () => { + if (canvasRef.current) { + canvasRef.current.style.transition = 'opacity 1s'; + canvasRef.current.style.opacity = '0'; + canvasRef.current.style.pointerEvents = 'none'; + } + }; + + const handleRetryButton = () => { + window.location.reload(); + }; + + const handleBackToMain = useCallback(() => { + navigation(ROUTER_PATH.MAIN); + }, [navigation]); + + return { + canvasRef, + draw, + endDrawing, + isOpen, + closeModal, + isGameEnded, + drawing, + handleRetryButton, + handleBackToMain, + }; +}; diff --git a/service/src/hooks/MainPage/useLoginLogic.tsx b/service/src/hooks/MainPage/useLoginLogic.tsx new file mode 100644 index 0000000..0569b31 --- /dev/null +++ b/service/src/hooks/MainPage/useLoginLogic.tsx @@ -0,0 +1,250 @@ +// src/hooks/useLoginTest.ts + +import { useState, useCallback, ChangeEvent, useEffect } from 'react'; +import { useLoginContext } from '@/store/context/useLoginContext'; +import { + useMutationCode, + useMutationCodeVerification, + useMutationLogin, + useMutationTestCode, +} from '@/apis/login/query'; +import { + ConfirmVerificationRequestBody, + LoginRequestBody, +} from '@/types/Authorization/type'; +import { ErrorCode, ERROR_CODES, ERROR_MESSAGES } from '@/constants/error'; +import { parseISO, differenceInSeconds } from 'date-fns'; +import { validatePhoneNumber } from '@/utils/checkPhoneNumber'; +import { checkAuthCode } from '@/utils/checkAuthCode'; +import { useQueryClient } from '@tanstack/react-query'; +import { setCookie } from '@/utils/cookie'; +import { useUrl } from '@/store/context/useUrl'; +import { getSharedUrl } from '@/apis/shareurl/api'; + +export const useLoginLogic = (onClose: () => void) => { + const codeMutation = useMutationCode(); + const codeVerificationMutation = useMutationCodeVerification(); + const loginMutation = useMutationLogin(); + + const [time, setTime] = useState({ timer: 0, minutes: 0, seconds: 0 }); + const [name, setName] = useState(''); + const [code, setCode] = useState(''); + const [phoneNumber, setPhoneNumber] = useState(''); + const [validPhoneNumber, setValidPhoneNumber] = useState(false); + const [validCode, setValidCode] = useState(false); + const [canSendCode, setCanSendCode] = useState(true); + const [codeVerified, setCodeVerified] = useState(false); + const [privacyConsent, setPrivacyConsent] = useState(false); + const [marketingConsent, setMarketingConsent] = useState(false); + const [allValid, setAllValid] = useState(false); + const [codeErrorMsg, setCodeErrorMsg] = useState(''); + const [validateErrorMsg, setValidateErrorMsg] = useState(''); + + const { setUrl } = useUrl(); + const queryClient = useQueryClient(); + const { setIsLogined } = useLoginContext(); + + const handleNameInputChange = useCallback( + (e: ChangeEvent) => { + const newValue = e.target.value.replace(/\s+/g, ''); // ๊ณต๋ฐฑ ์ œ๊ฑฐ + setName(newValue); + }, + [] + ); + + const handlePhoneNumberChange = useCallback( + (e: ChangeEvent) => { + const rawValue = e.target.value.replace(/\D/g, '').slice(0, 11); // ์ˆซ์ž๋งŒ ์ถ”์ถœ + setPhoneNumber(rawValue); + setValidPhoneNumber(validatePhoneNumber(rawValue)); + }, + [] + ); + + const handlePrivacyConsentChange = useCallback(() => { + setPrivacyConsent((prev) => !prev); + }, []); + + const handleMarketingConsentChange = useCallback(() => { + setMarketingConsent((prev) => !prev); + }, []); + + const handleCodeInputChange = useCallback( + (e: ChangeEvent) => { + const filteredValue = e.target.value.slice(0, 6); // 6์ž๋ฆฌ๊นŒ์ง€ ์ œํ•œ + setValidCode(checkAuthCode(filteredValue)); + setCode(filteredValue); + }, + [] + ); + + const handleSendAuthCode = useCallback( + async (phoneNumber: string) => { + if (!codeVerified) { + codeMutation.mutate(phoneNumber, { + onSuccess: (response) => { + console.log('์ธ์ฆ๋ฒˆํ˜ธ ์ „์†ก ์„ฑ๊ณต:', response); + + if (response.isSuccess && response.result) { + setTime({ + timer: response.result.timeLimit, + minutes: Math.floor(response.result.timeLimit / 60), + seconds: response.result.timeLimit % 60, + }); + setValidateErrorMsg(''); + const interval = setInterval(() => { + setTime((prevTime) => { + const newTimer = prevTime.timer - 1; + if (newTimer <= 0) { + clearInterval(interval); + return { ...prevTime, timer: 0, minutes: 0, seconds: 0 }; + } + return { + ...prevTime, + timer: newTimer, + minutes: Math.floor(newTimer / 60), + seconds: newTimer % 60, + }; + }); + }, 1000); + } else if (!response.isSuccess && response.code in ERROR_MESSAGES) { + setCodeErrorMsg(''); + setTime({ timer: 0, minutes: 0, seconds: 0 }); + setCanSendCode(true); + alert(ERROR_MESSAGES[response.code as ErrorCode]); + } + }, + onError: () => { + setCanSendCode(true); + }, + }); + } + }, + [codeMutation, codeVerified] + ); + + const handleVerification = useCallback( + async (body: ConfirmVerificationRequestBody) => { + codeVerificationMutation.mutate(body, { + onSuccess: async (response) => { + if (response.isSuccess) { + setCodeErrorMsg(''); + setValidateErrorMsg(''); + setCodeVerified(true); + setTime({ timer: 0, minutes: 0, seconds: 0 }); + } else { + if (response.code in ERROR_MESSAGES) { + const errorMessage = ERROR_MESSAGES[response.code as ErrorCode]; + + if (response.code === ERROR_CODES.TIMEOUT) { + setCodeErrorMsg(errorMessage); + } else if (response.code !== ERROR_CODES.RESEND_REQUIRED) { + setValidateErrorMsg(errorMessage); + } else if (response.code === ERROR_CODES.RESEND_REQUIRED) { + setValidateErrorMsg(errorMessage); + setTime({ timer: 0, minutes: 0, seconds: 0 }); + setCodeErrorMsg(''); + } + } + } + }, + }); + }, + [codeVerificationMutation] + ); + + const handleLogin = useCallback( + async (body: LoginRequestBody) => { + loginMutation.mutate(body, { + onSuccess: async (response) => { + if (response.isSuccess && response.result) { + setCodeErrorMsg(''); + setValidateErrorMsg(''); + + const expiresAt = parseISO(response.result.expiredTime); + const maxAge = differenceInSeconds(expiresAt, new Date()); + + setCookie('accessToken', response.result.accessToken, { + path: '/', + maxAge: maxAge, + secure: true, + sameSite: 'strict', + }); + + setCookie('refreshToken', response.result.refreshToken, { + path: '/', + maxAge: 604800, + secure: true, + sameSite: 'strict', + }); + + // ๋ฌดํšจํ™” ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค์‹œ ๊ฐ€์ ธ์˜ด + await queryClient.invalidateQueries({ + queryKey: ['sharedUrl', response.result.accessToken], + }); + + const sharedUrlData = await queryClient.fetchQuery({ + queryKey: ['sharedUrl', response.result.accessToken], + queryFn: () => getSharedUrl(response.result?.accessToken), + }); + + if (sharedUrlData.isSuccess) { + setUrl(sharedUrlData.result.shareUrl); + } + setIsLogined(true); + onClose(); + } else if (!response.isSuccess && response.code in ERROR_MESSAGES) { + alert(ERROR_MESSAGES[response.code as ErrorCode]); + resetForm(); + } + }, + }); + }, + [loginMutation, queryClient, onClose, setUrl, setIsLogined] + ); + + const resetForm = useCallback(() => { + setName(''); + setPhoneNumber(''); + setCode(''); + setValidCode(false); + setCodeVerified(false); + setValidPhoneNumber(false); + setAllValid(false); + setMarketingConsent(false); + setPrivacyConsent(false); + setCanSendCode(true); + }, []); + + useEffect(() => { + if (privacyConsent && codeVerified) { + setAllValid(true); + } else { + setAllValid(false); + } + }, [privacyConsent, codeVerified]); + + return { + time, + name, + code, + phoneNumber, + validPhoneNumber, + validCode, + canSendCode, + codeVerified, + privacyConsent, + marketingConsent, + allValid, + codeErrorMsg, + validateErrorMsg, + handleNameInputChange, + handlePhoneNumberChange, + handlePrivacyConsentChange, + handleMarketingConsentChange, + handleCodeInputChange, + handleSendAuthCode, + handleVerification, + handleLogin, + }; +}; diff --git a/service/src/hooks/common/useApiError.tsx b/service/src/hooks/common/useApiError.tsx index 1ef137a..198bd61 100644 --- a/service/src/hooks/common/useApiError.tsx +++ b/service/src/hooks/common/useApiError.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react'; interface ErrorResponse { status: number; // HTTP ์ƒํƒœ ์ฝ”๋“œ - errorCode: string; // ์„œ๋ฒ„ ์ •์˜ ์—๋Ÿฌ ์ฝ”๋“œ + code: string; // ์„œ๋ฒ„ ์ •์˜ ์—๋Ÿฌ ์ฝ”๋“œ message: string; // ์—๋Ÿฌ ๋ฉ”์‹œ์ง€ } @@ -24,7 +24,7 @@ export const useApiError = () => { } const httpMessage = errorResponse?.message || '์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'; - const httpErrorCode = errorResponse?.errorCode || null; + const httpErrorCode = errorResponse?.code || null; const handle = httpStatus ? statusHandlers[httpStatus] @@ -36,10 +36,8 @@ export const useApiError = () => { alert('์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); } } else if (error instanceof Error) { - //JavaScript ์—๋Ÿฌ ๊ฐ์ฒด๊ฐ€ ์ „๋‹ฌ๋œ ๊ฒฝ์šฐ alert(`์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: ${error.message}`); } else { - // ์•Œ ์ˆ˜ ์—†๋Š” ์—๋Ÿฌ alert('์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.'); } }, []); diff --git a/service/src/hooks/common/useFetch.ts b/service/src/hooks/common/useFetch.ts deleted file mode 100644 index f72aacb..0000000 --- a/service/src/hooks/common/useFetch.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { useState, useEffect } from 'react'; -import { UseFetchOptions } from '@/types/apiType'; - -interface Response { - data: T | null; - error: Error | null; - loading: boolean; -} - -function useFetch( - url: string, - { method, params, queryParams, body, headers }: UseFetchOptions -): Response { - //const [data, setData] = useState(null); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(true); - - useEffect(() => { - const fetchData = async () => { - setLoading(true); - setError(null); - - let fullUrl = url; - if (params) { - Object.keys(params).forEach((key) => { - fullUrl = fullUrl.replace(`/${key}`, String(params[key])); - }); - } - - if (queryParams) { - const queryString = new URLSearchParams( - queryParams as Record - ).toString(); - fullUrl += `?${queryString}`; - } - - const response = await fetch(fullUrl, { - method, - headers, - body: method !== 'GET' && body ? JSON.stringify(body) : null, - }); - - return response.json(); - }; - - fetchData(); - }, [url, method, params, queryParams, body, headers]); - - return { data, error, loading }; -} - -export default useFetch; diff --git a/service/src/hooks/useLoginModalDispatchContext.ts b/service/src/hooks/useLoginModalDispatchContext.ts deleted file mode 100644 index f21ce3d..0000000 --- a/service/src/hooks/useLoginModalDispatchContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Dispatch, useContext } from 'react'; - -import { LoginModalDispatchContext } from '@/store/context/loginModalContext'; -import { LoginAction } from '@/store/types/loginModalTypes'; - -export default function useLoginModalDispatchContext(): Dispatch { - const context = useContext(LoginModalDispatchContext); - if (context === null) { - throw new Error( - 'LoginModalDispatchContext must be used within a useLoginModalDispatchContext' - ); - } - return context; -} diff --git a/service/src/hooks/useLoginModalStateContext.ts b/service/src/hooks/useLoginModalStateContext.ts deleted file mode 100644 index 0e631c5..0000000 --- a/service/src/hooks/useLoginModalStateContext.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useContext } from 'react'; -import { loginModalState } from '@/store/types/loginModalTypes'; -import { LoginModalStateContext } from '@/store/context/loginModalContext'; - -export default function useLoginModalStateContext(): loginModalState { - const context = useContext(LoginModalStateContext); - if (context === null) { - throw new Error( - 'LoginModalStateContext must be used within a useLoginModalStateContext' - ); - } - return context; -} diff --git a/service/src/pages/LotteryLounge.tsx b/service/src/pages/LotteryLounge.tsx index 1d46939..e0b5985 100644 --- a/service/src/pages/LotteryLounge.tsx +++ b/service/src/pages/LotteryLounge.tsx @@ -11,6 +11,7 @@ import { useTabContext } from '@/store/context/useTabContext'; import LoadingPage from '@/components/Loading/Loading'; import { useEventDateContext } from '@/store/context/useEventDateContext'; import NotEventPeriodPage from '@/components/ErrorPage/NotEventPeriodPage'; +import { checkDrawPeriod } from '@/utils/checkDrawPeriod'; const backgroundImage = 'https://d1wv99asbppzjv.cloudfront.net/main-page/draw_bg.webp'; @@ -19,7 +20,7 @@ const sample = () => {}; const LotteryLoungePage = () => { const token = getCookie('accessToken'); - const { startDate, endDate } = useEventDateContext(); + const { startDate, endDate, startTime, endTime } = useEventDateContext(); const { data, isLoading } = useQueryGetDrawAttendance(token); const [drawResult, setDrawResult] = useState(null); const [isScratched, setIsScratched] = useState(false); @@ -52,16 +53,13 @@ const LotteryLoungePage = () => { setDrawResult(result); setIsScratched(true); }; - - const startPeriod = new Date(startDate); - const endPeriod = new Date(endDate); const today = new Date(); if (isLoading) { return ; } - if (today < startPeriod || today > endPeriod) { + if (!checkDrawPeriod(startDate, endDate, startTime, endTime, today)) { return ; } @@ -101,21 +99,14 @@ const LotteryLoungePage = () => { )) ) : ( <> - SVG 1 - SVG 2 - SVG 3 + {[...Array(3)].map((_, index) => ( + {`SVG + ))} )}
diff --git a/service/src/pages/ShareHandler.tsx b/service/src/pages/ShareHandlerPage.tsx similarity index 86% rename from service/src/pages/ShareHandler.tsx rename to service/src/pages/ShareHandlerPage.tsx index d55341b..1d55077 100644 --- a/service/src/pages/ShareHandler.tsx +++ b/service/src/pages/ShareHandlerPage.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import LoadingPage from '../components/Loading/Loading'; -const ShareHandler = () => { +const ShareHandlerPage = () => { const { code } = useParams<{ code: string }>(); const navigate = useNavigate(); @@ -18,4 +18,4 @@ const ShareHandler = () => { return ; }; -export default ShareHandler; +export default ShareHandlerPage; diff --git a/service/src/router.tsx b/service/src/router.tsx index 91514e0..35fc30a 100644 --- a/service/src/router.tsx +++ b/service/src/router.tsx @@ -8,7 +8,7 @@ import CommentsLoungePage from './pages/CommentsLoungePage'; import ErrorBoundaryPage from './components/ErrorPage/ErrorBoundaryPage'; import NotFoundPage from './components/ErrorPage/NotFoundPage'; import ProtectedRoute from '@/components/common/ProtectedRoute/ProtectedRoute'; -import ShareHandler from './pages/ShareHandler'; +import ShareHandlerPage from './pages/ShareHandlerPage'; import WinningResultPage from './pages/WinningResultPage'; export const router = createBrowserRouter([ @@ -43,7 +43,7 @@ export const router = createBrowserRouter([ path: ROUTER_PATH.COMMENTS_LOUNGE, }, { - element: , + element: , path: ROUTER_PATH.SHARE, }, { diff --git a/service/src/store/context/loginModalContext.tsx b/service/src/store/context/loginModalContext.tsx deleted file mode 100644 index 4699993..0000000 --- a/service/src/store/context/loginModalContext.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { createContext, Dispatch, ReactNode, useReducer } from 'react'; -import { LoginAction, LoginModalState } from '../types/loginModalTypes'; -import { loginModalReducer } from '../reducer/loginModalReducer'; - -const initialState: LoginModalState = { - name: '', - phoneNumber: '', - verificationCode: '', - isVerified: false, - isChecked: false, - marketingChecked: false, - allValid: false, -}; - -export const LoginModalStateContext = createContext( - null -); -export const LoginModalDispatchContext = - createContext | null>(null); - -export const LoginModalProvider = ({ children }: { children: ReactNode }) => { - const [state, dispatch] = useReducer(loginModalReducer, initialState); - - return ( - - - {children} - - - ); -}; diff --git a/service/src/store/context/useEventDateContext.tsx b/service/src/store/context/useEventDateContext.tsx index 3b3c87d..9c6a7b2 100644 --- a/service/src/store/context/useEventDateContext.tsx +++ b/service/src/store/context/useEventDateContext.tsx @@ -3,11 +3,15 @@ import { createContext, useContext } from 'react'; interface EventDateContextType { startDate: string; endDate: string; + startTime: string; + endTime: string; } interface EventDateSetterContextType { setStartDate: (date: string) => void; setEndDate: (date: string) => void; + setStartTime: (date: string) => void; + setEndTime: (date: string) => void; } export const EventDateContext = createContext( diff --git a/service/src/store/provider/EventDateProvider.tsx b/service/src/store/provider/EventDateProvider.tsx index b3bb83c..23a99e2 100644 --- a/service/src/store/provider/EventDateProvider.tsx +++ b/service/src/store/provider/EventDateProvider.tsx @@ -15,18 +15,28 @@ export const EventDateProvider = ({ children }: EventDateProviderProps) => { const [endDate, setEndDate] = useState(() => { return localStorage.getItem('endDate') || ''; }); + const [startTime, setStartTime] = useState(() => { + return localStorage.getItem('startTime') || ''; + }); + const [endTime, setEndTime] = useState(() => { + return localStorage.getItem('endTime') || ''; + }); useEffect(() => { - localStorage.setItem('startDate', startDate); - }, [startDate]); + localStorage.setItem('startTime', startTime); + }, [startTime]); useEffect(() => { - localStorage.setItem('endDate', endDate); - }, [endDate]); + localStorage.setItem('endTime', endTime); + }, [endTime]); return ( - - + + {children} diff --git a/service/src/store/reducer/loginModalReducer.tsx b/service/src/store/reducer/loginModalReducer.tsx deleted file mode 100644 index aa12925..0000000 --- a/service/src/store/reducer/loginModalReducer.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { - LoginModalState, - LoginAction, - LOGIN_ACTION, -} from '../types/loginModalTypes'; - -export const loginModalReducer = ( - state: LoginModalState, - action: LoginAction -): LoginModalState => { - switch (action.type) { - case LOGIN_ACTION.SET_VERIFICATION_CODE: - return { ...state, verificationCode: action.payload }; - case LOGIN_ACTION.SET_VERIFIED: - return { - ...state, - isVerified: action.payload, - allValid: action.payload && state.isChecked, - }; - case LOGIN_ACTION.SET_CHECKED: - return { - ...state, - isChecked: action.payload, - allValid: action.payload && state.isVerified, - }; - case LOGIN_ACTION.SET_MARKETING_CHECKED: - return { ...state, marketingChecked: action.payload }; - case LOGIN_ACTION.SET_NAME: - return { ...state, name: action.payload }; - case LOGIN_ACTION.SET_PHONE_NUMBER: - return { ...state, phoneNumber: action.payload }; - default: - return state; - } -}; diff --git a/service/src/store/reducer/modalReducer.tsx b/service/src/store/reducer/modalReducer.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/service/src/store/types/loginModalTypes.ts b/service/src/store/types/loginModalTypes.ts deleted file mode 100644 index e007c6f..0000000 --- a/service/src/store/types/loginModalTypes.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface LoginModalState { - name: string; - phoneNumber: string; - verificationCode: string; - isVerified: boolean; - isChecked: boolean; - marketingChecked: boolean; - allValid: boolean; -} - -export type LoginAction = - | { type: LOGIN_ACTION.SET_VERIFIED; payload: boolean } - | { type: LOGIN_ACTION.SET_CHECKED; payload: boolean } - | { type: LOGIN_ACTION.SET_MARKETING_CHECKED; payload: boolean } - | { type: LOGIN_ACTION.SET_NAME; payload: string } - | { type: LOGIN_ACTION.SET_PHONE_NUMBER; payload: string } - | { type: LOGIN_ACTION.SET_VERIFICATION_CODE; payload: string }; - -export enum LOGIN_ACTION { - SET_VERIFIED = 'SET_VERIFIED', - SET_CHECKED = 'SET_CHECKED', - SET_MARKETING_CHECKED = 'SET_MARKETING_CHECKED', - SET_NAME = 'SET_NAME', - SET_PHONE_NUMBER = 'SET_PHONE_NUMBER', - SET_VERIFICATION_CODE = 'SET_VERIFICATION_CODE', -} diff --git a/service/src/types/Authorization/type.ts b/service/src/types/Authorization/type.ts index 5f91204..6a90dd1 100644 --- a/service/src/types/Authorization/type.ts +++ b/service/src/types/Authorization/type.ts @@ -1,3 +1,5 @@ +import { ResponseType } from '../apiType'; + export interface SendCodeRequestBody { phoneNumber: string; } @@ -14,25 +16,15 @@ export interface LoginRequestBody { marketingConsent: boolean; } -export type CodeResponse = { - isSuccess: boolean; - code: string; - message: string; +export interface CodeResponse extends ResponseType { result?: { timeLimit: number; }; -}; - -export interface ConfirmResponse { - isSuccess: boolean; - code: string; - message: string; } -export interface LoginResponse { - isSuccess: boolean; - code: string; - message: string; +export interface ConfirmResponse extends ResponseType {} + +export interface LoginResponse extends ResponseType { result?: { accessToken: string; refreshToken: string; diff --git a/service/src/types/apiType.ts b/service/src/types/apiType.ts index d065b72..94d596d 100644 --- a/service/src/types/apiType.ts +++ b/service/src/types/apiType.ts @@ -1,22 +1,3 @@ -import { HEADERS } from '@/constants/lib/constants'; - -export interface RequestType { - url: string; - method: methodType; - params?: string; - queryParams?: Record; - body?: T; -} -export interface UseFetchOptions { - method: methodType; - params?: Record; - queryParams?: Record; - body?: BodyInit | null; - headers?: typeof HEADERS; -} - -export type methodType = 'GET' | 'POST' | 'PUT' | 'DELETE'; - export interface ResponseType { isSuccess: boolean; code: string; diff --git a/service/src/types/comment/type.ts b/service/src/types/comment/type.ts index f911d2f..2433177 100644 --- a/service/src/types/comment/type.ts +++ b/service/src/types/comment/type.ts @@ -1,13 +1,12 @@ +import { ResponseType } from '../apiType'; + export interface CommentsType { commentType: number; isMine: boolean; nickName: string; } -export interface CommentsResponseType { - isSuccess: boolean; - code: string; - message: string; +export interface CommentsResponseType extends ResponseType { result: { nextCursor: number; totalComments: number; diff --git a/service/src/utils/checkDrawPeriod.ts b/service/src/utils/checkDrawPeriod.ts new file mode 100644 index 0000000..ee3d3fd --- /dev/null +++ b/service/src/utils/checkDrawPeriod.ts @@ -0,0 +1,25 @@ +export const checkDrawPeriod = ( + startDate: string, + endDate: string, + drawStartTime: string, + drawEndTime: string, + today: Date +): boolean => { + const startPeriod = new Date(startDate); + const endPeriod = new Date(endDate); + const drawStartDateTime = new Date(today); + + const [startHour, startMinute] = drawStartTime.split(':').map(Number); + drawStartDateTime.setHours(startHour, startMinute, 0, 0); + + const drawEndDateTime = new Date(today); + const [endHour, endMinute] = drawEndTime.split(':').map(Number); + drawEndDateTime.setHours(endHour, endMinute, 0, 0); + + return ( + today >= startPeriod && + today <= endPeriod && + today >= drawStartDateTime && + today <= drawEndDateTime + ); +};