From 559d82ec54f44a63cfa5b3ad4f97f21afe5ee485 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Fri, 27 Dec 2024 22:35:18 +0900 Subject: [PATCH 01/27] =?UTF-8?q?feat:=20tailwind=20css=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/package-lock.json | 938 +++++++++++++++++++++++++++++++++++- newlearn/package.json | 3 + newlearn/src/App.css | 42 -- newlearn/src/App.tsx | 45 +- newlearn/src/index.css | 71 +-- newlearn/tailwind.config.js | 10 + newlearn/vite.config.ts | 8 +- 7 files changed, 965 insertions(+), 152 deletions(-) create mode 100644 newlearn/tailwind.config.js diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index 19da3cd..a9ccdad 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -18,9 +18,12 @@ "@typescript-eslint/parser": "^7.8.0", "@vite-pwa/assets-generator": "^0.2.4", "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.20", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", "vite-plugin-pwa": "^0.20.5", @@ -28,6 +31,19 @@ "workbox-window": "^7.1.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -2207,6 +2223,53 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", @@ -2309,6 +2372,17 @@ "node": ">= 8" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", @@ -3076,6 +3150,34 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3149,6 +3251,44 @@ "node": ">= 4.0.0" } }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3303,6 +3443,19 @@ ], "license": "MIT" }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -3473,6 +3626,16 @@ "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001690", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", @@ -3511,6 +3674,44 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -3650,6 +3851,19 @@ "node": ">=8" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3854,6 +4068,13 @@ "node": ">=8" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3867,6 +4088,13 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -3895,6 +4123,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3918,6 +4153,13 @@ "dev": true, "license": "ISC" }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -4536,6 +4778,37 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -5087,6 +5360,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-boolean-object": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.1.tgz", @@ -5194,6 +5480,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-function": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", @@ -5485,6 +5781,22 @@ "dev": true, "license": "ISC" }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -5668,6 +5980,26 @@ "node": ">= 0.8.0" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5817,6 +6149,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -5831,6 +6173,18 @@ "dev": true, "license": "MIT" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -5891,6 +6245,46 @@ "dev": true, "license": "MIT" }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -5995,6 +6389,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6045,16 +6446,40 @@ "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, "engines": { - "node": ">=8" - } - }, + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6075,6 +6500,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -6114,6 +6559,127 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -6314,6 +6880,16 @@ "node": ">=0.10.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -6329,6 +6905,19 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.9.tgz", @@ -6851,6 +7440,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -7002,6 +7604,76 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -7117,6 +7789,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", @@ -7140,6 +7826,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7166,6 +7906,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tar-fs": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", @@ -7258,6 +8036,29 @@ "dev": true, "license": "MIT" }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tinyglobby": { "version": "0.2.10", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", @@ -7343,6 +8144,13 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -8206,6 +9014,107 @@ "workbox-core": "7.3.0" } }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -8220,6 +9129,19 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 432ce66..3b9f636 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -21,9 +21,12 @@ "@typescript-eslint/parser": "^7.8.0", "@vite-pwa/assets-generator": "^0.2.4", "@vitejs/plugin-react": "^4.2.1", + "autoprefixer": "^10.4.20", "eslint": "^8.57.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", "vite-plugin-pwa": "^0.20.5", diff --git a/newlearn/src/App.css b/newlearn/src/App.css index b9d355d..e69de29 100644 --- a/newlearn/src/App.css +++ b/newlearn/src/App.css @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index 78ec3ba..84fd787 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -1,37 +1,16 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import appLogo from '/favicon.svg' -import PWABadge from './PWABadge.tsx' -import './App.css' +import "./App.css"; function App() { - const [count, setCount] = useState(0) - - return ( - <> -
- - newlearn logo - - - React logo - -
-

newlearn

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- - - ) + return ( + <> +
+ +
+
+ + ); } -export default App +export default App; diff --git a/newlearn/src/index.css b/newlearn/src/index.css index 6119ad9..b5c61c9 100644 --- a/newlearn/src/index.css +++ b/newlearn/src/index.css @@ -1,68 +1,3 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/newlearn/tailwind.config.js b/newlearn/tailwind.config.js new file mode 100644 index 0000000..4a08ce4 --- /dev/null +++ b/newlearn/tailwind.config.js @@ -0,0 +1,10 @@ +module.exports = { + content: [ + "./index.html", // Vite의 루트 HTML 파일 + "./src/**/*.{js,ts,jsx,tsx}", // 모든 React 컴포넌트 파일 + ], + theme: { + extend: {}, // 커스텀 테마 추가 시 사용 + }, + plugins: [], // 필요한 Tailwind 플러그인 추가 가능 +}; diff --git a/newlearn/vite.config.ts b/newlearn/vite.config.ts index c8c123f..895f749 100644 --- a/newlearn/vite.config.ts +++ b/newlearn/vite.config.ts @@ -1,6 +1,7 @@ import { VitePWA } from "vite-plugin-pwa"; import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import tailwindcss from "tailwindcss"; // https://vitejs.dev/config/ export default defineConfig({ @@ -38,11 +39,16 @@ export default defineConfig({ }, devOptions: { - enabled: false, + enabled: true, navigateFallback: "index.html", suppressWarnings: true, type: "module", }, }), ], + css: { + postcss: { + plugins: [tailwindcss()], + }, + }, }); From 0f10e9bddc77505cb2dde7f9788f2eaa8152b83b Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 28 Dec 2024 00:54:24 +0900 Subject: [PATCH 02/27] =?UTF-8?q?setting:=20prettier,=20eslint=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/.eslintrc.cjs | 3 ++- newlearn/.prettierrc | 11 +++++++++++ newlearn/package-lock.json | 31 +++++++++++++++++++++++++++++++ newlearn/package.json | 2 ++ newlearn/pwa-assets.config.ts | 18 +++++++++--------- newlearn/src/App.tsx | 23 ++++++++++++----------- 6 files changed, 67 insertions(+), 21 deletions(-) create mode 100644 newlearn/.prettierrc diff --git a/newlearn/.eslintrc.cjs b/newlearn/.eslintrc.cjs index d6c9537..ab36528 100644 --- a/newlearn/.eslintrc.cjs +++ b/newlearn/.eslintrc.cjs @@ -5,6 +5,7 @@ module.exports = { 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', + 'prettier', ], ignorePatterns: ['dist', '.eslintrc.cjs'], parser: '@typescript-eslint/parser', @@ -15,4 +16,4 @@ module.exports = { { allowConstantExport: true }, ], }, -} +}; diff --git a/newlearn/.prettierrc b/newlearn/.prettierrc new file mode 100644 index 0000000..9ad9a45 --- /dev/null +++ b/newlearn/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": true, + "semi": true, + "useTabs": false, + "tabWidth": 2, + "trailingComma": "all", + "printWidth": 80, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "auto" +} diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index a9ccdad..1c8e465 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -20,9 +20,11 @@ "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.20", "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", "postcss": "^8.4.49", + "prettier": "^3.4.2", "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", @@ -4418,6 +4420,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-react-hooks": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", @@ -6747,6 +6762,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 3b9f636..055408e 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -23,9 +23,11 @@ "@vitejs/plugin-react": "^4.2.1", "autoprefixer": "^10.4.20", "eslint": "^8.57.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", "postcss": "^8.4.49", + "prettier": "^3.4.2", "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", diff --git a/newlearn/pwa-assets.config.ts b/newlearn/pwa-assets.config.ts index 452b31f..42f9267 100644 --- a/newlearn/pwa-assets.config.ts +++ b/newlearn/pwa-assets.config.ts @@ -1,12 +1,12 @@ import { - defineConfig, - minimal2023Preset as preset, -} from '@vite-pwa/assets-generator/config' + defineConfig, + minimal2023Preset as preset, +} from '@vite-pwa/assets-generator/config'; export default defineConfig({ - headLinkOptions: { - preset: '2023', - }, - preset, - images: ['public/favicon.svg'], -}) + headLinkOptions: { + preset: '2023', + }, + preset, + images: ['public/favicon.svg'], +}); diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index 84fd787..f39bcd8 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -1,16 +1,17 @@ -import "./App.css"; +import './App.css'; function App() { - return ( - <> -
- -
-
- - ); + return ( + <> +
+ +
+
takdjfkjdk
+
+ + ); } export default App; From c80547f80c4072403bfa3af4f58f39751f3b2e51 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 28 Dec 2024 23:27:40 +0900 Subject: [PATCH 03/27] =?UTF-8?q?feat:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/App.tsx | 8 ++--- .../components/categories/categoryItem.tsx | 30 +++++++++++++++++++ .../components/categories/categoryLayout.tsx | 15 ++++++++++ newlearn/src/constants/category.ts | 22 ++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 newlearn/src/components/categories/categoryItem.tsx create mode 100644 newlearn/src/components/categories/categoryLayout.tsx create mode 100644 newlearn/src/constants/category.ts diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index f39bcd8..52ef445 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -1,14 +1,12 @@ import './App.css'; +import CategoryLayout from './components/categories/categoryLayout'; function App() { return ( <> -
- +
-
takdjfkjdk
+
); diff --git a/newlearn/src/components/categories/categoryItem.tsx b/newlearn/src/components/categories/categoryItem.tsx new file mode 100644 index 0000000..8c660b0 --- /dev/null +++ b/newlearn/src/components/categories/categoryItem.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react'; + +type CategoryItemProps = { + name: string; // 카테고리 이름 + id: number; // 카테고리 ID +}; + +const CategoryItem: React.FC = ({ name, id }) => { + const [isSelected, setIsSelected] = useState(false); + + const handleClick = () => { + setIsSelected((prev) => !prev); // 선택 상태 토글 + }; + + return ( +
+ {name} +
+ ); +}; + +export default CategoryItem; diff --git a/newlearn/src/components/categories/categoryLayout.tsx b/newlearn/src/components/categories/categoryLayout.tsx new file mode 100644 index 0000000..467a009 --- /dev/null +++ b/newlearn/src/components/categories/categoryLayout.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import CategoryItem from './categoryItem'; // categoryItem 컴포넌트 +import { CATEGORY_NAMES } from '../../constants/category.ts'; // 카테고리 상수 가져오기 + +const CategoryLayout: React.FC = () => { + return ( +
+ {Object.entries(CATEGORY_NAMES).map(([id, name]) => ( + + ))} +
+ ); +}; + +export default CategoryLayout; diff --git a/newlearn/src/constants/category.ts b/newlearn/src/constants/category.ts new file mode 100644 index 0000000..b443a3c --- /dev/null +++ b/newlearn/src/constants/category.ts @@ -0,0 +1,22 @@ +export const CATEGORIES = { + POLITICS: 1, // 정치 + ECONOMY: 2, // 경제 + SOCIETY: 3, // 사회 + CULTURE: 4, // 생활/문화 + WORLD: 5, // 세계 + TECHNOLOGY: 6, // 기술/IT + ENTERTAINMENT: 7, // 연예 + SPORTS: 8, // 스포츠 +} as const; + +// 역으로 숫자 값을 통해 카테고리 이름을 얻고 싶을 때 +export const CATEGORY_NAMES: { [key: number]: string } = { + 1: '정치', + 2: '경제', + 3: '사회', + 4: '생활/문화', + 5: '세계', + 6: '기술/IT', + 7: '연예', + 8: '스포츠', +}; From 94235db9c769ae1f8935b254ce6713f621a7d620 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sun, 29 Dec 2024 01:16:11 +0900 Subject: [PATCH 04/27] =?UTF-8?q?feat:=20=ED=99=88=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/App.tsx | 6 +++--- .../components/categories/categoryItem.tsx | 4 ++-- .../components/categories/categoryLayout.tsx | 11 ++++++++-- newlearn/src/index.css | 10 +++++++++ newlearn/src/pages/HomePage.tsx/index.tsx | 21 +++++++++++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 newlearn/src/pages/HomePage.tsx/index.tsx diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index 52ef445..1f99bed 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -1,12 +1,12 @@ import './App.css'; -import CategoryLayout from './components/categories/categoryLayout'; +import HomePage from './pages/HomePage.tsx'; function App() { return ( <> -
+
- +
); diff --git a/newlearn/src/components/categories/categoryItem.tsx b/newlearn/src/components/categories/categoryItem.tsx index 8c660b0..9dc0e6f 100644 --- a/newlearn/src/components/categories/categoryItem.tsx +++ b/newlearn/src/components/categories/categoryItem.tsx @@ -5,7 +5,7 @@ type CategoryItemProps = { id: number; // 카테고리 ID }; -const CategoryItem: React.FC = ({ name, id }) => { +const CategoryItem: React.FC = ({ name }) => { const [isSelected, setIsSelected] = useState(false); const handleClick = () => { @@ -15,7 +15,7 @@ const CategoryItem: React.FC = ({ name, id }) => { return (
{ +type Layout = { + layout: string; +}; + +const CategoryLayout: React.FC = ({ layout }) => { return ( -
+
{Object.entries(CATEGORY_NAMES).map(([id, name]) => ( ))} diff --git a/newlearn/src/index.css b/newlearn/src/index.css index b5c61c9..7af71b1 100644 --- a/newlearn/src/index.css +++ b/newlearn/src/index.css @@ -1,3 +1,13 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer utilities { + .hide-scrollbar { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + } + .hide-scrollbar::-webkit-scrollbar { + display: none; /* Chrome, Safari, Opera */ + } +} diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx new file mode 100644 index 0000000..65eccc7 --- /dev/null +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -0,0 +1,21 @@ +import CategoryLayout from '../../components/categories/categoryLayout'; + +const HomePage = () => { + return ( + <> +
+
+ 12월 29일 + +
+ +
+
+
메인기사 3개 케러셀
+
카드뉴스 목록
+
+ + ); +}; + +export default HomePage; From b2cf4aaebd2cf39ca2a482f2a5a71545a806def0 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 16:07:07 +0900 Subject: [PATCH 05/27] =?UTF-8?q?feat:=20=EC=B9=B4=EB=93=9C=EB=89=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=20=EB=B0=8F=20svg=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=ED=99=94=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 10 + newlearn/package-lock.json | 394 ++++++++++++++++++++++ newlearn/package.json | 1 + newlearn/src/App.tsx | 2 +- newlearn/src/assets/arrow-right.svg | 1 + newlearn/src/assets/image.svg | 1 + newlearn/src/components/cardnews.tsx | 32 ++ newlearn/src/pages/HomePage.tsx/index.tsx | 11 +- newlearn/src/vite-env.d.ts | 1 + newlearn/tsconfig.json | 1 + newlearn/type.d.ts | 0 newlearn/vite.config.ts | 94 +++--- 12 files changed, 500 insertions(+), 48 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 newlearn/src/assets/arrow-right.svg create mode 100644 newlearn/src/assets/image.svg create mode 100644 newlearn/src/components/cardnews.tsx create mode 100644 newlearn/type.d.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0b3081d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + // 파일을 저장할 때마다 `eslint` 규칙에 따라 자동으로 코드를 수정 + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + // `prettier`를 기본 포맷터로 지정 + "editor.defaultFormatter": "esbenp.prettier-vscode", + // 파일을 저장할 때마다 포매팅 실행 + "editor.formatOnSave": true +} diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index 1c8e465..c2d4962 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -29,6 +29,7 @@ "typescript": "^5.2.2", "vite": "^5.2.10", "vite-plugin-pwa": "^0.20.5", + "vite-plugin-svgr": "^4.3.0", "workbox-core": "^7.1.0", "workbox-window": "^7.1.0" } @@ -2748,6 +2749,231 @@ "string.prototype.matchall": "^4.0.6" } }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3628,6 +3854,19 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -3828,6 +4067,33 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4110,6 +4376,17 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4172,6 +4449,36 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, "node_modules/es-abstract": { "version": "1.23.6", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.6.tgz", @@ -5904,6 +6211,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -6071,6 +6385,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6233,6 +6557,17 @@ "dev": true, "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-abi": { "version": "3.71.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", @@ -6424,6 +6759,25 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7558,6 +7912,17 @@ "dev": true, "license": "MIT" }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -7937,6 +8302,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, "node_modules/tailwindcss": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", @@ -8182,6 +8554,13 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -8564,6 +8943,21 @@ } } }, + "node_modules/vite-plugin-svgr": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.3.0.tgz", + "integrity": "sha512-Jy9qLB2/PyWklpYy0xk0UU3TlU0t2UMpJXZvf+hWII1lAmRHrOUKi11Uw8N3rxoNk7atZNYO3pR3vI1f7oi+6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.3", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 055408e..72f2f34 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -32,6 +32,7 @@ "typescript": "^5.2.2", "vite": "^5.2.10", "vite-plugin-pwa": "^0.20.5", + "vite-plugin-svgr": "^4.3.0", "workbox-core": "^7.1.0", "workbox-window": "^7.1.0" }, diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index 1f99bed..0491dad 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -5,7 +5,7 @@ function App() { return ( <>
-
+
diff --git a/newlearn/src/assets/arrow-right.svg b/newlearn/src/assets/arrow-right.svg new file mode 100644 index 0000000..c9e49b1 --- /dev/null +++ b/newlearn/src/assets/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/newlearn/src/assets/image.svg b/newlearn/src/assets/image.svg new file mode 100644 index 0000000..15c1a73 --- /dev/null +++ b/newlearn/src/assets/image.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/newlearn/src/components/cardnews.tsx b/newlearn/src/components/cardnews.tsx new file mode 100644 index 0000000..13bb733 --- /dev/null +++ b/newlearn/src/components/cardnews.tsx @@ -0,0 +1,32 @@ +import Arrow from '../assets/arrow-right.svg?react'; +import Image from '../assets/image.svg?react'; + +type CardNews = { + thumbnail: string; + title: string; + time: string; + agency: string; +}; +const Cardnews = ({ thumbnail, title, time, agency }: CardNews) => { + const commonStyles = 'w-40 h-30 bg-[#EAF2FF]'; + return ( +
+ {thumbnail ? ( + + ) : ( + + )} +
+
+ {title} +
+ {time}, {agency} +
+
+ +
+
+ ); +}; + +export default Cardnews; diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 65eccc7..0907138 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,3 +1,4 @@ +import Cardnews from '../../components/cardnews'; import CategoryLayout from '../../components/categories/categoryLayout'; const HomePage = () => { @@ -12,7 +13,15 @@ const HomePage = () => {
메인기사 3개 케러셀
-
카드뉴스 목록
+
+ 카드뉴스 목록 + +
); diff --git a/newlearn/src/vite-env.d.ts b/newlearn/src/vite-env.d.ts index ec878b7..f40765c 100644 --- a/newlearn/src/vite-env.d.ts +++ b/newlearn/src/vite-env.d.ts @@ -1,2 +1,3 @@ /// /// +/// diff --git a/newlearn/tsconfig.json b/newlearn/tsconfig.json index a7fc6fb..74d7195 100644 --- a/newlearn/tsconfig.json +++ b/newlearn/tsconfig.json @@ -5,6 +5,7 @@ "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, + "types": ["vite-plugin-svgr/client"], /* Bundler mode */ "moduleResolution": "bundler", diff --git a/newlearn/type.d.ts b/newlearn/type.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/newlearn/vite.config.ts b/newlearn/vite.config.ts index 895f749..1e25e1b 100644 --- a/newlearn/vite.config.ts +++ b/newlearn/vite.config.ts @@ -1,54 +1,56 @@ -import { VitePWA } from "vite-plugin-pwa"; -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react"; -import tailwindcss from "tailwindcss"; +import { VitePWA } from 'vite-plugin-pwa'; +import { defineConfig } from 'vite'; +import svgr from 'vite-plugin-svgr'; +import react from '@vitejs/plugin-react'; +import tailwindcss from 'tailwindcss'; // https://vitejs.dev/config/ export default defineConfig({ - base: "/", - plugins: [ - react(), - VitePWA({ - registerType: "autoUpdate", - injectRegister: false, + base: '/', + plugins: [ + react(), + svgr(), + VitePWA({ + registerType: 'autoUpdate', + injectRegister: false, - manifest: { - name: "newlearn", - short_name: "newlearn", - description: "어려운 뉴스 기사를 쉽고 빠르게", - theme_color: "#ffffff", - start_url: "/", - icons: [ - { - src: "icons/apple-touch-icon-60x60.png", - sizes: "60x60", - type: "image/png", - }, - { - src: "icons/apple-touch-icon-152x152.png", - sizes: "192x192", - type: "image/png", - }, - ], - }, + manifest: { + name: 'newlearn', + short_name: 'newlearn', + description: '어려운 뉴스 기사를 쉽고 빠르게', + theme_color: '#ffffff', + start_url: '/', + icons: [ + { + src: 'icons/apple-touch-icon-60x60.png', + sizes: '60x60', + type: 'image/png', + }, + { + src: 'icons/apple-touch-icon-152x152.png', + sizes: '192x192', + type: 'image/png', + }, + ], + }, - workbox: { - globPatterns: ["**/*.{js,css,html,svg,png,ico,json}"], - cleanupOutdatedCaches: true, - clientsClaim: true, - }, + workbox: { + globPatterns: ['**/*.{js,css,html,svg,png,ico,json}'], + cleanupOutdatedCaches: true, + clientsClaim: true, + }, - devOptions: { - enabled: true, - navigateFallback: "index.html", - suppressWarnings: true, - type: "module", - }, - }), - ], - css: { - postcss: { - plugins: [tailwindcss()], - }, + devOptions: { + enabled: true, + navigateFallback: 'index.html', + suppressWarnings: true, + type: 'module', + }, + }), + ], + css: { + postcss: { + plugins: [tailwindcss()], }, + }, }); From 4f16ef4b071eb75152df40dbea30cd9aaf1b5cbc Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 18:46:52 +0900 Subject: [PATCH 06/27] =?UTF-8?q?feat:=20Main=20=EA=B8=B0=EC=82=AC=20?= =?UTF-8?q?=EC=BC=80=EB=9F=AC=EC=85=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/package-lock.json | 84 ++++++++++++++++++- newlearn/package.json | 5 +- newlearn/src/App.tsx | 1 - newlearn/src/components/carousel/carousel.tsx | 45 ++++++++++ newlearn/src/components/carousel/mainNews.tsx | 31 +++++++ newlearn/src/main.tsx | 13 +-- newlearn/src/pages/HomePage.tsx/index.tsx | 31 +++++-- 7 files changed, 192 insertions(+), 18 deletions(-) create mode 100644 newlearn/src/components/carousel/carousel.tsx create mode 100644 newlearn/src/components/carousel/mainNews.tsx diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index c2d4962..0b07107 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -9,11 +9,13 @@ "version": "0.0.0", "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-slick": "^0.30.3" }, "devDependencies": { "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", + "@types/react-slick": "^0.23.13", "@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/parser": "^7.8.0", "@vite-pwa/assets-generator": "^0.2.4", @@ -25,6 +27,7 @@ "eslint-plugin-react-refresh": "^0.4.6", "postcss": "^8.4.49", "prettier": "^3.4.2", + "slick-carousel": "^1.8.1", "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", @@ -3054,6 +3057,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-slick": { + "version": "0.23.13", + "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.13.tgz", + "integrity": "sha512-bNZfDhe/L8t5OQzIyhrRhBr/61pfBcWaYJoq6UDqFtv5LMwfg4NsVDD2J8N01JqdAdxLjOt66OZEp6PX+dGs/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", @@ -3960,6 +3973,12 @@ "dev": true, "license": "ISC" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -4449,6 +4468,12 @@ "once": "^1.4.0" } }, + "node_modules/enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==", + "license": "MIT" + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -6172,6 +6197,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6239,6 +6272,15 @@ "dev": true, "license": "MIT" }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6356,7 +6398,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, "license": "MIT" }, "node_modules/lodash.merge": { @@ -7265,6 +7306,23 @@ "node": ">=0.10.0" } }, + "node_modules/react-slick": { + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.3.tgz", + "integrity": "sha512-B4x0L9GhkEWUMApeHxr/Ezp2NncpGc+5174R02j+zFiWuYboaq98vmxwlpafZfMjZic1bjdIqqmwLDcQY0QaFA==", + "license": "MIT", + "dependencies": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -7443,6 +7501,12 @@ "node": ">=0.10.0" } }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -7905,6 +7969,16 @@ "node": ">=8" } }, + "node_modules/slick-carousel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", + "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jquery": ">=1.8.0" + } + }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -8000,6 +8074,12 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 72f2f34..7cdee88 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -12,11 +12,13 @@ }, "dependencies": { "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-slick": "^0.30.3" }, "devDependencies": { "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", + "@types/react-slick": "^0.23.13", "@typescript-eslint/eslint-plugin": "^7.8.0", "@typescript-eslint/parser": "^7.8.0", "@vite-pwa/assets-generator": "^0.2.4", @@ -28,6 +30,7 @@ "eslint-plugin-react-refresh": "^0.4.6", "postcss": "^8.4.49", "prettier": "^3.4.2", + "slick-carousel": "^1.8.1", "tailwindcss": "^3.4.17", "typescript": "^5.2.2", "vite": "^5.2.10", diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index 0491dad..eda28f0 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -5,7 +5,6 @@ function App() { return ( <>
-
diff --git a/newlearn/src/components/carousel/carousel.tsx b/newlearn/src/components/carousel/carousel.tsx new file mode 100644 index 0000000..452fda3 --- /dev/null +++ b/newlearn/src/components/carousel/carousel.tsx @@ -0,0 +1,45 @@ +import Slider from 'react-slick'; +import 'slick-carousel/slick/slick.css'; +import 'slick-carousel/slick/slick-theme.css'; +import MainNews from './mainNews'; + +type News = { + thumbnail: string; + title: string; + time: string; + agency: string; +}; + +interface CarouselProps { + items: News[]; // 이미지 URL 또는 콘텐츠 +} + +const Carousel: React.FC = ({ items }) => { + const settings = { + dots: true, + infinite: false, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1, + arrows: false, // 기본 화살표 숨김 + }; + + return ( +
+ + {items.map((item, index) => ( +
+ +
+ ))} +
+
+ ); +}; + +export default Carousel; diff --git a/newlearn/src/components/carousel/mainNews.tsx b/newlearn/src/components/carousel/mainNews.tsx new file mode 100644 index 0000000..57355db --- /dev/null +++ b/newlearn/src/components/carousel/mainNews.tsx @@ -0,0 +1,31 @@ +import React from 'react'; + +interface NewsCardProps { + thumbnail: string; // 뉴스 썸네일 이미지 URL + title: string; // 뉴스 제목 + time: string; + agency: string; +} + +const MainNews: React.FC = ({ + thumbnail, + title, + time, + agency, +}) => { + return ( +
+
+

{title}

+

+ {time}, {agency} +

+
+
+ ); +}; + +export default MainNews; diff --git a/newlearn/src/main.tsx b/newlearn/src/main.tsx index 3d7150d..0fe8995 100644 --- a/newlearn/src/main.tsx +++ b/newlearn/src/main.tsx @@ -1,10 +1,5 @@ -import React from 'react' -import ReactDOM from 'react-dom/client' -import App from './App.tsx' -import './index.css' +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; -ReactDOM.createRoot(document.getElementById('root')!).render( - - - , -) +ReactDOM.createRoot(document.getElementById('root')!).render(); diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 0907138..5fd2561 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,18 +1,39 @@ import Cardnews from '../../components/cardnews'; +import Carousel from '../../components/carousel/carousel'; import CategoryLayout from '../../components/categories/categoryLayout'; const HomePage = () => { + const mockup = [ + { + thumbnail: 'https://url.kr/qsyxm9', + title: '너무 귀여워서 충격.. 실화', + time: '2시간 전', + agency: '매일경제', + }, + { + thumbnail: 'https://url.kr/qsyxm9', + title: '너무 귀여워서 충격.. 실화', + time: '2시간 전', + agency: '매일경제', + }, + { + thumbnail: 'https://url.kr/qsyxm9', + title: '너무 귀여워서 충격.. 실화', + time: '2시간 전', + agency: '매일경제', + }, + ]; return ( - <> -
+
+
12월 29일
-
-
메인기사 3개 케러셀
+
+
카드뉴스 목록 { />
- +
); }; From c6f1e34b32f2ec4fa8c744fc7fec3337bed7f741 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 18:55:31 +0900 Subject: [PATCH 07/27] =?UTF-8?q?style:=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/pages/HomePage.tsx/Header.tsx | 15 +++++++++++++++ newlearn/src/pages/HomePage.tsx/index.tsx | 12 +++--------- 2 files changed, 18 insertions(+), 9 deletions(-) create mode 100644 newlearn/src/pages/HomePage.tsx/Header.tsx diff --git a/newlearn/src/pages/HomePage.tsx/Header.tsx b/newlearn/src/pages/HomePage.tsx/Header.tsx new file mode 100644 index 0000000..05a057a --- /dev/null +++ b/newlearn/src/pages/HomePage.tsx/Header.tsx @@ -0,0 +1,15 @@ +import CategoryLayout from '../../components/categories/categoryLayout'; + +const Header = () => { + return ( +
+
+ 12월 29일 + +
+ +
+ ); +}; + +export default Header; diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 5fd2561..9693797 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,6 +1,7 @@ import Cardnews from '../../components/cardnews'; import Carousel from '../../components/carousel/carousel'; import CategoryLayout from '../../components/categories/categoryLayout'; +import Header from './Header'; const HomePage = () => { const mockup = [ @@ -24,18 +25,11 @@ const HomePage = () => { }, ]; return ( -
-
-
- 12월 29일 - -
- -
+
+
- 카드뉴스 목록 Date: Tue, 31 Dec 2024 19:59:00 +0900 Subject: [PATCH 08/27] =?UTF-8?q?feat:=20=EB=84=A4=EB=B9=84=EA=B2=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=94=20=EB=B0=8F=20header=20=EB=82=A0?= =?UTF-8?q?=EC=A7=9C,=EC=9A=94=EC=9D=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/assets/alarm-on.svg | 0 newlearn/src/assets/alarm.svg | 3 ++ newlearn/src/assets/home.svg | 3 ++ newlearn/src/assets/profile.svg | 3 ++ newlearn/src/assets/scrap.svg | 4 +++ newlearn/src/components/navigationbar.tsx | 15 ++++++++ newlearn/src/pages/HomePage.tsx/Header.tsx | 16 +++++++-- newlearn/src/pages/HomePage.tsx/index.tsx | 3 +- newlearn/src/utils/dateUtil.ts | 40 ++++++++++++++++++++++ newlearn/tailwind.config.js | 21 +++++++----- 10 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 newlearn/src/assets/alarm-on.svg create mode 100644 newlearn/src/assets/alarm.svg create mode 100644 newlearn/src/assets/home.svg create mode 100644 newlearn/src/assets/profile.svg create mode 100644 newlearn/src/assets/scrap.svg create mode 100644 newlearn/src/components/navigationbar.tsx create mode 100644 newlearn/src/utils/dateUtil.ts diff --git a/newlearn/src/assets/alarm-on.svg b/newlearn/src/assets/alarm-on.svg new file mode 100644 index 0000000..e69de29 diff --git a/newlearn/src/assets/alarm.svg b/newlearn/src/assets/alarm.svg new file mode 100644 index 0000000..f65915d --- /dev/null +++ b/newlearn/src/assets/alarm.svg @@ -0,0 +1,3 @@ + + + diff --git a/newlearn/src/assets/home.svg b/newlearn/src/assets/home.svg new file mode 100644 index 0000000..98d8a2e --- /dev/null +++ b/newlearn/src/assets/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/newlearn/src/assets/profile.svg b/newlearn/src/assets/profile.svg new file mode 100644 index 0000000..3982de5 --- /dev/null +++ b/newlearn/src/assets/profile.svg @@ -0,0 +1,3 @@ + + + diff --git a/newlearn/src/assets/scrap.svg b/newlearn/src/assets/scrap.svg new file mode 100644 index 0000000..ed4e57a --- /dev/null +++ b/newlearn/src/assets/scrap.svg @@ -0,0 +1,4 @@ + + + + diff --git a/newlearn/src/components/navigationbar.tsx b/newlearn/src/components/navigationbar.tsx new file mode 100644 index 0000000..ba8a0df --- /dev/null +++ b/newlearn/src/components/navigationbar.tsx @@ -0,0 +1,15 @@ +import Home from '../assets/home.svg?react'; +import Profile from '../assets/profile.svg?react'; +import Scrap from '../assets/scrap.svg?react'; + +const NavigationBar = () => { + return ( +
+ + + +
+ ); +}; + +export default NavigationBar; diff --git a/newlearn/src/pages/HomePage.tsx/Header.tsx b/newlearn/src/pages/HomePage.tsx/Header.tsx index 05a057a..83c7dd2 100644 --- a/newlearn/src/pages/HomePage.tsx/Header.tsx +++ b/newlearn/src/pages/HomePage.tsx/Header.tsx @@ -1,11 +1,21 @@ import CategoryLayout from '../../components/categories/categoryLayout'; +import Alarm from '../../assets/alarm.svg?react'; +// import AlarmOn from '../../assets/alarm-on.svg?react'; +import { getTodayDate } from '../../utils/dateUtil'; const Header = () => { + const { month, day, weekday } = getTodayDate(); + return (
-
- 12월 29일 - +
+
+ {month} {day}일 {} + {weekday} +
+
+ +
diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 9693797..599d127 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,6 +1,6 @@ import Cardnews from '../../components/cardnews'; import Carousel from '../../components/carousel/carousel'; -import CategoryLayout from '../../components/categories/categoryLayout'; +import NavigationBar from '../../components/navigationbar'; import Header from './Header'; const HomePage = () => { @@ -38,6 +38,7 @@ const HomePage = () => { />
+
); }; diff --git a/newlearn/src/utils/dateUtil.ts b/newlearn/src/utils/dateUtil.ts new file mode 100644 index 0000000..61a688d --- /dev/null +++ b/newlearn/src/utils/dateUtil.ts @@ -0,0 +1,40 @@ +export const getTodayDate = (): { + month: string; + day: number; + weekday: string; +} => { + const today = new Date(); + + // 월 이름 배열 (한국어) + const months = [ + '1월', + '2월', + '3월', + '4월', + '5월', + '6월', + '7월', + '8월', + '9월', + '10월', + '11월', + '12월', + ]; + + // 요일 이름 배열 (한국어) + const weekdays = [ + '일요일', + '월요일', + '화요일', + '수요일', + '목요일', + '금요일', + '토요일', + ]; + + return { + month: months[today.getMonth()], // 월 이름 + day: today.getDate(), // 날짜 + weekday: weekdays[today.getDay()], // 요일 이름 + }; +}; diff --git a/newlearn/tailwind.config.js b/newlearn/tailwind.config.js index 4a08ce4..2412096 100644 --- a/newlearn/tailwind.config.js +++ b/newlearn/tailwind.config.js @@ -1,10 +1,15 @@ module.exports = { - content: [ - "./index.html", // Vite의 루트 HTML 파일 - "./src/**/*.{js,ts,jsx,tsx}", // 모든 React 컴포넌트 파일 - ], - theme: { - extend: {}, // 커스텀 테마 추가 시 사용 - }, - plugins: [], // 필요한 Tailwind 플러그인 추가 가능 + content: [ + './index.html', // Vite의 루트 HTML 파일 + './src/**/*.{js,ts,jsx,tsx}', // 모든 React 컴포넌트 파일 + ], + theme: { + extend: { + backgroundImage: { + 'custom-gradient': + 'linear-gradient(111.82deg, #502EE5 0%, #95D9FD 131.35%)', + }, + }, // 커스텀 테마 추가 시 사용 + }, + plugins: [], // 필요한 Tailwind 플러그인 추가 가능 }; From 46c9c68f7f80ad58b204df527985d3288309d822 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 20:06:38 +0900 Subject: [PATCH 09/27] =?UTF-8?q?style:=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/components/cardnews.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/newlearn/src/components/cardnews.tsx b/newlearn/src/components/cardnews.tsx index 13bb733..a64fbdb 100644 --- a/newlearn/src/components/cardnews.tsx +++ b/newlearn/src/components/cardnews.tsx @@ -20,7 +20,9 @@ const Cardnews = ({ thumbnail, title, time, agency }: CardNews) => {
{title}
- {time}, {agency} + + {time}, {agency} +
From 42f70cceba20c3413f7953bcd3d5041fca2436c7 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 21:10:39 +0900 Subject: [PATCH 10/27] =?UTF-8?q?feat:=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20?= =?UTF-8?q?=EB=B0=8F=20=EB=84=A4=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95,=20HomePage=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/package-lock.json | 52 +++++++++++++++++++ newlearn/package.json | 1 + newlearn/src/App.tsx | 15 +++++- newlearn/src/assets/home.svg | 4 +- newlearn/src/assets/profile.svg | 4 +- newlearn/src/assets/scrap.svg | 6 +-- newlearn/src/components/navigationbar.tsx | 37 +++++++++++-- newlearn/src/pages/HomePage.tsx/Header.tsx | 2 +- newlearn/src/pages/HomePage.tsx/index.tsx | 12 ++--- .../src/pages/NewsDetailPage.tsx/index.tsx | 5 ++ newlearn/src/pages/ProfilePage/index.tsx | 5 ++ newlearn/src/pages/ScrapPage/index.tsx | 5 ++ 12 files changed, 128 insertions(+), 20 deletions(-) create mode 100644 newlearn/src/pages/NewsDetailPage.tsx/index.tsx create mode 100644 newlearn/src/pages/ProfilePage/index.tsx create mode 100644 newlearn/src/pages/ScrapPage/index.tsx diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index 0b07107..9e993c7 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router": "^7.1.1", "react-slick": "^0.30.3" }, "devDependencies": { @@ -3022,6 +3023,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4072,6 +4079,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -7306,6 +7322,30 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.1.1.tgz", + "integrity": "sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/react-slick": { "version": "0.30.3", "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.3.tgz", @@ -7720,6 +7760,12 @@ "randombytes": "^2.1.0" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8654,6 +8700,12 @@ "node": "*" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 7cdee88..d9e3161 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -13,6 +13,7 @@ "dependencies": { "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router": "^7.1.1", "react-slick": "^0.30.3" }, "devDependencies": { diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index eda28f0..dedc70d 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -1,11 +1,22 @@ +import { BrowserRouter, Route, Routes } from 'react-router'; import './App.css'; import HomePage from './pages/HomePage.tsx'; +import NewsDetailPage from './pages/NewsDetailPage.tsx/index.tsx'; +import ScrapPage from './pages/ScrapPage/index.tsx'; +import ProfilPage from './pages/ProfilePage/index.tsx'; function App() { return ( <> -
- +
+ + + } /> + } /> + } /> + } /> + +
); diff --git a/newlearn/src/assets/home.svg b/newlearn/src/assets/home.svg index 98d8a2e..bb1dcd2 100644 --- a/newlearn/src/assets/home.svg +++ b/newlearn/src/assets/home.svg @@ -1,3 +1,3 @@ - - + + diff --git a/newlearn/src/assets/profile.svg b/newlearn/src/assets/profile.svg index 3982de5..49972a0 100644 --- a/newlearn/src/assets/profile.svg +++ b/newlearn/src/assets/profile.svg @@ -1,3 +1,3 @@ - - + + diff --git a/newlearn/src/assets/scrap.svg b/newlearn/src/assets/scrap.svg index ed4e57a..dc87dbf 100644 --- a/newlearn/src/assets/scrap.svg +++ b/newlearn/src/assets/scrap.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/newlearn/src/components/navigationbar.tsx b/newlearn/src/components/navigationbar.tsx index ba8a0df..38021cc 100644 --- a/newlearn/src/components/navigationbar.tsx +++ b/newlearn/src/components/navigationbar.tsx @@ -1,13 +1,42 @@ +import { Link, useLocation } from 'react-router'; import Home from '../assets/home.svg?react'; import Profile from '../assets/profile.svg?react'; import Scrap from '../assets/scrap.svg?react'; +import { useState, useEffect } from 'react'; const NavigationBar = () => { + const location = useLocation(); + const [crnt, setCrnt] = useState(location.pathname); + + // location.pathname이 변경될 때 crnt 업데이트 + useEffect(() => { + setCrnt(location.pathname); + }, [location.pathname]); + + const navItems = [ + { path: '/scrap', component: Scrap, label: '스크랩' }, + { path: '/', component: Home, label: '홈' }, + { path: '/profile', component: Profile, label: '프로필' }, + ]; + return ( -
- - - +
+ {navItems.map(({ path, component: Icon, label }) => ( + + + + {label} + + + ))}
); }; diff --git a/newlearn/src/pages/HomePage.tsx/Header.tsx b/newlearn/src/pages/HomePage.tsx/Header.tsx index 83c7dd2..823abd7 100644 --- a/newlearn/src/pages/HomePage.tsx/Header.tsx +++ b/newlearn/src/pages/HomePage.tsx/Header.tsx @@ -7,7 +7,7 @@ const Header = () => { const { month, day, weekday } = getTodayDate(); return ( -
+
{month} {day}일 {} diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 599d127..a784496 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -26,18 +26,18 @@ const HomePage = () => { ]; return (
-
-
- -
+
+
+
+ -
-
+ +
); diff --git a/newlearn/src/pages/NewsDetailPage.tsx/index.tsx b/newlearn/src/pages/NewsDetailPage.tsx/index.tsx new file mode 100644 index 0000000..05a4a14 --- /dev/null +++ b/newlearn/src/pages/NewsDetailPage.tsx/index.tsx @@ -0,0 +1,5 @@ +const NewsDetailPage = () => { + return
news detail page
; +}; + +export default NewsDetailPage; diff --git a/newlearn/src/pages/ProfilePage/index.tsx b/newlearn/src/pages/ProfilePage/index.tsx new file mode 100644 index 0000000..1a19d9c --- /dev/null +++ b/newlearn/src/pages/ProfilePage/index.tsx @@ -0,0 +1,5 @@ +const ProfilPage = () => { + return
news detail page
; +}; + +export default ProfilPage; diff --git a/newlearn/src/pages/ScrapPage/index.tsx b/newlearn/src/pages/ScrapPage/index.tsx new file mode 100644 index 0000000..683ebfa --- /dev/null +++ b/newlearn/src/pages/ScrapPage/index.tsx @@ -0,0 +1,5 @@ +const ScrapPage = () => { + return
news detail page
; +}; + +export default ScrapPage; From d9494ebd5e7fbf303666bcc97f3bca8e6962c7da Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Tue, 31 Dec 2024 23:42:16 +0900 Subject: [PATCH 11/27] =?UTF-8?q?publish:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/assets/alarm-on.svg | 4 ++++ newlearn/src/pages/ProfilePage/index.tsx | 28 +++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/newlearn/src/assets/alarm-on.svg b/newlearn/src/assets/alarm-on.svg index e69de29..2e0938e 100644 --- a/newlearn/src/assets/alarm-on.svg +++ b/newlearn/src/assets/alarm-on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/newlearn/src/pages/ProfilePage/index.tsx b/newlearn/src/pages/ProfilePage/index.tsx index 1a19d9c..e747404 100644 --- a/newlearn/src/pages/ProfilePage/index.tsx +++ b/newlearn/src/pages/ProfilePage/index.tsx @@ -1,5 +1,31 @@ +import Cardnews from '../../components/cardnews'; +import CategoryLayout from '../../components/categories/categoryLayout'; + const ProfilPage = () => { - return
news detail page
; + return ( +
+
+ 육종호님은 지금까지
+ 총 10개의 기사를 읽었어요! +
+
+
그래프
+
순위
+
+
+ +
+
+
+ 최근에 읽은 뉴스 * 최신 20개만 저장됩니다. +
+ + + + +
+
+ ); }; export default ProfilPage; From a183c074df5550e1f16c6bf33bfcc9c37b37d859 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Wed, 1 Jan 2025 14:48:09 +0900 Subject: [PATCH 12/27] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/categories/categoryItem.tsx | 18 ++-- .../components/categories/categoryLayout.tsx | 30 +++++- newlearn/src/pages/HomePage.tsx/Header.tsx | 2 +- newlearn/src/pages/ProfilePage/Category.tsx | 12 +++ newlearn/src/pages/ProfilePage/DonutChart.tsx | 96 +++++++++++++++++++ newlearn/src/pages/ProfilePage/Header.tsx | 10 ++ newlearn/src/pages/ProfilePage/index.tsx | 23 ++--- 7 files changed, 167 insertions(+), 24 deletions(-) create mode 100644 newlearn/src/pages/ProfilePage/Category.tsx create mode 100644 newlearn/src/pages/ProfilePage/DonutChart.tsx create mode 100644 newlearn/src/pages/ProfilePage/Header.tsx diff --git a/newlearn/src/components/categories/categoryItem.tsx b/newlearn/src/components/categories/categoryItem.tsx index 9dc0e6f..a3e523c 100644 --- a/newlearn/src/components/categories/categoryItem.tsx +++ b/newlearn/src/components/categories/categoryItem.tsx @@ -1,20 +1,20 @@ -import React, { useState } from 'react'; +import React from 'react'; type CategoryItemProps = { name: string; // 카테고리 이름 id: number; // 카테고리 ID + isSelected: boolean; // 선택 여부 + onClick: () => void; // 클릭 이벤트 핸들러 }; -const CategoryItem: React.FC = ({ name }) => { - const [isSelected, setIsSelected] = useState(false); - - const handleClick = () => { - setIsSelected((prev) => !prev); // 선택 상태 토글 - }; - +const CategoryItem: React.FC = ({ + name, + isSelected, + onClick, +}) => { return (
= ({ layout }) => { + const [selectedCategories, setSelectedCategories] = useState( + layout === 'profile' ? [] : [-1], // 기본값: 프로필은 다중 선택, 홈은 단일 선택 (-1은 선택되지 않은 상태) + ); + + const handleCategoryClick = (id: number) => { + if (layout === 'profile') { + // 다중 선택 로직 + setSelectedCategories((prev) => + prev.includes(id) + ? prev.filter((categoryId) => categoryId !== id) + : [...prev, id], + ); + } else { + // 단일 선택 로직 + setSelectedCategories([id]); + } + }; + return (
{Object.entries(CATEGORY_NAMES).map(([id, name]) => ( - + handleCategoryClick(Number(id))} + /> ))}
); diff --git a/newlearn/src/pages/HomePage.tsx/Header.tsx b/newlearn/src/pages/HomePage.tsx/Header.tsx index 823abd7..db825c9 100644 --- a/newlearn/src/pages/HomePage.tsx/Header.tsx +++ b/newlearn/src/pages/HomePage.tsx/Header.tsx @@ -17,7 +17,7 @@ const Header = () => {
- +
); }; diff --git a/newlearn/src/pages/ProfilePage/Category.tsx b/newlearn/src/pages/ProfilePage/Category.tsx new file mode 100644 index 0000000..f4c04e0 --- /dev/null +++ b/newlearn/src/pages/ProfilePage/Category.tsx @@ -0,0 +1,12 @@ +import CategoryLayout from '../../components/categories/categoryLayout'; + +const Category = () => { + return ( + <> + 나의 카테고리 + + + ); +}; + +export default Category; diff --git a/newlearn/src/pages/ProfilePage/DonutChart.tsx b/newlearn/src/pages/ProfilePage/DonutChart.tsx new file mode 100644 index 0000000..36f65f8 --- /dev/null +++ b/newlearn/src/pages/ProfilePage/DonutChart.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { CATEGORY_NAMES } from '../../constants/category'; + +type ChartData = { + categoryId: number; + percentage: number; +}; + +type ChartProps = { + data: ChartData[]; + colors?: string[]; + size?: number; // 차트 크기 (DonutChart 전용) +}; + +const DEFAULT_COLORS = ['#f9c74f', '#90be6d', '#f94144', '#577590']; + +const DonutChart: React.FC = ({ + data, + size = 100, + colors = DEFAULT_COLORS, +}) => { + let cumulativePercentage = 0; + + const getCoordinatesForPercentage = (percentage: number) => { + const x = Math.cos(2 * Math.PI * percentage); + const y = Math.sin(2 * Math.PI * percentage); + return [x, y]; + }; + + return ( + + {data.map((item, index) => { + const [startX, startY] = + getCoordinatesForPercentage(cumulativePercentage); + cumulativePercentage += item.percentage; + const [endX, endY] = getCoordinatesForPercentage(cumulativePercentage); + + const largeArcFlag = item.percentage > 0.5 ? 1 : 0; + + const pathData = ` + M ${startX} ${startY} + A 1 1 0 ${largeArcFlag} 1 ${endX} ${endY} + L 0 0 + `; + + return ( + + ); + })} + {/* 도넛 구멍 */} + + + ); +}; + +const Ranking: React.FC = ({ data, colors = DEFAULT_COLORS }) => { + return ( +
+ {data.map((category, index) => ( +
+ + {index + 1} + + + {CATEGORY_NAMES[category.categoryId]}: {category.percentage * 100}% + +
+ ))} +
+ ); +}; + +const Chart: React.FC<{ data: ChartData[] }> = ({ data }) => { + return ( +
+ + +
+ ); +}; + +export default Chart; diff --git a/newlearn/src/pages/ProfilePage/Header.tsx b/newlearn/src/pages/ProfilePage/Header.tsx new file mode 100644 index 0000000..f25ac44 --- /dev/null +++ b/newlearn/src/pages/ProfilePage/Header.tsx @@ -0,0 +1,10 @@ +const Header = () => { + return ( +
+ 육종호님은 지금까지
+ 총 10개의 기사를 읽었어요! +
+ ); +}; + +export default Header; diff --git a/newlearn/src/pages/ProfilePage/index.tsx b/newlearn/src/pages/ProfilePage/index.tsx index e747404..9fb1038 100644 --- a/newlearn/src/pages/ProfilePage/index.tsx +++ b/newlearn/src/pages/ProfilePage/index.tsx @@ -1,20 +1,21 @@ import Cardnews from '../../components/cardnews'; import CategoryLayout from '../../components/categories/categoryLayout'; +import Category from './Category'; +import Chart from './DonutChart'; +import Header from './Header'; const ProfilPage = () => { + const chartData = [ + { categoryId: 1, percentage: 0.6 }, + { categoryId: 2, percentage: 0.25 }, + { categoryId: 3, percentage: 0.15 }, + ]; + return (
-
- 육종호님은 지금까지
- 총 10개의 기사를 읽었어요! -
-
-
그래프
-
순위
-
-
- -
+
+ +
최근에 읽은 뉴스 * 최신 20개만 저장됩니다. From 059e9ac4fbd44a357228679579508f45efbf911e Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:30:47 +0900 Subject: [PATCH 13/27] =?UTF-8?q?"style:=20=ED=99=94=EC=82=B4=ED=91=9C=20s?= =?UTF-8?q?vg=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/assets/arrow-right.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newlearn/src/assets/arrow-right.svg b/newlearn/src/assets/arrow-right.svg index c9e49b1..361081a 100644 --- a/newlearn/src/assets/arrow-right.svg +++ b/newlearn/src/assets/arrow-right.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 68a24f77396de156f5ea7814d42e3a83469ee414 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:31:28 +0900 Subject: [PATCH 14/27] =?UTF-8?q?"feat:=20news=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/types/news.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 newlearn/src/types/news.ts diff --git a/newlearn/src/types/news.ts b/newlearn/src/types/news.ts new file mode 100644 index 0000000..958add9 --- /dev/null +++ b/newlearn/src/types/news.ts @@ -0,0 +1,11 @@ +export type news = { + newsId: number; + thumbnail: string; + title: string; + publishedDate: string; + source: string; +}; + +export type NewsData = { + newsList: news[]; +}; From 1da9a7b38fe0ef82d041ccf980d234ac023cc270 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:31:44 +0900 Subject: [PATCH 15/27] =?UTF-8?q?"chore:=20=EB=AA=A9=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/mockup/latestNewsList.json | 27 +++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 newlearn/src/mockup/latestNewsList.json diff --git a/newlearn/src/mockup/latestNewsList.json b/newlearn/src/mockup/latestNewsList.json new file mode 100644 index 0000000..4ae51a7 --- /dev/null +++ b/newlearn/src/mockup/latestNewsList.json @@ -0,0 +1,27 @@ +{ + "data": { + "latestNews": [ + { + "newsId": 101, + "title": "Latest AI breakthrough", + "source": "TechTimes", + "publishedDate": "2024-12-31T23:00:00Z", + "thumbnail": "https://url.kr/qsyxm9" + }, + { + "newsId": 102, + "title": "Tech company announces new gadget", + "source": "GadgetWorld", + "publishedDate": "2024-12-31T23:30:00Z", + "thumbnail": "https://url.kr/qsyxm9" + }, + { + "newsId": 103, + "title": "Breakthrough in renewable energy", + "source": "EnergyDaily", + "publishedDate": "2024-12-31T23:15:00Z", + "thumbnail": "https://url.kr/qsyxm9" + } + ] + } +} From 48efde877f371241dcec930b501f353c9548bcfa Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:32:03 +0900 Subject: [PATCH 16/27] =?UTF-8?q?"feat:=20=EC=8B=9C=EA=B0=84=20=EA=B3=84?= =?UTF-8?q?=EC=82=B0=20=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/utils/timeCalc.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 newlearn/src/utils/timeCalc.ts diff --git a/newlearn/src/utils/timeCalc.ts b/newlearn/src/utils/timeCalc.ts new file mode 100644 index 0000000..60f6b52 --- /dev/null +++ b/newlearn/src/utils/timeCalc.ts @@ -0,0 +1,15 @@ +export const hoursSince = (isoDate: string): number => { + // 지정된 시간 + const startDate = new Date(isoDate); + + // 현재 시간 + const now = new Date(); + + // 두 시간의 차이 (밀리초) + const diffMilliseconds = now.getTime() - startDate.getTime(); + + // 시간으로 변환 + const diffHours = diffMilliseconds / (1000 * 60 * 60); + + return Math.floor(diffHours); // 소수점 버림 +}; From b2dbd6f6a426407df5e97417b73575fa68532515 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:32:28 +0900 Subject: [PATCH 17/27] =?UTF-8?q?"feat:=20=EA=B8=B0=EB=B3=B8=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/layout/basicLayout.tsx | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 newlearn/src/layout/basicLayout.tsx diff --git a/newlearn/src/layout/basicLayout.tsx b/newlearn/src/layout/basicLayout.tsx new file mode 100644 index 0000000..d4f2570 --- /dev/null +++ b/newlearn/src/layout/basicLayout.tsx @@ -0,0 +1,21 @@ +import { ReactNode } from 'react'; +import NavigationBar from '../components/navigationbar'; + +type LayoutProps = { + headerComponent: ReactNode; // Header 컴포넌트 + children: ReactNode; // 여러 개의 Main 컴포넌트를 포함할 수 있음 +}; + +const BasicLayout: React.FC = ({ headerComponent, children }) => { + return ( +
+
+ {headerComponent} +
{children}
+
+ +
+ ); +}; + +export default BasicLayout; From f92eca61ae02b9220b3ac5493f837eb08ca9c666 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:33:41 +0900 Subject: [PATCH 18/27] =?UTF-8?q?"refactor:=20=EB=B3=80=EC=88=98=EB=AA=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/components/cardnews.tsx | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/newlearn/src/components/cardnews.tsx b/newlearn/src/components/cardnews.tsx index a64fbdb..a8f845b 100644 --- a/newlearn/src/components/cardnews.tsx +++ b/newlearn/src/components/cardnews.tsx @@ -1,27 +1,29 @@ import Arrow from '../assets/arrow-right.svg?react'; import Image from '../assets/image.svg?react'; +import { hoursSince } from '../utils/timeCalc'; type CardNews = { thumbnail: string; title: string; time: string; - agency: string; + source: string; }; -const Cardnews = ({ thumbnail, title, time, agency }: CardNews) => { - const commonStyles = 'w-40 h-30 bg-[#EAF2FF]'; + +const Cardnews = ({ thumbnail, title, time, source }: CardNews) => { + const commonStyles = 'w-32 bg-[#EAF2FF] rounded-lg'; return ( -
+
{thumbnail ? ( ) : ( - + )} -
-
- {title} +
+
+ {title}
- {time}, {agency} + {hoursSince(time)}시간 전, {source}
From 7f90d7098c4f7586b8b97fff44ebe0bbbe4265dd Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:34:18 +0900 Subject: [PATCH 19/27] =?UTF-8?q?"refactor:=20=ED=9B=85=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/components/navigationbar.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/newlearn/src/components/navigationbar.tsx b/newlearn/src/components/navigationbar.tsx index 38021cc..4ae28b1 100644 --- a/newlearn/src/components/navigationbar.tsx +++ b/newlearn/src/components/navigationbar.tsx @@ -2,16 +2,9 @@ import { Link, useLocation } from 'react-router'; import Home from '../assets/home.svg?react'; import Profile from '../assets/profile.svg?react'; import Scrap from '../assets/scrap.svg?react'; -import { useState, useEffect } from 'react'; const NavigationBar = () => { const location = useLocation(); - const [crnt, setCrnt] = useState(location.pathname); - - // location.pathname이 변경될 때 crnt 업데이트 - useEffect(() => { - setCrnt(location.pathname); - }, [location.pathname]); const navItems = [ { path: '/scrap', component: Scrap, label: '스크랩' }, @@ -25,12 +18,12 @@ const NavigationBar = () => { {label} From d3b2e1368da48ebcc7d6740aab3427e524bed5d4 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:35:24 +0900 Subject: [PATCH 20/27] =?UTF-8?q?"refactor:=20type=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/components/carousel/carousel.tsx | 4 +-- newlearn/src/components/carousel/mainNews.tsx | 6 ++-- newlearn/src/pages/HomePage.tsx/index.tsx | 32 ++++++++----------- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/newlearn/src/components/carousel/carousel.tsx b/newlearn/src/components/carousel/carousel.tsx index 452fda3..0609d38 100644 --- a/newlearn/src/components/carousel/carousel.tsx +++ b/newlearn/src/components/carousel/carousel.tsx @@ -7,7 +7,7 @@ type News = { thumbnail: string; title: string; time: string; - agency: string; + source: string; }; interface CarouselProps { @@ -33,7 +33,7 @@ const Carousel: React.FC = ({ items }) => { thumbnail={item.thumbnail} title={item.title} time={item.time} - agency={item.agency} + source={item.source} />
))} diff --git a/newlearn/src/components/carousel/mainNews.tsx b/newlearn/src/components/carousel/mainNews.tsx index 57355db..df96cdd 100644 --- a/newlearn/src/components/carousel/mainNews.tsx +++ b/newlearn/src/components/carousel/mainNews.tsx @@ -4,14 +4,14 @@ interface NewsCardProps { thumbnail: string; // 뉴스 썸네일 이미지 URL title: string; // 뉴스 제목 time: string; - agency: string; + source: string; } const MainNews: React.FC = ({ thumbnail, title, time, - agency, + source, }) => { return (
= ({

{title}

- {time}, {agency} + {time}, {source}

diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index a784496..81cc5d4 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,6 +1,6 @@ import Cardnews from '../../components/cardnews'; import Carousel from '../../components/carousel/carousel'; -import NavigationBar from '../../components/navigationbar'; +import BasicLayout from '../../layout/basicLayout'; import Header from './Header'; const HomePage = () => { @@ -9,37 +9,31 @@ const HomePage = () => { thumbnail: 'https://url.kr/qsyxm9', title: '너무 귀여워서 충격.. 실화', time: '2시간 전', - agency: '매일경제', + source: '매일경제', }, { thumbnail: 'https://url.kr/qsyxm9', title: '너무 귀여워서 충격.. 실화', time: '2시간 전', - agency: '매일경제', + source: '매일경제', }, { thumbnail: 'https://url.kr/qsyxm9', title: '너무 귀여워서 충격.. 실화', time: '2시간 전', - agency: '매일경제', + source: '매일경제', }, ]; return ( -
-
-
-
- - -
-
- -
+ }> + + + ); }; From 26a1f14df0107129d903f71cf02e424a292c3cff Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:35:45 +0900 Subject: [PATCH 21/27] =?UTF-8?q?"feat:=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/pages/ProfilePage/CardNews.tsx | 26 +++++++++++++++++++ newlearn/src/pages/ProfilePage/Category.tsx | 8 +++--- newlearn/src/pages/ProfilePage/DonutChart.tsx | 2 +- newlearn/src/pages/ProfilePage/Header.tsx | 4 +-- newlearn/src/pages/ProfilePage/index.tsx | 20 +++++--------- 5 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 newlearn/src/pages/ProfilePage/CardNews.tsx diff --git a/newlearn/src/pages/ProfilePage/CardNews.tsx b/newlearn/src/pages/ProfilePage/CardNews.tsx new file mode 100644 index 0000000..13e20f8 --- /dev/null +++ b/newlearn/src/pages/ProfilePage/CardNews.tsx @@ -0,0 +1,26 @@ +import Cardnews from '../../components/cardnews'; +import { NewsData } from '../../types/news'; + +const CardNews: React.FC = ({ newsList }) => { + return ( +
+ + 최근에 읽은 뉴스{' '} + * 최신 20개만 저장됩니다. + +
+ {newsList.map((news) => ( + + ))} +
+
+ ); +}; + +export default CardNews; diff --git a/newlearn/src/pages/ProfilePage/Category.tsx b/newlearn/src/pages/ProfilePage/Category.tsx index f4c04e0..3a3ebde 100644 --- a/newlearn/src/pages/ProfilePage/Category.tsx +++ b/newlearn/src/pages/ProfilePage/Category.tsx @@ -2,10 +2,12 @@ import CategoryLayout from '../../components/categories/categoryLayout'; const Category = () => { return ( - <> - 나의 카테고리 +
+ + 나의 카테고리 + - +
); }; diff --git a/newlearn/src/pages/ProfilePage/DonutChart.tsx b/newlearn/src/pages/ProfilePage/DonutChart.tsx index 36f65f8..b3b535c 100644 --- a/newlearn/src/pages/ProfilePage/DonutChart.tsx +++ b/newlearn/src/pages/ProfilePage/DonutChart.tsx @@ -87,7 +87,7 @@ const Ranking: React.FC = ({ data, colors = DEFAULT_COLORS }) => { const Chart: React.FC<{ data: ChartData[] }> = ({ data }) => { return (
- +
); diff --git a/newlearn/src/pages/ProfilePage/Header.tsx b/newlearn/src/pages/ProfilePage/Header.tsx index f25ac44..d4f5674 100644 --- a/newlearn/src/pages/ProfilePage/Header.tsx +++ b/newlearn/src/pages/ProfilePage/Header.tsx @@ -1,8 +1,8 @@ const Header = () => { return ( -
+
육종호님은 지금까지
- 총 10개의 기사를 읽었어요! + 총 10개의 기사를 읽었어요!
); }; diff --git a/newlearn/src/pages/ProfilePage/index.tsx b/newlearn/src/pages/ProfilePage/index.tsx index 9fb1038..47bf4a1 100644 --- a/newlearn/src/pages/ProfilePage/index.tsx +++ b/newlearn/src/pages/ProfilePage/index.tsx @@ -1,8 +1,9 @@ -import Cardnews from '../../components/cardnews'; -import CategoryLayout from '../../components/categories/categoryLayout'; +import BasicLayout from '../../layout/basicLayout'; +import CardNews from './CardNews'; import Category from './Category'; import Chart from './DonutChart'; import Header from './Header'; +import data from '../../mockup/latestNewsList.json'; const ProfilPage = () => { const chartData = [ @@ -12,20 +13,11 @@ const ProfilPage = () => { ]; return ( -
-
+ }> -
-
- 최근에 읽은 뉴스 * 최신 20개만 저장됩니다. -
- - - - -
-
+ + ); }; From 03183867a772ec40abee921d134d36cb8e5bf40c Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Thu, 2 Jan 2025 01:54:27 +0900 Subject: [PATCH 22/27] =?UTF-8?q?feat:=20=EC=8A=A4=ED=81=AC=EB=9E=A9?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=8D=BC=EB=B8=94=EB=A6=AC?= =?UTF-8?q?=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/pages/ScrapPage/Header.tsx | 11 +++++++++++ newlearn/src/pages/ScrapPage/ScrapNews.tsx | 20 ++++++++++++++++++++ newlearn/src/pages/ScrapPage/index.tsx | 11 ++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 newlearn/src/pages/ScrapPage/Header.tsx create mode 100644 newlearn/src/pages/ScrapPage/ScrapNews.tsx diff --git a/newlearn/src/pages/ScrapPage/Header.tsx b/newlearn/src/pages/ScrapPage/Header.tsx new file mode 100644 index 0000000..bc4aa32 --- /dev/null +++ b/newlearn/src/pages/ScrapPage/Header.tsx @@ -0,0 +1,11 @@ +const Header = () => { + return ( +
+ + 스크랩 목록 + +
+ ); +}; + +export default Header; diff --git a/newlearn/src/pages/ScrapPage/ScrapNews.tsx b/newlearn/src/pages/ScrapPage/ScrapNews.tsx new file mode 100644 index 0000000..8ba668b --- /dev/null +++ b/newlearn/src/pages/ScrapPage/ScrapNews.tsx @@ -0,0 +1,20 @@ +import Cardnews from '../../components/cardnews'; +import { NewsData } from '../../types/news'; + +const ScrapNews: React.FC = ({ newsList }) => { + return ( +
+ {newsList.map((news) => ( + + ))} +
+ ); +}; + +export default ScrapNews; diff --git a/newlearn/src/pages/ScrapPage/index.tsx b/newlearn/src/pages/ScrapPage/index.tsx index 683ebfa..03b9324 100644 --- a/newlearn/src/pages/ScrapPage/index.tsx +++ b/newlearn/src/pages/ScrapPage/index.tsx @@ -1,5 +1,14 @@ +import BasicLayout from '../../layout/basicLayout'; +import Header from './Header'; +import ScrapNews from './ScrapNews'; +import data from '../../mockup/latestNewsList.json'; + const ScrapPage = () => { - return
news detail page
; + return ( + }> + + + ); }; export default ScrapPage; From e8cb99c6d334f46d47061e28dd3e8bcf37b06046 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 4 Jan 2025 03:21:33 +0900 Subject: [PATCH 23/27] =?UTF-8?q?chore:=20svg=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/assets/bookmark-line.svg | 1 + newlearn/src/assets/links-line.svg | 1 + 2 files changed, 2 insertions(+) create mode 100644 newlearn/src/assets/bookmark-line.svg create mode 100644 newlearn/src/assets/links-line.svg diff --git a/newlearn/src/assets/bookmark-line.svg b/newlearn/src/assets/bookmark-line.svg new file mode 100644 index 0000000..0bca459 --- /dev/null +++ b/newlearn/src/assets/bookmark-line.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/newlearn/src/assets/links-line.svg b/newlearn/src/assets/links-line.svg new file mode 100644 index 0000000..b4fd960 --- /dev/null +++ b/newlearn/src/assets/links-line.svg @@ -0,0 +1 @@ + \ No newline at end of file From be97af0ac6f9e951c65cb86053c40a5e7ade69e1 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 4 Jan 2025 03:22:01 +0900 Subject: [PATCH 24/27] =?UTF-8?q?feat:=20detail=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EB=9D=BC=EC=9A=B0=ED=8C=85=20=EC=85=8B?= =?UTF-8?q?=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/src/App.tsx | 2 +- newlearn/src/components/cardnews.tsx | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/newlearn/src/App.tsx b/newlearn/src/App.tsx index dedc70d..1f82da7 100644 --- a/newlearn/src/App.tsx +++ b/newlearn/src/App.tsx @@ -12,7 +12,7 @@ function App() { } /> - } /> + } /> } /> } /> diff --git a/newlearn/src/components/cardnews.tsx b/newlearn/src/components/cardnews.tsx index a8f845b..d40020b 100644 --- a/newlearn/src/components/cardnews.tsx +++ b/newlearn/src/components/cardnews.tsx @@ -1,18 +1,28 @@ +import { useNavigate } from 'react-router'; import Arrow from '../assets/arrow-right.svg?react'; import Image from '../assets/image.svg?react'; +import { news } from '../types/news'; import { hoursSince } from '../utils/timeCalc'; -type CardNews = { - thumbnail: string; - title: string; - time: string; - source: string; -}; +const Cardnews = ({ + thumbnail, + title, + publishedDate, + source, + newsId, +}: news) => { + const navigate = useNavigate(); -const Cardnews = ({ thumbnail, title, time, source }: CardNews) => { + // 특정 라우터로 이동하는 함수 + const handleNavigation = (newsId: number) => { + navigate(`/details/${newsId}`); + }; const commonStyles = 'w-32 bg-[#EAF2FF] rounded-lg'; return ( -
+
handleNavigation(newsId)} + > {thumbnail ? ( ) : ( @@ -23,7 +33,7 @@ const Cardnews = ({ thumbnail, title, time, source }: CardNews) => { {title}
- {hoursSince(time)}시간 전, {source} + {hoursSince(publishedDate)}시간 전, {source}
From ec845a3c0e30036abbbd59ee0f9ba6d862ab69e5 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 4 Jan 2025 03:22:27 +0900 Subject: [PATCH 25/27] =?UTF-8?q?feat:=20=EB=89=B4=EC=8A=A4=20=EA=B8=B0?= =?UTF-8?q?=EC=82=AC=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20=ED=8D=BC?= =?UTF-8?q?=EB=B8=94=EB=A6=AC=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/NewsDetailPage.tsx/Header.tsx | 30 ++++++++++++++ .../src/pages/NewsDetailPage.tsx/NewsInfo.tsx | 41 +++++++++++++++++++ .../src/pages/NewsDetailPage.tsx/index.tsx | 34 ++++++++++++++- newlearn/src/pages/ProfilePage/CardNews.tsx | 3 +- 4 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 newlearn/src/pages/NewsDetailPage.tsx/Header.tsx create mode 100644 newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx diff --git a/newlearn/src/pages/NewsDetailPage.tsx/Header.tsx b/newlearn/src/pages/NewsDetailPage.tsx/Header.tsx new file mode 100644 index 0000000..3be14ca --- /dev/null +++ b/newlearn/src/pages/NewsDetailPage.tsx/Header.tsx @@ -0,0 +1,30 @@ +import { useNavigate } from 'react-router'; +import Arrow from '../../assets/arrow-right.svg?react'; + +type HeaderProps = { + thumbnail: string; +}; + +const Header: React.FC = ({ thumbnail }) => { + const navigate = useNavigate(); + + // 이전 페이지로 돌아가는 함수 + const goBack = () => { + navigate(-1); // 히스토리 스택에서 한 단계 뒤로 이동 + }; + return ( +
+
+ +
+
+ ); +}; + +export default Header; diff --git a/newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx b/newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx new file mode 100644 index 0000000..ab806d9 --- /dev/null +++ b/newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx @@ -0,0 +1,41 @@ +import { hoursSince } from '../../utils/timeCalc'; +import Bookmark from '../../assets/bookmark-line.svg?react'; +import Link from '../../assets/links-line.svg?react'; + +type NewsInfoProps = { + title: string; + publishedDate: string; + source: string; + content: string; +}; + +const NewsInfo: React.FC = ({ + title, + publishedDate, + source, + content, +}) => { + return ( +
+
+ {title} +
+ + {hoursSince(publishedDate)}시간 전, {source} + +
+
+
+ + + + +
+
+
+
{content}
+
+ ); +}; + +export default NewsInfo; diff --git a/newlearn/src/pages/NewsDetailPage.tsx/index.tsx b/newlearn/src/pages/NewsDetailPage.tsx/index.tsx index 05a4a14..ae4deb7 100644 --- a/newlearn/src/pages/NewsDetailPage.tsx/index.tsx +++ b/newlearn/src/pages/NewsDetailPage.tsx/index.tsx @@ -1,5 +1,37 @@ +import { useParams } from 'react-router'; +import { useNavigate } from 'react-router'; +import Header from './Header'; +import NewsInfo from './NewsInfo'; + const NewsDetailPage = () => { - return
news detail page
; + const { newsId } = useParams<{ newsId: string }>(); + const navigate = useNavigate(); + + // 이전 페이지로 돌아가는 함수 + const goBack = () => { + navigate(-1); // 히스토리 스택에서 한 단계 뒤로 이동 + }; + + const mockup = { + newsId: 101, + title: 'Latest AI breakthrough', + source: 'TechTimes', + publishedDate: '2024-12-31T23:00:00Z', + thumbnail: 'https://url.kr/qsyxm9', + content: + 'The full article content goes here. This section contains all the detailed information about the news, including insights, statistics, and expert opinions.Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate at earum consequuntur officia repellat doloremque numquam similique et adipisci ex, non eaque, itaque inventore! Fugiat adipisci ut dignissimos cupiditate. Laborum!', + }; + return ( + <> +
+ + + ); }; export default NewsDetailPage; diff --git a/newlearn/src/pages/ProfilePage/CardNews.tsx b/newlearn/src/pages/ProfilePage/CardNews.tsx index 13e20f8..5f43108 100644 --- a/newlearn/src/pages/ProfilePage/CardNews.tsx +++ b/newlearn/src/pages/ProfilePage/CardNews.tsx @@ -14,8 +14,9 @@ const CardNews: React.FC = ({ newsList }) => { key={news.newsId} thumbnail={news.thumbnail} title={news.title} - time={news.publishedDate} + publishedDate={news.publishedDate} source={news.source} + newsId={news.newsId} /> ))}
From 7cd4bebb1feb032ee37403d24089b97ed6a60a39 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sat, 11 Jan 2025 00:26:58 +0900 Subject: [PATCH 26/27] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20api=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/.gitignore | 1 + newlearn/package-lock.json | 558 ++++++++++++++++++ newlearn/package.json | 1 + newlearn/routes.json | 3 + newlearn/src/components/carousel/carousel.tsx | 12 +- newlearn/src/hooks/api.ts | 50 ++ newlearn/src/pages/HomePage.tsx/index.tsx | 68 ++- newlearn/src/types/news.ts | 16 + ....timestamp-1736522539273-3f647e2259e4d.mjs | 56 ++ 9 files changed, 729 insertions(+), 36 deletions(-) create mode 100644 newlearn/routes.json create mode 100644 newlearn/src/hooks/api.ts create mode 100644 newlearn/vite.config.ts.timestamp-1736522539273-3f647e2259e4d.mjs diff --git a/newlearn/.gitignore b/newlearn/.gitignore index 6d6ae5a..076f805 100644 --- a/newlearn/.gitignore +++ b/newlearn/.gitignore @@ -23,3 +23,4 @@ dev-dist *.njsproj *.sln *.sw? +db.json diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index 9e993c7..3d86e4a 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -26,6 +26,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", + "json-server": "^1.0.0-beta.3", "postcss": "^8.4.49", "prettier": "^3.4.2", "slick-carousel": "^1.8.1", @@ -2390,6 +2391,13 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", @@ -2978,6 +2986,263 @@ "@svgr/core": "*" } }, + "node_modules/@tinyhttp/accepts": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/accepts/-/accepts-2.2.3.tgz", + "integrity": "sha512-9pQN6pJAJOU3McmdJWTcyq7LLFW8Lj5q+DadyKcvp+sxMkEpktKX5sbfJgJuOvjk6+1xWl7pe0YL1US1vaO/1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "4.0.4", + "negotiator": "^0.6.3" + }, + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/app": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/app/-/app-2.5.0.tgz", + "integrity": "sha512-mwVY6RhTqF/s49tqNqpkRWbXAC7OmfpxR6YI6atp9xDauUOv3n1hv38AKy89Ga8HC8G2p1KR9vPD66VmF6VMnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/proxy-addr": "2.2.0", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/res": "2.2.4", + "@tinyhttp/router": "2.2.3", + "header-range-parser": "1.1.3", + "regexparam": "^2.0.2" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-disposition": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.2.tgz", + "integrity": "sha512-crXw1txzrS36huQOyQGYFvhTeLeG0Si1xu+/l6kXUVYpE0TjFjEZRqTbuadQLfKGZ0jaI+jJoRyqaWwxOSHW2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/content-type": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/content-type/-/content-type-0.1.4.tgz", + "integrity": "sha512-dl6f3SHIJPYbhsW1oXdrqOmLSQF/Ctlv3JnNfXAE22kIP7FosqJHxkz/qj2gv465prG8ODKH5KEyhBkvwrueKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4" + } + }, + "node_modules/@tinyhttp/cookie": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie/-/cookie-2.1.1.tgz", + "integrity": "sha512-h/kL9jY0e0Dvad+/QU3efKZww0aTvZJslaHj3JTPmIPC9Oan9+kYqmh3M6L5JUQRuTJYFK2nzgL2iJtH2S+6dA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" + } + }, + "node_modules/@tinyhttp/cookie-signature": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cookie-signature/-/cookie-signature-2.1.1.tgz", + "integrity": "sha512-VDsSMY5OJfQJIAtUgeQYhqMPSZptehFSfvEEtxr+4nldPA8IImlp3QVcOVuK985g4AFR4Hl1sCbWCXoqBnVWnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/cors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/cors/-/cors-2.0.1.tgz", + "integrity": "sha512-qrmo6WJuaiCzKWagv2yA/kw6hIISfF/hOqPWwmI6w0o8apeTMmRN3DoCFvQ/wNVuWVdU5J4KU7OX8aaSOEq51A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/vary": "^0.1.3" + }, + "engines": { + "node": ">=12.20 || 14.x || >=16" + } + }, + "node_modules/@tinyhttp/encode-url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/encode-url/-/encode-url-2.1.1.tgz", + "integrity": "sha512-AhY+JqdZ56qV77tzrBm0qThXORbsVjs/IOPgGCS7x/wWnsa/Bx30zDUU/jPAUcSzNOzt860x9fhdGpzdqbUeUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/etag": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@tinyhttp/etag/-/etag-2.1.2.tgz", + "integrity": "sha512-j80fPKimGqdmMh6962y+BtQsnYPVCzZfJw0HXjyH70VaJBHLKGF+iYhcKqzI3yef6QBNa8DKIPsbEYpuwApXTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/forwarded": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/forwarded/-/forwarded-2.1.1.tgz", + "integrity": "sha512-nO3kq0R1LRl2+CAMlnggm22zE6sT8gfvGbNvSitV6F9eaUSurHP0A8YZFMihSkugHxK+uIegh1TKrqgD8+lyGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-8DfLQjGDIaIJeivYamVrrpmwmsGwS8wt2DGvzlcY5HEBagdiI4QJy/veAFcUHuaJqufn4wLwmn4q5VUkW8BCpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.20", + "dayjs": "^1.11.10", + "http-status-emojis": "^2.2.0" + }, + "engines": { + "node": ">=14.18 || >=16.20" + } + }, + "node_modules/@tinyhttp/proxy-addr": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@tinyhttp/proxy-addr/-/proxy-addr-2.2.0.tgz", + "integrity": "sha512-WM/PPL9xNvrs7/8Om5nhKbke5FHrP3EfjOOR+wBnjgESfibqn0K7wdUTnzSLp1lBmemr88os1XvzwymSgaibyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/forwarded": "2.1.1", + "ipaddr.js": "^2.2.0" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/req": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/req/-/req-2.2.4.tgz", + "integrity": "sha512-lQAZIAo0NOeghxFOZS57tQzxpHSPPLs9T68Krq2BncEBImKwqaDKUt7M9Y5Kb+rvC/GwIL3LeErhkg7f5iG4IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/accepts": "2.2.3", + "@tinyhttp/type-is": "2.2.4", + "@tinyhttp/url": "2.1.1", + "header-range-parser": "^1.1.3" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/res": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/res/-/res-2.2.4.tgz", + "integrity": "sha512-ETBRShnO19oJyIg2XQHQoofXPWeTXPAuwnIVYkU8WaftvXd/Vz4y5+WFQDHUzKlmdGOw5fAFnrEU7pIVMeFeVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-disposition": "2.2.2", + "@tinyhttp/cookie": "2.1.1", + "@tinyhttp/cookie-signature": "2.1.1", + "@tinyhttp/encode-url": "2.1.1", + "@tinyhttp/req": "2.2.4", + "@tinyhttp/send": "2.2.3", + "@tinyhttp/vary": "^0.1.3", + "es-escape-html": "^0.1.1", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/router": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/router/-/router-2.2.3.tgz", + "integrity": "sha512-O0MQqWV3Vpg/uXsMYg19XsIgOhwjyhTYWh51Qng7bxqXixxx2PEvZWnFjP7c84K7kU/nUX41KpkEBTLnznk9/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/send": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/send/-/send-2.2.3.tgz", + "integrity": "sha512-o4cVHHGQ8WjVBS8UT0EE/2WnjoybrfXikHwsRoNlG1pfrC/Sd01u1N4Te8cOd/9aNGLr4mGxWb5qTm2RRtEi7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "@tinyhttp/etag": "2.1.2", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/type-is": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@tinyhttp/type-is/-/type-is-2.2.4.tgz", + "integrity": "sha512-7F328NheridwjIfefBB2j1PEcKKABpADgv7aCJaE8x8EON77ZFrAkI3Rir7pGjopV7V9MBmW88xUQigBEX2rmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tinyhttp/content-type": "^0.1.4", + "mime": "4.0.4" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/url": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@tinyhttp/url/-/url-2.1.1.tgz", + "integrity": "sha512-POJeq2GQ5jI7Zrdmj22JqOijB5/GeX+LEX7DUdml1hUnGbJOTWDx7zf2b5cCERj7RoXL67zTgyzVblBJC+NJWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@tinyhttp/vary": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@tinyhttp/vary/-/vary-0.1.3.tgz", + "integrity": "sha512-SoL83sQXAGiHN1jm2VwLUWQSQeDAAl1ywOm6T0b0Cg1CZhVsjoiZadmjhxF6FHCCY7OHHVaLnTgSMxTPIDLxMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -4228,6 +4493,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -4422,6 +4694,35 @@ "tslib": "^2.0.3" } }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "4.31.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.31.0.tgz", + "integrity": "sha512-yCxltHW07Nkhv/1F6wWBr8kz+5BGMfP+RbRSYFnegVb0qV/UMT0G0ElBloPVerqn4M2ZV80Ir1FtCcYv1cT6vQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4603,6 +4904,16 @@ "node": ">= 0.4" } }, + "node_modules/es-escape-html": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/es-escape-html/-/es-escape-html-0.1.1.tgz", + "integrity": "sha512-yUx1o+8RsG7UlszmYPtks+dm6Lho2m8lgHMOsLJQsFI0R8XwUJwiMhM1M4E/S8QLeGyf6MkDV/pWgjQ0tdTSyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.x" + } + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -4958,6 +5269,19 @@ "node": ">=0.10.0" } }, + "node_modules/eta": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-3.5.0.tgz", + "integrity": "sha512-e3x3FBvGzeCIHhF+zhK8FZA2vC5uFn6b4HJjegUbIWrDb4mJ7JjTGMJY9VGIbRVpmSwHopNiaJibhjIr+HfLug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "url": "https://github.com/eta-dev/eta?sponsor=1" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -5553,6 +5877,23 @@ "node": ">= 0.4" } }, + "node_modules/header-range-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/header-range-parser/-/header-range-parser-1.1.3.tgz", + "integrity": "sha512-B9zCFt3jH8g09LR1vHL4pcAn8yMEtlSlOUdQemzHMRKMImNIhhszdeosYFfNW0WXKQtXIlWB+O4owHJKvEJYaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/http-status-emojis": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/http-status-emojis/-/http-status-emojis-2.2.0.tgz", + "integrity": "sha512-ompKtgwpx8ff0hsbpIB7oE4ax1LXoHmftsHHStMELX56ivG3GhofTX8ZHWlUaFKfGjcGjw6G3rPk7dJRXMmbbg==", + "dev": true, + "license": "MIT" + }, "node_modules/ico-endec": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ico-endec/-/ico-endec-0.1.6.tgz", @@ -5625,6 +5966,16 @@ "node": ">=0.8.19" } }, + "node_modules/inflection": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-3.0.1.tgz", + "integrity": "sha512-EpyF+TLa3xZPClbkp9pDJZtJFYPHouWuFYsfAQI8AfjnaPVOpmRV6GSzCs+QGelj2eZ7oQllcK23aI1vHhPVVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5666,6 +6017,16 @@ "node": ">= 0.4" } }, + "node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -6281,6 +6642,77 @@ "dev": true, "license": "MIT" }, + "node_modules/json-server": { + "version": "1.0.0-beta.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-1.0.0-beta.3.tgz", + "integrity": "sha512-DwE69Ep5ccwIJZBUIWEENC30Yj8bwr4Ax9W9VoIWAYnB8Sj4ReptscO8/DRHv/nXwVlmb3Bk73Ls86+VZdYkkA==", + "dev": true, + "license": "SEE LICENSE IN ./LICENSE", + "dependencies": { + "@tinyhttp/app": "^2.4.0", + "@tinyhttp/cors": "^2.0.1", + "@tinyhttp/logger": "^2.0.0", + "chalk": "^5.3.0", + "chokidar": "^4.0.1", + "dot-prop": "^9.0.0", + "eta": "^3.5.0", + "inflection": "^3.0.0", + "json5": "^2.2.3", + "lowdb": "^7.0.1", + "milliparsec": "^4.0.0", + "sirv": "^2.0.4", + "sort-on": "^6.1.0" + }, + "bin": { + "json-server": "lib/bin.js" + }, + "engines": { + "node": ">=18.3" + } + }, + "node_modules/json-server/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/json-server/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/json-server/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -6442,6 +6874,22 @@ "loose-envify": "cli.js" } }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dev": true, + "license": "MIT", + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -6506,6 +6954,32 @@ "node": ">=8.6" } }, + "node_modules/milliparsec": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/milliparsec/-/milliparsec-4.0.0.tgz", + "integrity": "sha512-/wk9d4Z6/9ZvoEH/6BI4TrTCgmkpZPuSRN/6fI9aUHOfXdNTuj/VhLS7d+NqG26bi6L9YmGXutVYvWC8zQ0qtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -6562,6 +7036,16 @@ "dev": true, "license": "MIT" }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6614,6 +7098,16 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -7480,6 +7974,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexparam": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/regexparam/-/regexparam-2.0.2.tgz", + "integrity": "sha512-A1PeDEYMrkLrfyOwv2jwihXbo9qxdGD3atBYQA9JJgreAx8/7rC6IUkWOw2NQlOxLp2wL0ifQbh1HuidDfYA6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/regexpu-core": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", @@ -8005,6 +8509,21 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8043,6 +8562,22 @@ "tslib": "^2.0.3" } }, + "node_modules/sort-on": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-6.1.0.tgz", + "integrity": "sha512-WTECP0nYNWO1n2g5bpsV0yZN9cBmZsF8ThHFbOqVN0HBFRoaQZLLEMvMmJlKHNPYQeVngeI5+jJzIfFqOIo1OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-prop": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/source-map": { "version": "0.8.0-beta.0", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", @@ -8095,6 +8630,19 @@ "dev": true, "license": "MIT" }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/streamx": { "version": "2.21.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", @@ -8650,6 +9198,16 @@ "node": ">=8.0" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tr46": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index d9e3161..6e81a7b 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -29,6 +29,7 @@ "eslint-config-prettier": "^9.1.0", "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.6", + "json-server": "^1.0.0-beta.3", "postcss": "^8.4.49", "prettier": "^3.4.2", "slick-carousel": "^1.8.1", diff --git a/newlearn/routes.json b/newlearn/routes.json new file mode 100644 index 0000000..50d20ba --- /dev/null +++ b/newlearn/routes.json @@ -0,0 +1,3 @@ +{ + "/v1/articles": "/v1/articles" +} diff --git a/newlearn/src/components/carousel/carousel.tsx b/newlearn/src/components/carousel/carousel.tsx index 0609d38..4c1559c 100644 --- a/newlearn/src/components/carousel/carousel.tsx +++ b/newlearn/src/components/carousel/carousel.tsx @@ -2,16 +2,10 @@ import Slider from 'react-slick'; import 'slick-carousel/slick/slick.css'; import 'slick-carousel/slick/slick-theme.css'; import MainNews from './mainNews'; - -type News = { - thumbnail: string; - title: string; - time: string; - source: string; -}; +import { news } from '../../types/news'; interface CarouselProps { - items: News[]; // 이미지 URL 또는 콘텐츠 + items: news[]; // 개별 뉴스 항목 배열 } const Carousel: React.FC = ({ items }) => { @@ -32,7 +26,7 @@ const Carousel: React.FC = ({ items }) => {
diff --git a/newlearn/src/hooks/api.ts b/newlearn/src/hooks/api.ts new file mode 100644 index 0000000..c317c2c --- /dev/null +++ b/newlearn/src/hooks/api.ts @@ -0,0 +1,50 @@ +import { useState, useCallback } from 'react'; + +interface ApiState { + data: T | null; + loading: boolean; + error: string | null; +} + +type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; + +const useApi = () => { + const [state, setState] = useState>({ + data: null, + loading: false, + error: null, + }); + + const request = useCallback( + async ( + url: string, + method: HttpMethod = 'GET', + body?: any, + headers?: HeadersInit, + ) => { + setState({ data: null, loading: true, error: null }); + try { + const response = await fetch(url, { + method, + body: body ? JSON.stringify(body) : null, + headers: { + 'Content-Type': 'application/json', + ...headers, + }, + }); + if (!response.ok) { + throw new Error(`Error: ${response.statusText}`); + } + const data: T = await response.json(); + setState({ data, loading: false, error: null }); + } catch (error: any) { + setState({ data: null, loading: false, error: error.message }); + } + }, + [], + ); + + return { ...state, request }; +}; + +export default useApi; diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage.tsx/index.tsx index 81cc5d4..36af3d2 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage.tsx/index.tsx @@ -1,38 +1,52 @@ +import { useEffect } from 'react'; import Cardnews from '../../components/cardnews'; import Carousel from '../../components/carousel/carousel'; +import useApi from '../../hooks/api'; import BasicLayout from '../../layout/basicLayout'; +import { ApiResponse, news } from '../../types/news'; import Header from './Header'; const HomePage = () => { - const mockup = [ - { - thumbnail: 'https://url.kr/qsyxm9', - title: '너무 귀여워서 충격.. 실화', - time: '2시간 전', - source: '매일경제', - }, - { - thumbnail: 'https://url.kr/qsyxm9', - title: '너무 귀여워서 충격.. 실화', - time: '2시간 전', - source: '매일경제', - }, - { - thumbnail: 'https://url.kr/qsyxm9', - title: '너무 귀여워서 충격.. 실화', - time: '2시간 전', - source: '매일경제', - }, - ]; + const { data, loading, error, request } = useApi(); + + // 데이터 요청 + useEffect(() => { + const fetchData = async () => { + await request('http://localhost:3000/articles', 'GET'); + }; + fetchData(); + }, [request]); + + if (loading) { + return

Loading...

; + } + + if (error) { + return

Error: {error}

; + } + + // articles 데이터 분리 + const articles: news[] = data?.data.articles || []; + const carouselItems = articles.slice(0, 3); // 첫 3개 아이템 + const cardNewsItems = articles.slice(3); // 나머지 아이템 + return ( }> - - + {/* Carousel에 첫 3개 데이터 전달 */} + + +
+ {cardNewsItems.map((item, index) => ( + + ))} +
); }; diff --git a/newlearn/src/types/news.ts b/newlearn/src/types/news.ts index 958add9..386a35b 100644 --- a/newlearn/src/types/news.ts +++ b/newlearn/src/types/news.ts @@ -9,3 +9,19 @@ export type news = { export type NewsData = { newsList: news[]; }; + +export interface Article { + newsId: number; + thumbnail: string; + title: string; + publishedDate: string; + source: string; +} + +export interface ApiResponse { + status: string; + message: string; + data: { + articles: Article[]; + }; +} diff --git a/newlearn/vite.config.ts.timestamp-1736522539273-3f647e2259e4d.mjs b/newlearn/vite.config.ts.timestamp-1736522539273-3f647e2259e4d.mjs new file mode 100644 index 0000000..d2a2702 --- /dev/null +++ b/newlearn/vite.config.ts.timestamp-1736522539273-3f647e2259e4d.mjs @@ -0,0 +1,56 @@ +// vite.config.ts +import { VitePWA } from "file:///C:/Users/MYCOM/Desktop/NewLearn/newlearn/node_modules/vite-plugin-pwa/dist/index.js"; +import { defineConfig } from "file:///C:/Users/MYCOM/Desktop/NewLearn/newlearn/node_modules/vite/dist/node/index.js"; +import svgr from "file:///C:/Users/MYCOM/Desktop/NewLearn/newlearn/node_modules/vite-plugin-svgr/dist/index.js"; +import react from "file:///C:/Users/MYCOM/Desktop/NewLearn/newlearn/node_modules/@vitejs/plugin-react/dist/index.mjs"; +import tailwindcss from "file:///C:/Users/MYCOM/Desktop/NewLearn/newlearn/node_modules/tailwindcss/lib/index.js"; +var vite_config_default = defineConfig({ + base: "/", + plugins: [ + react(), + svgr(), + VitePWA({ + registerType: "autoUpdate", + injectRegister: false, + manifest: { + name: "newlearn", + short_name: "newlearn", + description: "\uC5B4\uB824\uC6B4 \uB274\uC2A4 \uAE30\uC0AC\uB97C \uC27D\uACE0 \uBE60\uB974\uAC8C", + theme_color: "#ffffff", + start_url: "/", + icons: [ + { + src: "icons/apple-touch-icon-60x60.png", + sizes: "60x60", + type: "image/png" + }, + { + src: "icons/apple-touch-icon-152x152.png", + sizes: "192x192", + type: "image/png" + } + ] + }, + workbox: { + globPatterns: ["**/*.{js,css,html,svg,png,ico,json}"], + cleanupOutdatedCaches: true, + clientsClaim: true + }, + devOptions: { + enabled: true, + navigateFallback: "index.html", + suppressWarnings: true, + type: "module" + } + }) + ], + css: { + postcss: { + plugins: [tailwindcss()] + } + } +}); +export { + vite_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxNWUNPTVxcXFxEZXNrdG9wXFxcXE5ld0xlYXJuXFxcXG5ld2xlYXJuXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxNWUNPTVxcXFxEZXNrdG9wXFxcXE5ld0xlYXJuXFxcXG5ld2xlYXJuXFxcXHZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9DOi9Vc2Vycy9NWUNPTS9EZXNrdG9wL05ld0xlYXJuL25ld2xlYXJuL3ZpdGUuY29uZmlnLnRzXCI7aW1wb3J0IHsgVml0ZVBXQSB9IGZyb20gJ3ZpdGUtcGx1Z2luLXB3YSc7XHJcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gJ3ZpdGUnO1xyXG5pbXBvcnQgc3ZnciBmcm9tICd2aXRlLXBsdWdpbi1zdmdyJztcclxuaW1wb3J0IHJlYWN0IGZyb20gJ0B2aXRlanMvcGx1Z2luLXJlYWN0JztcclxuaW1wb3J0IHRhaWx3aW5kY3NzIGZyb20gJ3RhaWx3aW5kY3NzJztcclxuXHJcbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXHJcbmV4cG9ydCBkZWZhdWx0IGRlZmluZUNvbmZpZyh7XHJcbiAgYmFzZTogJy8nLFxyXG4gIHBsdWdpbnM6IFtcclxuICAgIHJlYWN0KCksXHJcbiAgICBzdmdyKCksXHJcbiAgICBWaXRlUFdBKHtcclxuICAgICAgcmVnaXN0ZXJUeXBlOiAnYXV0b1VwZGF0ZScsXHJcbiAgICAgIGluamVjdFJlZ2lzdGVyOiBmYWxzZSxcclxuXHJcbiAgICAgIG1hbmlmZXN0OiB7XHJcbiAgICAgICAgbmFtZTogJ25ld2xlYXJuJyxcclxuICAgICAgICBzaG9ydF9uYW1lOiAnbmV3bGVhcm4nLFxyXG4gICAgICAgIGRlc2NyaXB0aW9uOiAnXHVDNUI0XHVCODI0XHVDNkI0IFx1QjI3NFx1QzJBNCBcdUFFMzBcdUMwQUNcdUI5N0MgXHVDMjdEXHVBQ0UwIFx1QkU2MFx1Qjk3NFx1QUM4QycsXHJcbiAgICAgICAgdGhlbWVfY29sb3I6ICcjZmZmZmZmJyxcclxuICAgICAgICBzdGFydF91cmw6ICcvJyxcclxuICAgICAgICBpY29uczogW1xyXG4gICAgICAgICAge1xyXG4gICAgICAgICAgICBzcmM6ICdpY29ucy9hcHBsZS10b3VjaC1pY29uLTYweDYwLnBuZycsXHJcbiAgICAgICAgICAgIHNpemVzOiAnNjB4NjAnLFxyXG4gICAgICAgICAgICB0eXBlOiAnaW1hZ2UvcG5nJyxcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgICB7XHJcbiAgICAgICAgICAgIHNyYzogJ2ljb25zL2FwcGxlLXRvdWNoLWljb24tMTUyeDE1Mi5wbmcnLFxyXG4gICAgICAgICAgICBzaXplczogJzE5MngxOTInLFxyXG4gICAgICAgICAgICB0eXBlOiAnaW1hZ2UvcG5nJyxcclxuICAgICAgICAgIH0sXHJcbiAgICAgICAgXSxcclxuICAgICAgfSxcclxuXHJcbiAgICAgIHdvcmtib3g6IHtcclxuICAgICAgICBnbG9iUGF0dGVybnM6IFsnKiovKi57anMsY3NzLGh0bWwsc3ZnLHBuZyxpY28sanNvbn0nXSxcclxuICAgICAgICBjbGVhbnVwT3V0ZGF0ZWRDYWNoZXM6IHRydWUsXHJcbiAgICAgICAgY2xpZW50c0NsYWltOiB0cnVlLFxyXG4gICAgICB9LFxyXG5cclxuICAgICAgZGV2T3B0aW9uczoge1xyXG4gICAgICAgIGVuYWJsZWQ6IHRydWUsXHJcbiAgICAgICAgbmF2aWdhdGVGYWxsYmFjazogJ2luZGV4Lmh0bWwnLFxyXG4gICAgICAgIHN1cHByZXNzV2FybmluZ3M6IHRydWUsXHJcbiAgICAgICAgdHlwZTogJ21vZHVsZScsXHJcbiAgICAgIH0sXHJcbiAgICB9KSxcclxuICBdLFxyXG4gIGNzczoge1xyXG4gICAgcG9zdGNzczoge1xyXG4gICAgICBwbHVnaW5zOiBbdGFpbHdpbmRjc3MoKV0sXHJcbiAgICB9LFxyXG4gIH0sXHJcbn0pO1xyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQXNULFNBQVMsZUFBZTtBQUM5VSxTQUFTLG9CQUFvQjtBQUM3QixPQUFPLFVBQVU7QUFDakIsT0FBTyxXQUFXO0FBQ2xCLE9BQU8saUJBQWlCO0FBR3hCLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLE1BQU07QUFBQSxFQUNOLFNBQVM7QUFBQSxJQUNQLE1BQU07QUFBQSxJQUNOLEtBQUs7QUFBQSxJQUNMLFFBQVE7QUFBQSxNQUNOLGNBQWM7QUFBQSxNQUNkLGdCQUFnQjtBQUFBLE1BRWhCLFVBQVU7QUFBQSxRQUNSLE1BQU07QUFBQSxRQUNOLFlBQVk7QUFBQSxRQUNaLGFBQWE7QUFBQSxRQUNiLGFBQWE7QUFBQSxRQUNiLFdBQVc7QUFBQSxRQUNYLE9BQU87QUFBQSxVQUNMO0FBQUEsWUFDRSxLQUFLO0FBQUEsWUFDTCxPQUFPO0FBQUEsWUFDUCxNQUFNO0FBQUEsVUFDUjtBQUFBLFVBQ0E7QUFBQSxZQUNFLEtBQUs7QUFBQSxZQUNMLE9BQU87QUFBQSxZQUNQLE1BQU07QUFBQSxVQUNSO0FBQUEsUUFDRjtBQUFBLE1BQ0Y7QUFBQSxNQUVBLFNBQVM7QUFBQSxRQUNQLGNBQWMsQ0FBQyxxQ0FBcUM7QUFBQSxRQUNwRCx1QkFBdUI7QUFBQSxRQUN2QixjQUFjO0FBQUEsTUFDaEI7QUFBQSxNQUVBLFlBQVk7QUFBQSxRQUNWLFNBQVM7QUFBQSxRQUNULGtCQUFrQjtBQUFBLFFBQ2xCLGtCQUFrQjtBQUFBLFFBQ2xCLE1BQU07QUFBQSxNQUNSO0FBQUEsSUFDRixDQUFDO0FBQUEsRUFDSDtBQUFBLEVBQ0EsS0FBSztBQUFBLElBQ0gsU0FBUztBQUFBLE1BQ1AsU0FBUyxDQUFDLFlBQVksQ0FBQztBQUFBLElBQ3pCO0FBQUEsRUFDRjtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg== From fcb79429cf641ec72610b41a3751ac36d87aa8d0 Mon Sep 17 00:00:00 2001 From: yook-jongho Date: Sun, 19 Jan 2025 10:34:54 +0900 Subject: [PATCH 27/27] =?UTF-8?q?feat:=20kakao=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- newlearn/.gitignore | 2 + newlearn/package-lock.json | 8 ++ newlearn/package.json | 1 + newlearn/public/kakao_login_medium_narrow.png | Bin 0 -> 2946 bytes newlearn/routes.json | 3 +- newlearn/src/App.tsx | 34 +++++++-- newlearn/src/components/kakaologinbtn.tsx | 18 +++++ newlearn/src/hooks/login.ts | 23 ++++++ .../{HomePage.tsx => HomePage}/Header.tsx | 0 .../{HomePage.tsx => HomePage}/index.tsx | 3 +- newlearn/src/pages/LoginPage/index.tsx | 14 ++++ .../src/pages/NewsDetailPage.tsx/index.tsx | 37 --------- .../Header.tsx | 0 .../NewsInfo.tsx | 10 +-- newlearn/src/pages/NewsDetailPage/index.tsx | 58 ++++++++++++++ newlearn/src/pages/OauthPage/index.tsx | 71 ++++++++++++++++++ newlearn/src/types/authobj.ts | 8 ++ newlearn/src/types/kakao.d.ts | 7 ++ newlearn/src/types/news.ts | 18 ++--- 19 files changed, 252 insertions(+), 63 deletions(-) create mode 100644 newlearn/public/kakao_login_medium_narrow.png create mode 100644 newlearn/src/components/kakaologinbtn.tsx create mode 100644 newlearn/src/hooks/login.ts rename newlearn/src/pages/{HomePage.tsx => HomePage}/Header.tsx (100%) rename newlearn/src/pages/{HomePage.tsx => HomePage}/index.tsx (94%) create mode 100644 newlearn/src/pages/LoginPage/index.tsx delete mode 100644 newlearn/src/pages/NewsDetailPage.tsx/index.tsx rename newlearn/src/pages/{NewsDetailPage.tsx => NewsDetailPage}/Header.tsx (100%) rename newlearn/src/pages/{NewsDetailPage.tsx => NewsDetailPage}/NewsInfo.tsx (85%) create mode 100644 newlearn/src/pages/NewsDetailPage/index.tsx create mode 100644 newlearn/src/pages/OauthPage/index.tsx create mode 100644 newlearn/src/types/authobj.ts create mode 100644 newlearn/src/types/kakao.d.ts diff --git a/newlearn/.gitignore b/newlearn/.gitignore index 076f805..422c407 100644 --- a/newlearn/.gitignore +++ b/newlearn/.gitignore @@ -24,3 +24,5 @@ dev-dist *.sln *.sw? db.json +routes.json +.env \ No newline at end of file diff --git a/newlearn/package-lock.json b/newlearn/package-lock.json index 3d86e4a..4fd4ad6 100644 --- a/newlearn/package-lock.json +++ b/newlearn/package-lock.json @@ -14,6 +14,7 @@ "react-slick": "^0.30.3" }, "devDependencies": { + "@types/kakao-js-sdk": "^1.39.5", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@types/react-slick": "^0.23.13", @@ -3301,6 +3302,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/kakao-js-sdk": { + "version": "1.39.5", + "resolved": "https://registry.npmjs.org/@types/kakao-js-sdk/-/kakao-js-sdk-1.39.5.tgz", + "integrity": "sha512-+u+sfAq2ZcBM4yF3zE/FDxaNq85DeLkKh8K6jP9GQwGdbIJ/IGwoAQEfkbG5tL5XHOP30b3KeHmxDFG2n1E96Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", diff --git a/newlearn/package.json b/newlearn/package.json index 6e81a7b..0dae5f6 100644 --- a/newlearn/package.json +++ b/newlearn/package.json @@ -17,6 +17,7 @@ "react-slick": "^0.30.3" }, "devDependencies": { + "@types/kakao-js-sdk": "^1.39.5", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.0", "@types/react-slick": "^0.23.13", diff --git a/newlearn/public/kakao_login_medium_narrow.png b/newlearn/public/kakao_login_medium_narrow.png new file mode 100644 index 0000000000000000000000000000000000000000..09bb358843a08576d0029ff8427120c9f5f5c36d GIT binary patch literal 2946 zcmV-|3w`v7P)Px=JV``BRCodHT?=$n#Tou~^I8JtArcY-;n7xG;YdSyzvLw#RsjQ?LV-plh_pbV z7&Y>!6{SGyQ7PIA1q-OyLJ?6xkSGWUhC(S+KzRr@5E4Up1d{9~+0K7&=I-8o-o1M_ z>K zJfyi-#pdme(05ddSGi|FWMQ#bu-~&sqw*H#dF*pK(aop^c{`RCutpXZ-Vqb+{@?~PST%axaN|9sl5JgJIH%c4~IU>To?lhB0+0XCxP3iNAxKi{VC zjvQKnb(`Xq{N3HFP+286-lkOzX3S4Srw$HekGZbU1WWi7JQ!0$sJB|v8L$zYDQlkR?lC+CKkVmPTkqT#7o4e%f+nkhKWm0nES@$uNi!c2 zssx`jE+k%wc<(zU3e%*=la=(SN1GyRa#JNw%RavJkZar|{jF zIh9v8Swf}bW|5sHAS=!LW)Ya=RHl5htyRnfvZt&O-$U^1P~<)4O#w*c5zssi&Rq<2 z=r2E%7C|2ZUJESuX4v?S^iJ+2-sy0*FeW|j+Fx0*j9y6a&uMW@dlNWz;7OTq#^{GU^9##0; zKN=%%Q3CGmUj>qbR=6vclzG3b0VUCa+Sq#_P_10IYaeiTkKlB^D84)sjm-9T96Kqm zeu?J(hi*eclUg{cEx1q|B>_OI zUQw03yRDIwC$PWyyomMf`gBkc8kh;F;k=)&!+`(EO2UiKw>z-zRV5p8qPEt8R$Wu^ z)u$H`-$>rj;owbqDjCU%wOIJ13=jd%HJo?EX?fl=e)gpV%$;%7v)FHb{rmA4b)WnV zw)GPmhW4xUOB+swCctwS0E_2@(~UOX8x>Xse0L7|<5;b(VZ*)7Y-vq^o9(3sC zDGm80%p{xXK9S7?uBWWhmnIdmV~89Uy}*`%O+1N2K0E~29!fcDVHN{>xy4zC9c(HZ z5MGmlI~W3Nw?AJ@mcdXmuwG1*27(!^DFJpcgbb_~U>gW#u%-mq!4NXAUK7|yjsZi) z0vYT$mg1ww4V2(J)Kt*b2mxGO?Ttt@+ebWC;9ah_giI)+qI~a%kg3HjY=^ME0&Vd& za@*21s^5TM_ZkkIIp@Etwi&>lP5$cx)dbql_O}O4?Jauc7QDPt{`Q)gQ;yk>UQ>0` zx_g=`KW@YgX-9F}P8)XZSDTjbt=J#+PsO!z3l489M$_aPJeHS?SD!b2uRGvAcOanY zje^yy-r6J6vvU>bp};LSI05{ z;03wQ$u>BH@2&(rJaqkO3Krx7Il0L=_HmI^9BeC>^XiQ?joEq|55z|nvSS`=iauE> zig^qj9FT=9*#XU%pTvd^*RVo|4YoKZ&KJ2XXwP4alFHos$>^2_GAZYezkTlFQn{U8 zo$Ywzr==VQEdZ#>L&K+RtBGB9Df{~oAbZ+K(rB?%BZJ>g;F@>1zoY}fi? z(5;O_TU{qHn^%1+ksUu3gCF!s1s&%tefA1krl|p{sR2e!ah=4(v17LFZ@LV=cwdgX z9|PGzUAi2_9t-l{_NL?XM#KTZ9@qzX_jTZbZ11kNtOYu@_b%63!cS;C&jH;}bu1Q_ zX7hH34X8jyi)u;hTenmLO*H)IY}mxx@WE>N55#%h;}bhCD%sBWrQq})*YVWIAr(rU zk%@p{Q%85+E_XLR3z#<(Xwul2?*F{d_XI>IG#oI`o;Gfk&k}k0!K3n-CfUJL*s#07h{YA+22b9Ituf5 zeCDsMhFTsJ?1n118RfcZs}VDT6|T$z>~IkbfnR_f0$Aaa3b4aPFa&-posES+FGzJF zKsZ}1=@`gPfG5|T0_$u_u=zuPaFy^SoQcR;f;8B%mVK`c@W&*|1p;P3KpLrIG2YS) z7wD>R4%^px%?yGI83h7b5s*f2rrg(r@tIc01f5wBpyi2GD7IPtTtd!J5Fou4pYEX` zCTLBCfKYBz{kep&p&%fXI~248t*H<&v6=ry{E%>`XFc`?43bbn-oK{c> z1j0cetdaY(1G1-w-XpXrHVH~U0HP-k5yamR1Kq19<4bausWk-SA$ys^qG%jC8Y>wR zIi1?u)+$5zbh3=TJlv6Y3$pq(Q_wwf?=4`qjg$4iD*Kv67P|$zp6#&rWVZPA8OV3? zC!eX!xL0q8$DrPom_O6E;|D!F%%c|`{!Oyv6L0!M<&sO3%no*Z{Y^A;jS`QSmeESRmr;YoPhVE5$jZ9laIBK|Oe}wkc{O0b&*tqme7G|Xr*;>q`~;EH$87dw4Xcn`at88|HUC?Qlp|;8G#rze~<1hSmUA*K0hS#rQKkP0Y9hm;GtnB@I)L>T5HObW{%{J+& z-nPV?w;{*6m77doSFQt2pVR+(>8ZdYWA*dvItYv2%TiX%(B^O}BDK zv1=>4*=@458-ORDXaA!l#M^bh&jTio=J3{k{2{wxmbWMoZP^HV^3x*e3W}!`gZoyh z%JuNf`}I}OL*WQ>+1qnOGwj(|0y6S{@wcv}glFG8Pb2X9zm($hf6L1Z^7MA}L=2Y9 zaq&j6FQ=a{FLPSL?t)mvuzLpGJGs+4@w?eq*bKnD)OEG1E{$nvv!T)$W-~vEt)G2) zwj&pQEt&&eGl8)qz^2Qa55<}djhVYahu28WC8LkS2{V#$X0J=>L*?1>u}$*g&D*57 z{5rdm`9%y3y4u*=Q|~OznE5VR7CS#etkBt=aO1D0lvDVEi<;1HzUWf1D4=W1!i9aF^GY za=X|2O6%eA)y~Z9PV7be0)}6*3A0LPTJe swC^J>2!{xpAaAe;Fon)X-38(Q1DqPh - } /> + } /> + + + + } + /> } /> - } /> - } /> + + + + } + /> + + + + } + />
diff --git a/newlearn/src/components/kakaologinbtn.tsx b/newlearn/src/components/kakaologinbtn.tsx new file mode 100644 index 0000000..c1c8874 --- /dev/null +++ b/newlearn/src/components/kakaologinbtn.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +const KakaoLoginButton: React.FC = () => { + const K_REST_API_KEY = import.meta.env.VITE_KAKAO_REST_API_KEY; + const K_REDIRECT_URI = import.meta.env.VITE_REDIRECT_URL; + const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${K_REST_API_KEY}&redirect_uri=${K_REDIRECT_URI}&response_type=code`; + + const handleLogin = () => { + console.log(kakaoURL); + window.location.href = kakaoURL; + }; + + return ( + + ); +}; + +export default KakaoLoginButton; diff --git a/newlearn/src/hooks/login.ts b/newlearn/src/hooks/login.ts new file mode 100644 index 0000000..d32bb49 --- /dev/null +++ b/newlearn/src/hooks/login.ts @@ -0,0 +1,23 @@ +import { useEffect } from 'react'; + +const useKakaoInit = () => { + useEffect(() => { + const kakaoJsKey = import.meta.env.VITE_KAKAO_JS_KEY; + if (!kakaoJsKey) { + console.error('Kakao JavaScript Key is missing.'); + return; + } + + if (!window.Kakao) { + console.error('Kakao SDK not loaded.'); + return; + } + + if (!window.Kakao.isInitialized()) { + window.Kakao.init(kakaoJsKey); + console.log('Kakao SDK initialized'); + } + }, []); +}; + +export default useKakaoInit; diff --git a/newlearn/src/pages/HomePage.tsx/Header.tsx b/newlearn/src/pages/HomePage/Header.tsx similarity index 100% rename from newlearn/src/pages/HomePage.tsx/Header.tsx rename to newlearn/src/pages/HomePage/Header.tsx diff --git a/newlearn/src/pages/HomePage.tsx/index.tsx b/newlearn/src/pages/HomePage/index.tsx similarity index 94% rename from newlearn/src/pages/HomePage.tsx/index.tsx rename to newlearn/src/pages/HomePage/index.tsx index 36af3d2..dc1b52c 100644 --- a/newlearn/src/pages/HomePage.tsx/index.tsx +++ b/newlearn/src/pages/HomePage/index.tsx @@ -7,7 +7,8 @@ import { ApiResponse, news } from '../../types/news'; import Header from './Header'; const HomePage = () => { - const { data, loading, error, request } = useApi(); + const { data, loading, error, request } = + useApi>(); // 데이터 요청 useEffect(() => { diff --git a/newlearn/src/pages/LoginPage/index.tsx b/newlearn/src/pages/LoginPage/index.tsx new file mode 100644 index 0000000..7ea1efe --- /dev/null +++ b/newlearn/src/pages/LoginPage/index.tsx @@ -0,0 +1,14 @@ +import KakaoLoginButton from '../../components/kakaologinbtn'; + +const LoginPage = () => { + return ( +
+
+ + +
+
+ ); +}; + +export default LoginPage; diff --git a/newlearn/src/pages/NewsDetailPage.tsx/index.tsx b/newlearn/src/pages/NewsDetailPage.tsx/index.tsx deleted file mode 100644 index ae4deb7..0000000 --- a/newlearn/src/pages/NewsDetailPage.tsx/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { useParams } from 'react-router'; -import { useNavigate } from 'react-router'; -import Header from './Header'; -import NewsInfo from './NewsInfo'; - -const NewsDetailPage = () => { - const { newsId } = useParams<{ newsId: string }>(); - const navigate = useNavigate(); - - // 이전 페이지로 돌아가는 함수 - const goBack = () => { - navigate(-1); // 히스토리 스택에서 한 단계 뒤로 이동 - }; - - const mockup = { - newsId: 101, - title: 'Latest AI breakthrough', - source: 'TechTimes', - publishedDate: '2024-12-31T23:00:00Z', - thumbnail: 'https://url.kr/qsyxm9', - content: - 'The full article content goes here. This section contains all the detailed information about the news, including insights, statistics, and expert opinions.Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptate at earum consequuntur officia repellat doloremque numquam similique et adipisci ex, non eaque, itaque inventore! Fugiat adipisci ut dignissimos cupiditate. Laborum!', - }; - return ( - <> -
- - - ); -}; - -export default NewsDetailPage; diff --git a/newlearn/src/pages/NewsDetailPage.tsx/Header.tsx b/newlearn/src/pages/NewsDetailPage/Header.tsx similarity index 100% rename from newlearn/src/pages/NewsDetailPage.tsx/Header.tsx rename to newlearn/src/pages/NewsDetailPage/Header.tsx diff --git a/newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx b/newlearn/src/pages/NewsDetailPage/NewsInfo.tsx similarity index 85% rename from newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx rename to newlearn/src/pages/NewsDetailPage/NewsInfo.tsx index ab806d9..eb8161a 100644 --- a/newlearn/src/pages/NewsDetailPage.tsx/NewsInfo.tsx +++ b/newlearn/src/pages/NewsDetailPage/NewsInfo.tsx @@ -1,15 +1,9 @@ import { hoursSince } from '../../utils/timeCalc'; import Bookmark from '../../assets/bookmark-line.svg?react'; import Link from '../../assets/links-line.svg?react'; +import { NewsDetail } from '../../types/news'; -type NewsInfoProps = { - title: string; - publishedDate: string; - source: string; - content: string; -}; - -const NewsInfo: React.FC = ({ +const NewsInfo: React.FC = ({ title, publishedDate, source, diff --git a/newlearn/src/pages/NewsDetailPage/index.tsx b/newlearn/src/pages/NewsDetailPage/index.tsx new file mode 100644 index 0000000..d08018f --- /dev/null +++ b/newlearn/src/pages/NewsDetailPage/index.tsx @@ -0,0 +1,58 @@ +import { useParams } from 'react-router'; +import Header from './Header'; +import NewsInfo from './NewsInfo'; +import useApi from '../../hooks/api'; +import { ApiResponse, NewsDetail } from '../../types/news'; +import { useEffect } from 'react'; + +const NewsDetailPage = () => { + const { newsId } = useParams<{ newsId: string }>(); + const { data, loading, error, request } = + useApi>(); + + // 기본값 설정 + const defaultArticle: NewsDetail = { + newsId: 0, + title: '', + source: '', + publishedDate: '', + thumbnail: '', + content: '', + }; + + // 데이터 요청 + useEffect(() => { + const fetchData = async () => { + await request(`http://localhost:3000/articles/details/${newsId}`, 'GET'); + }; + fetchData(); + }, [newsId, request]); + + const articles: NewsDetail = data?.data.articles || defaultArticle; + + console.log(articles); + + if (loading) { + return

Loading...

; + } + + if (error) { + return

Error: {error}

; + } + + return ( + <> +
+ + + ); +}; + +export default NewsDetailPage; diff --git a/newlearn/src/pages/OauthPage/index.tsx b/newlearn/src/pages/OauthPage/index.tsx new file mode 100644 index 0000000..51e9c9c --- /dev/null +++ b/newlearn/src/pages/OauthPage/index.tsx @@ -0,0 +1,71 @@ +import React, { useEffect, useState } from 'react'; +import { Navigate, useLocation, useSearchParams } from 'react-router'; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +const ProtectedRoute: React.FC = ({ children }) => { + const location = useLocation(); + const [searchParams] = useSearchParams(); + const code = searchParams.get('code'); + const [isAuthenticated, setIsAuthenticated] = useState(null); // 인증 상태 + // const [userData, setUserData] = useState(null); // 백엔드에서 받은 사용자 데이터 + + useEffect(() => { + const queryParams = new URLSearchParams(location.search); + const code = queryParams.get('code'); // URL에서 카카오 인증 코드 가져오기 + if (code) { + localStorage.setItem('authToken', code); // 로컬 스토리지에 'code' 저장 + console.log('Code saved to localStorage:', code); + setIsAuthenticated(true); + } else { + setIsAuthenticated(false); + } + // const fetchAuthToken = async () => { + // if (code) { + // try { + // // 인증 코드를 백엔드로 보내기 + // const response = await fetch('http://localhost:3000/auth/kakao', { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ code }), + // }); + + // if (response.ok) { + // const data = await response.json(); + // localStorage.setItem('authToken', data.token); // 받은 토큰 저장 + // setUserData(data.user); // 사용자 데이터 저장 + // setIsAuthenticated(true); // 인증 상태 업데이트 + // } else { + // console.error('Failed to authenticate'); + // setIsAuthenticated(false); + // } + // } catch (error) { + // console.error('Error during authentication:', error); + // setIsAuthenticated(false); + // } + // } else { + // // 인증 코드가 없으면 로그인 페이지로 이동 + // setIsAuthenticated(false); + // } + // }; + + // fetchAuthToken(); + }, [location.search]); + + if (isAuthenticated === null) { + return

Loading...

; // 인증 상태 확인 중 + } + + if (!isAuthenticated) { + return ; // 인증되지 않았으면 로그인 페이지로 이동 + } + + // 인증이 완료되었으면 자식 컴포넌트 렌더링 (userData를 Context로 전달 가능) + return <>{children}; +}; + +export default ProtectedRoute; diff --git a/newlearn/src/types/authobj.ts b/newlearn/src/types/authobj.ts new file mode 100644 index 0000000..1994495 --- /dev/null +++ b/newlearn/src/types/authobj.ts @@ -0,0 +1,8 @@ +interface AuthResponse { + access_token: string; + expires_in: number; + refresh_token: string; + refresh_token_expires_in: number; + scope: string; + token_type: string; +} diff --git a/newlearn/src/types/kakao.d.ts b/newlearn/src/types/kakao.d.ts new file mode 100644 index 0000000..7e12e69 --- /dev/null +++ b/newlearn/src/types/kakao.d.ts @@ -0,0 +1,7 @@ +declare global { + interface Window { + Kakao: any; + } +} + +export {}; diff --git a/newlearn/src/types/news.ts b/newlearn/src/types/news.ts index 386a35b..2039a98 100644 --- a/newlearn/src/types/news.ts +++ b/newlearn/src/types/news.ts @@ -10,18 +10,14 @@ export type NewsData = { newsList: news[]; }; -export interface Article { - newsId: number; - thumbnail: string; - title: string; - publishedDate: string; - source: string; -} +export type NewsDetail = { + [K in keyof news]: news[K]; // 기존 타입 복사 +} & { + content: string; // 새로운 프로퍼티 추가 +}; -export interface ApiResponse { +export interface ApiResponse { status: string; message: string; - data: { - articles: Article[]; - }; + data: T; }