-
-
- Docs ->
-
-
- Find in-depth information about Next.js features and API.
-
-
+export default function Home({
+ items: initialItems,
+}: {
+ items: ResponseSimpleItems;
+}) {
+ const [items, setItems] = useState(initialItems);
+ const [disabled, setDisabled] = useState(false);
+ const searchRef = useRef
(null);
-
-
- Learn ->
-
-
- Learn about Next.js in an interactive course with quizzes!
-
-
+ const handleSubmit = async (e: FormEvent) => {
+ e.preventDefault();
+ if (searchRef.current?.value) {
+ try {
+ setDisabled(true);
+ const item = await postItem({ name: searchRef.current.value });
+ setItems((prev) => [...prev, item]);
+ searchRef.current.value = "";
+ } finally {
+ setDisabled(false);
+ }
+ }
+ };
-
-
- Templates ->
-
-
- Discover and deploy boilerplate example Next.js projects.
-
-
+ const changeIsCompleted = async (item: SimpleItem) => {
+ await patchItem(item.id, { isCompleted: !item.isCompleted });
+ setItems((prev) =>
+ prev.map((p) => {
+ if (p.id !== item.id) return p;
+ return { ...p, isCompleted: !p.isCompleted };
+ })
+ );
+ };
-
-
- Deploy ->
-
-
- Instantly deploy your Next.js site to a shareable URL
- with Vercel.
-
-
+ return (
+
+
+
+
+
+
+
+ {items.filter((item) => !item.isCompleted).length ? (
+ items
+ .filter((item) => !item.isCompleted)
+ .map((item) => (
+ {
+ e.currentTarget.disabled = true;
+ try {
+ await changeIsCompleted(item);
+ } catch {
+ e.currentTarget.disabled = false;
+ }
+ }}
+ >
+ {item.name}
+
+ ))
+ ) : (
+
+
+ 할 일이 없어요.
+
+ TODO를 새롭게 추가해주세요!
+
+ )}
+
+
+
+ {items.filter((item) => item.isCompleted).length ? (
+ items
+ .filter((item) => item.isCompleted)
+ .map((item) => (
+ {
+ e.currentTarget.disabled = true;
+ try {
+ await changeIsCompleted(item);
+ } catch {
+ e.currentTarget.disabled = false;
+ }
+ }}
+ >
+ {item.name}
+
+ ))
+ ) : (
+
+
+ 아직 다 한 일이 없어요.
+
+ 해야 할 일을 체크해보세요!
+
+ )}
+
- >
- )
+
+ );
}
diff --git a/public/favicon.ico b/public/favicon.ico
index 718d6fea..08ec48cc 100644
Binary files a/public/favicon.ico and b/public/favicon.ico differ
diff --git a/public/next.svg b/public/next.svg
deleted file mode 100644
index 5174b28c..00000000
--- a/public/next.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/public/vercel.svg b/public/vercel.svg
deleted file mode 100644
index d2f84222..00000000
--- a/public/vercel.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/styles/Home.module.css b/styles/Home.module.css
deleted file mode 100644
index 6676d2c6..00000000
--- a/styles/Home.module.css
+++ /dev/null
@@ -1,229 +0,0 @@
-.main {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: center;
- padding: 6rem;
- min-height: 100vh;
-}
-
-.description {
- display: inherit;
- justify-content: inherit;
- align-items: inherit;
- font-size: 0.85rem;
- max-width: var(--max-width);
- width: 100%;
- z-index: 2;
- font-family: var(--font-mono);
-}
-
-.description a {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 0.5rem;
-}
-
-.description p {
- position: relative;
- margin: 0;
- padding: 1rem;
- background-color: rgba(var(--callout-rgb), 0.5);
- border: 1px solid rgba(var(--callout-border-rgb), 0.3);
- border-radius: var(--border-radius);
-}
-
-.code {
- font-weight: 700;
- font-family: var(--font-mono);
-}
-
-.grid {
- display: grid;
- grid-template-columns: repeat(4, minmax(25%, auto));
- max-width: 100%;
- width: var(--max-width);
-}
-
-.card {
- padding: 1rem 1.2rem;
- border-radius: var(--border-radius);
- background: rgba(var(--card-rgb), 0);
- border: 1px solid rgba(var(--card-border-rgb), 0);
- transition: background 200ms, border 200ms;
-}
-
-.card span {
- display: inline-block;
- transition: transform 200ms;
-}
-
-.card h2 {
- font-weight: 600;
- margin-bottom: 0.7rem;
-}
-
-.card p {
- margin: 0;
- opacity: 0.6;
- font-size: 0.9rem;
- line-height: 1.5;
- max-width: 30ch;
-}
-
-.center {
- display: flex;
- justify-content: center;
- align-items: center;
- position: relative;
- padding: 4rem 0;
-}
-
-.center::before {
- background: var(--secondary-glow);
- border-radius: 50%;
- width: 480px;
- height: 360px;
- margin-left: -400px;
-}
-
-.center::after {
- background: var(--primary-glow);
- width: 240px;
- height: 180px;
- z-index: -1;
-}
-
-.center::before,
-.center::after {
- content: '';
- left: 50%;
- position: absolute;
- filter: blur(45px);
- transform: translateZ(0);
-}
-
-.logo {
- position: relative;
-}
-/* Enable hover only on non-touch devices */
-@media (hover: hover) and (pointer: fine) {
- .card:hover {
- background: rgba(var(--card-rgb), 0.1);
- border: 1px solid rgba(var(--card-border-rgb), 0.15);
- }
-
- .card:hover span {
- transform: translateX(4px);
- }
-}
-
-@media (prefers-reduced-motion) {
- .card:hover span {
- transform: none;
- }
-}
-
-/* Mobile */
-@media (max-width: 700px) {
- .content {
- padding: 4rem;
- }
-
- .grid {
- grid-template-columns: 1fr;
- margin-bottom: 120px;
- max-width: 320px;
- text-align: center;
- }
-
- .card {
- padding: 1rem 2.5rem;
- }
-
- .card h2 {
- margin-bottom: 0.5rem;
- }
-
- .center {
- padding: 8rem 0 6rem;
- }
-
- .center::before {
- transform: none;
- height: 300px;
- }
-
- .description {
- font-size: 0.8rem;
- }
-
- .description a {
- padding: 1rem;
- }
-
- .description p,
- .description div {
- display: flex;
- justify-content: center;
- position: fixed;
- width: 100%;
- }
-
- .description p {
- align-items: center;
- inset: 0 0 auto;
- padding: 2rem 1rem 1.4rem;
- border-radius: 0;
- border: none;
- border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
- background: linear-gradient(
- to bottom,
- rgba(var(--background-start-rgb), 1),
- rgba(var(--callout-rgb), 0.5)
- );
- background-clip: padding-box;
- backdrop-filter: blur(24px);
- }
-
- .description div {
- align-items: flex-end;
- pointer-events: none;
- inset: auto 0 0;
- padding: 2rem;
- height: 200px;
- background: linear-gradient(
- to bottom,
- transparent 0%,
- rgb(var(--background-end-rgb)) 40%
- );
- z-index: 1;
- }
-}
-
-/* Tablet and Smaller Desktop */
-@media (min-width: 701px) and (max-width: 1120px) {
- .grid {
- grid-template-columns: repeat(2, 50%);
- }
-}
-
-@media (prefers-color-scheme: dark) {
- .vercelLogo {
- filter: invert(1);
- }
-
- .logo {
- filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
- }
-}
-
-@keyframes rotate {
- from {
- transform: rotate(360deg);
- }
- to {
- transform: rotate(0deg);
- }
-}
diff --git a/styles/global.css b/styles/global.css
new file mode 100644
index 00000000..2a356f11
--- /dev/null
+++ b/styles/global.css
@@ -0,0 +1,20 @@
+@import url("https://hangeul.pstatic.net/hangeul_static/css/nanum-square.css");
+
+html {
+ font-family: "NanumSquare";
+}
+
+:root {
+ --slate900: #0f172a;
+ --slate800: #1e293b;
+ --slate500: #64748b;
+ --slate400: #94a3b8;
+ --slate300: #cbd5e1;
+ --slate200: #e2e8f0;
+ --slate100: #f1f5f9;
+ --violet600: #7c3aed;
+ --violet100: #ede9fe;
+ --rose500: #f43f5e;
+ --lime300: #bef264;
+ --amber800: #92400e;
+}
diff --git a/styles/globals.css b/styles/globals.css
deleted file mode 100644
index d4f491e1..00000000
--- a/styles/globals.css
+++ /dev/null
@@ -1,107 +0,0 @@
-:root {
- --max-width: 1100px;
- --border-radius: 12px;
- --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono',
- 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro',
- 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace;
-
- --foreground-rgb: 0, 0, 0;
- --background-start-rgb: 214, 219, 220;
- --background-end-rgb: 255, 255, 255;
-
- --primary-glow: conic-gradient(
- from 180deg at 50% 50%,
- #16abff33 0deg,
- #0885ff33 55deg,
- #54d6ff33 120deg,
- #0071ff33 160deg,
- transparent 360deg
- );
- --secondary-glow: radial-gradient(
- rgba(255, 255, 255, 1),
- rgba(255, 255, 255, 0)
- );
-
- --tile-start-rgb: 239, 245, 249;
- --tile-end-rgb: 228, 232, 233;
- --tile-border: conic-gradient(
- #00000080,
- #00000040,
- #00000030,
- #00000020,
- #00000010,
- #00000010,
- #00000080
- );
-
- --callout-rgb: 238, 240, 241;
- --callout-border-rgb: 172, 175, 176;
- --card-rgb: 180, 185, 188;
- --card-border-rgb: 131, 134, 135;
-}
-
-@media (prefers-color-scheme: dark) {
- :root {
- --foreground-rgb: 255, 255, 255;
- --background-start-rgb: 0, 0, 0;
- --background-end-rgb: 0, 0, 0;
-
- --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0));
- --secondary-glow: linear-gradient(
- to bottom right,
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0),
- rgba(1, 65, 255, 0.3)
- );
-
- --tile-start-rgb: 2, 13, 46;
- --tile-end-rgb: 2, 5, 19;
- --tile-border: conic-gradient(
- #ffffff80,
- #ffffff40,
- #ffffff30,
- #ffffff20,
- #ffffff10,
- #ffffff10,
- #ffffff80
- );
-
- --callout-rgb: 20, 20, 20;
- --callout-border-rgb: 108, 108, 108;
- --card-rgb: 100, 100, 100;
- --card-border-rgb: 200, 200, 200;
- }
-}
-
-* {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
-}
-
-html,
-body {
- max-width: 100vw;
- overflow-x: hidden;
-}
-
-body {
- color: rgb(var(--foreground-rgb));
- background: linear-gradient(
- to bottom,
- transparent,
- rgb(var(--background-end-rgb))
- )
- rgb(var(--background-start-rgb));
-}
-
-a {
- color: inherit;
- text-decoration: none;
-}
-
-@media (prefers-color-scheme: dark) {
- html {
- color-scheme: dark;
- }
-}
diff --git a/styles/index.module.css b/styles/index.module.css
new file mode 100644
index 00000000..6b8b2edc
--- /dev/null
+++ b/styles/index.module.css
@@ -0,0 +1,44 @@
+.home {
+ main {
+ display: flex;
+ flex-direction: column;
+ gap: 40px;
+ max-width: 1200px;
+ margin: 24px auto;
+ }
+
+ main > form {
+ display: flex;
+ gap: 16px;
+ width: 100%;
+ }
+
+ main > form .search {
+ flex: 1;
+ }
+
+ main > div {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 24px;
+ }
+
+ main > div section {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ }
+
+ main > div section .empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 24px;
+ margin: 48px auto;
+ color: var(--slate400);
+ line-height: 18px;
+ font-size: 16px;
+ font-weight: 700;
+ text-align: center;
+ }
+}
diff --git a/styles/reset.css b/styles/reset.css
new file mode 100644
index 00000000..e36321d0
--- /dev/null
+++ b/styles/reset.css
@@ -0,0 +1,134 @@
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ font-family: inherit;
+}
+html,
+body,
+div,
+span,
+applet,
+object,
+iframe,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+a,
+abbr,
+acronym,
+address,
+big,
+cite,
+code,
+del,
+dfn,
+em,
+img,
+ins,
+kbd,
+q,
+s,
+samp,
+small,
+strike,
+strong,
+sub,
+sup,
+tt,
+var,
+b,
+u,
+i,
+center,
+dl,
+dt,
+dd,
+ol,
+ul,
+li,
+button,
+fieldset,
+form,
+label,
+legend,
+table,
+caption,
+tbody,
+tfoot,
+thead,
+tr,
+th,
+td,
+article,
+aside,
+canvas,
+details,
+embed,
+figure,
+figcaption,
+footer,
+header,
+hgroup,
+menu,
+nav,
+output,
+ruby,
+section,
+summary,
+time,
+mark,
+audio,
+video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+menu,
+nav,
+section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol,
+ul {
+ list-style: none;
+}
+blockquote,
+q {
+ quotes: none;
+}
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+ content: "";
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+button {
+ cursor: pointer;
+ background: none;
+ color: initial;
+}