From e3ba7d8946a837d6ff7f318fbde94b6cffa13b0f Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 17 Mar 2025 15:13:47 +0900 Subject: [PATCH 01/54] =?UTF-8?q?feat=20:=20packages=20=EC=84=B8=ED=8C=85.?= =?UTF-8?q?=20=EB=8F=99=EC=9E=91=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/find/page.tsx | 21 + apps/web/app/layout.tsx | 25 +- apps/web/app/page.tsx | 21 +- apps/web/app/query.tsx | 11 + apps/web/package.json | 6 + apps/web/tsconfig.json | 1 + package.json | 12 +- packages/api/package.json | 4 +- packages/api/tsconfig.json | 7 +- packages/query/package.json | 27 + packages/query/src/index.ts | 1 + packages/query/src/types.ts | 8 + packages/query/src/useSong.ts | 27 + packages/query/tsconfig.json | 19 + pnpm-lock.yaml | 2319 ++++++++++----------------------- turbo.json | 3 +- 16 files changed, 869 insertions(+), 1643 deletions(-) create mode 100644 apps/web/app/find/page.tsx create mode 100644 apps/web/app/query.tsx create mode 100644 packages/query/package.json create mode 100644 packages/query/src/index.ts create mode 100644 packages/query/src/types.ts create mode 100644 packages/query/src/useSong.ts create mode 100644 packages/query/tsconfig.json diff --git a/apps/web/app/find/page.tsx b/apps/web/app/find/page.tsx new file mode 100644 index 0000000..1ce90b9 --- /dev/null +++ b/apps/web/app/find/page.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { getComposer } from '@repo/api'; +import { useState } from 'react'; + +export default function Home() { + const [search, setSearch] = useState(''); + + const handleSearch = (e: React.ChangeEvent) => { + setSearch(e.target.value); + }; + + return ( +
+ +
+

fotter

+
+
+ ); +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 42fc323..b54140f 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,20 +1,12 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; +import type { Metadata } from 'next'; +import QueryProvider from './query'; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); +// import { Geist, Geist_Mono } from "next/font/google"; +// import "./globals.css"; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Singcode', + description: 'Singcode', }; export default function RootLayout({ @@ -24,8 +16,9 @@ export default function RootLayout({ }>) { return ( - - {children} + {/* */} + + {children} ); diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 3948e16..4860714 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,23 +1,18 @@ 'use client'; -import styles from './page.module.css'; +// import styles from './page.module.css'; -import { getComposer } from '@repo/api'; -import { useEffect } from 'react'; +import { useSong } from '@repo/query'; export default function Home() { - const testAPI = async () => { - const response = await getComposer({ composer: '아이유' }); - console.log(response); - }; - useEffect(() => { - testAPI(); - }, []); + const { data } = useSong({ title: '불나방' }); + console.log(data); + return ( -
-
+
+

Hello World

-
+

fotter

diff --git a/apps/web/app/query.tsx b/apps/web/app/query.tsx new file mode 100644 index 0000000..f38e2f9 --- /dev/null +++ b/apps/web/app/query.tsx @@ -0,0 +1,11 @@ +'use client'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useState } from 'react'; + +const QueryProvider = ({ children }: { children: React.ReactNode }) => { + const [queryClient] = useState(() => new QueryClient()); + + return {children}; +}; + +export default QueryProvider; diff --git a/apps/web/package.json b/apps/web/package.json index ef9f91c..404c34a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,6 +11,9 @@ }, "dependencies": { "@repo/api": "workspace:*", + "@repo/query": "workspace:*", + "@tanstack/react-query": "^5.68.0", + "@tanstack/react-query-devtools": "^5.68.0", "axios": "^1.5.0", "next": "15.2.2", "react": "^19.0.0", @@ -24,5 +27,8 @@ "eslint": "^9", "eslint-config-next": "15.2.2", "typescript": "^5" + }, + "peerDependencies": { + "react": "^19.0.0" } } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index c133409..8e68406 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -13,6 +13,7 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, + "forceConsistentCasingInFileNames": true, "plugins": [ { "name": "next" diff --git a/package.json b/package.json index f6e3cca..e7bfaa3 100644 --- a/package.json +++ b/package.json @@ -8,16 +8,16 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "check-types": "turbo run check-types" }, - "devDependencies": { - "prettier": "^3.5.3", - "turbo": "^2.4.4", - "typescript": "5.8.2" - }, "packageManager": "pnpm@9.0.0", "engines": { "node": ">=18" }, "dependencies": { - "tamagui": "^1.125.23" + "react": "^19.0.0" + }, + "devDependencies": { + "prettier": "^3.5.3", + "turbo": "^2.4.4", + "typescript": "5.8.2" } } diff --git a/packages/api/package.json b/packages/api/package.json index aa14073..b79f0e7 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -8,12 +8,14 @@ }, "type": "module", "scripts": { - "test": "node --loader ts-node/esm" + "test": "node --loader ts-node/esm", + "build": "tsup src/index.ts --format esm,cjs --dts" }, "dependencies": { "axios": "^1.5.0" }, "devDependencies": { + "tsup": "^8.4.0", "@types/node": "^22.13.10", "ts-node": "^10.9.2", "typescript": "^5.8.2" diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 26456b7..a73bba3 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -9,8 +9,11 @@ "strict": true, "forceConsistentCasingInFileNames": true, "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/types", // 디버깅 편의를 위해 추가 + "declarationMap": true // 디버깅 편의를 위해 추가 }, "include": ["**/*.ts"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "dist"] } diff --git a/packages/query/package.json b/packages/query/package.json new file mode 100644 index 0000000..009cbaf --- /dev/null +++ b/packages/query/package.json @@ -0,0 +1,27 @@ +{ + "name": "@repo/query", + "version": "1.0.0", + "description": "", + "main": "./src/index.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "type": "module", + "scripts": { + "build": "tsup src/index.ts --format esm,cjs --dts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@repo/api": "workspace:*", + "@tanstack/react-query": "^5.68.0" + }, + "devDependencies": { + "tsup": "^8.4.0", + "typescript": "^5.8.2" + } +} diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts new file mode 100644 index 0000000..e2c8c88 --- /dev/null +++ b/packages/query/src/index.ts @@ -0,0 +1 @@ +export { default as useSong } from './useSong'; diff --git a/packages/query/src/types.ts b/packages/query/src/types.ts new file mode 100644 index 0000000..462075c --- /dev/null +++ b/packages/query/src/types.ts @@ -0,0 +1,8 @@ +import { ResponseType } from '@repo/api/src/types'; + +export interface UseQueryReturn { + data: ResponseType | null | undefined; + isLoading: boolean; + isError: boolean; + error: Error | null; +} diff --git a/packages/query/src/useSong.ts b/packages/query/src/useSong.ts new file mode 100644 index 0000000..760baf3 --- /dev/null +++ b/packages/query/src/useSong.ts @@ -0,0 +1,27 @@ +import { useQuery } from '@tanstack/react-query'; +import { getSong } from '@repo/api'; +import { Brand } from '@repo/api/src/types'; +import { UseQueryReturn } from './types'; + +interface GetSongProps { + title: string; + brand?: Brand; +} + +const useSong = (props: GetSongProps): UseQueryReturn => { + const { title, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['song', { title, brand }], + queryFn: () => getSong(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useSong; diff --git a/packages/query/tsconfig.json b/packages/query/tsconfig.json new file mode 100644 index 0000000..8b9089c --- /dev/null +++ b/packages/query/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "lib": ["ESNext", "esnext.asynciterable"], + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "declarationDir": "./dist/types", // 디버깅 편의를 위해 추가 + "declarationMap": true // 디버깅 편의를 위해 추가 + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fe74611..dda29cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - tamagui: - specifier: ^1.125.23 - version: 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) + react: + specifier: ^19.0.0 + version: 19.0.0 devDependencies: prettier: specifier: ^3.5.3 @@ -127,12 +127,21 @@ importers: '@repo/api': specifier: workspace:* version: link:../../packages/api + '@repo/query': + specifier: workspace:* + version: link:../../packages/query + '@tanstack/react-query': + specifier: ^5.68.0 + version: 5.68.0(react@19.0.0) + '@tanstack/react-query-devtools': + specifier: ^5.68.0 + version: 5.68.0(@tanstack/react-query@5.68.0(react@19.0.0))(react@19.0.0) axios: specifier: ^1.5.0 version: 1.8.3 next: specifier: 15.2.2 - version: 15.2.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: specifier: ^19.0.0 version: 19.0.0 @@ -174,6 +183,9 @@ importers: ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.13.10)(typescript@5.8.2) + tsup: + specifier: ^8.4.0 + version: 8.4.0(postcss@8.4.49)(typescript@5.8.2) typescript: specifier: ^5.8.2 version: 5.8.2 @@ -214,6 +226,22 @@ importers: specifier: ^8.26.0 version: 8.26.0(eslint@9.22.0)(typescript@5.8.2) + packages/query: + dependencies: + '@repo/api': + specifier: workspace:* + version: link:../api + '@tanstack/react-query': + specifier: ^5.68.0 + version: 5.68.0(react@19.0.0) + devDependencies: + tsup: + specifier: ^8.4.0 + version: 8.4.0(postcss@8.4.49)(typescript@5.8.2) + typescript: + specifier: ^5.8.2 + version: 5.8.2 + packages/typescript-config: {} packages/ui: @@ -993,6 +1021,156 @@ packages: '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} + '@esbuild/aix-ppc64@0.25.1': + resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.1': + resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.1': + resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.1': + resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.1': + resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.1': + resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.1': + resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.1': + resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.1': + resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.1': + resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.1': + resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.1': + resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.1': + resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.1': + resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.1': + resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.1': + resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.1': + resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.1': + resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.1': + resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.1': + resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.1': + resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.1': + resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.1': + resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.1': + resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.1': + resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1112,33 +1290,6 @@ packages: resolution: {integrity: sha512-ReZxZ8pdnoI3tP/dNnJdnmAk7uLT4FjsKDGW7YeDdvdOMz2XCQSmSCM9IWlrXuWtMF9zeSB6WJtEhCQ41gQOfw==} hasBin: true - '@floating-ui/core@1.6.9': - resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} - - '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} - - '@floating-ui/react-dom@2.1.2': - resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/react-native@0.10.7': - resolution: {integrity: sha512-deSecLPrdfl8RL1yyNJlbgqDDZFPuhBtJhY2aTnOZOoJWaal2vVOad9EBVIa0QV/YordgTyFPgDI8oLfyLZuZA==} - peerDependencies: - react: '>=16.8.0' - react-native: '>=0.64.0' - - '@floating-ui/react@0.27.5': - resolution: {integrity: sha512-BX3jKxo39Ba05pflcQmqPPwc0qdNsdNi/eweAFtoIdrJWNen2sVEWMEac3i6jU55Qfx+lOcdMNKYn2CtWmlnOQ==} - peerDependencies: - react: '>=17.0.0' - react-dom: '>=17.0.0' - - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -1516,9 +1667,6 @@ packages: peerDependencies: '@babel/core': '*' - '@react-native/normalize-color@2.1.0': - resolution: {integrity: sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==} - '@react-native/normalize-colors@0.74.89': resolution: {integrity: sha512-qoMMXddVKVhZ8PA1AbUCk83trpd6N+1nF2A6k1i6LsQObyS92fELuk8kU/lQs6M7BsMHwqyLCpQJ1uFgNvIQXg==} @@ -1580,6 +1728,101 @@ packages: '@react-navigation/routers@7.2.0': resolution: {integrity: sha512-lMyib39H4a//u+eiyt162U6TwCfI8zJbjl9ovjKtDddQ4/Vf7b8/OhyimnJH09N2CBfm4pv0gCV6Q0WnZcfaJg==} + '@rollup/rollup-android-arm-eabi@4.35.0': + resolution: {integrity: sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.35.0': + resolution: {integrity: sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.35.0': + resolution: {integrity: sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.35.0': + resolution: {integrity: sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.35.0': + resolution: {integrity: sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.35.0': + resolution: {integrity: sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + resolution: {integrity: sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + resolution: {integrity: sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.35.0': + resolution: {integrity: sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.35.0': + resolution: {integrity: sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + resolution: {integrity: sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + resolution: {integrity: sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + resolution: {integrity: sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.35.0': + resolution: {integrity: sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.35.0': + resolution: {integrity: sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.35.0': + resolution: {integrity: sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.35.0': + resolution: {integrity: sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.35.0': + resolution: {integrity: sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.35.0': + resolution: {integrity: sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==} + cpu: [x64] + os: [win32] + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} @@ -1604,463 +1847,92 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tamagui/accordion@1.125.23': - resolution: {integrity: sha512-jJWjiFmseqL/O72KBu51suzO2+Fk45fvXHY7JQyJW4l+lNukak+Y6H0c53ynqL2n3rImP8mGQHNyrOOTi7jpaA==} - peerDependencies: - react: '*' + '@tanstack/query-core@5.68.0': + resolution: {integrity: sha512-r8rFYYo8/sY/LNaOqX84h12w7EQev4abFXDWy4UoDVUJzJ5d9Fbmb8ayTi7ScG+V0ap44SF3vNs/45mkzDGyGw==} - '@tamagui/adapt@1.125.23': - resolution: {integrity: sha512-zTHwyh0bWzX1rFTcJsdI0gTzPIAYpqzE/HikHk+lT+cYqRFIK9gtj6Aw3tk//ZWd8iHSsPJUbf63mgBxvcR7sA==} + '@tanstack/query-devtools@5.67.2': + resolution: {integrity: sha512-O4QXFFd7xqp6EX7sdvc9tsVO8nm4lpWBqwpgjpVLW5g7IeOY6VnS/xvs/YzbRhBVkKTMaJMOUGU7NhSX+YGoNg==} - '@tamagui/alert-dialog@1.125.23': - resolution: {integrity: sha512-tNk9KW9k5zgX/xl0rSpCZj9px3t5kd/d0vM3jPBObDd4s4y9A7s340t8SPXLag4vaChuZVEds6Wii6kGhYtwVQ==} + '@tanstack/react-query-devtools@5.68.0': + resolution: {integrity: sha512-h9ArHkfa7SD5eGnJ9h+9M5uYWBdeVeY+WalrtGLCAtJJvHx6/RrtbbzxeoEQbPyx3f0kPcwJ58DGQ+7CBXelpg==} peerDependencies: - react: '*' + '@tanstack/react-query': ^5.68.0 + react: ^18 || ^19 - '@tamagui/animate-presence@1.125.23': - resolution: {integrity: sha512-N4P9wOeJd0+bO9TYIaTE3MCEq2dbujku+FovM1J4cGmx3WySHvvFIjPMGshLccfgWtCIPkIHDHgZn4MhBFTrJQ==} - - '@tamagui/animate@1.125.23': - resolution: {integrity: sha512-1jfmH3a9fp3tNovIg1lr6LeNoyx816C0sZIA9SCtROQ9RVmZnDahi4y5TVimhSqzwWvHxgRyETc1kE9SFRk4sA==} - - '@tamagui/animations-react-native@1.125.23': - resolution: {integrity: sha512-NdoqAZr5g+RXqlnxkTBQ1oxKzM0e/+b9/01gT+38D0bSqawB1tt7xz6PLYG5Sua9swT31HfevX6U3k09wJWabQ==} + '@tanstack/react-query@5.68.0': + resolution: {integrity: sha512-mMOdGDKlwTP/WV72QqSNf4PAMeoBp/DqBHQ222wBfb51Looi8QUqnCnb9O98ZgvNISmy6fzxRGBJdZ+9IBvX2Q==} peerDependencies: - react: '*' + react: ^18 || ^19 - '@tamagui/aria-hidden@1.125.23': - resolution: {integrity: sha512-WUOROYNpZhRE6x1XYV0sw9oPYh7kFdv8BYvOKGLOgvc4SsMhmmhKKn1qP7MEH0/DKrvEzcfIRRx0ovFy8vlq4w==} - peerDependencies: - react: '*' + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} - '@tamagui/avatar@1.125.23': - resolution: {integrity: sha512-ccpzRWIgNzfxt2eRSJ7UY6bo+L3p4tD96R6FkZM433O9w9AaocxxkT+RUCWL0dp2zefnfphZQNOsr3c9kZTJPQ==} - peerDependencies: - react: '*' + '@tootallnate/quickjs-emscripten@0.23.0': + resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@tamagui/button@1.125.23': - resolution: {integrity: sha512-10YqFz+R84D+0zH2hY9296z1A8+QPvoUXMm7Hme0cSjK3kLlBOzEjY3jqZ+4vKlKzu2tCtvg3X4H/aSzJNdKVQ==} - peerDependencies: - react: '*' + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - '@tamagui/card@1.125.23': - resolution: {integrity: sha512-gU4cSphPAaSSVs4YEZcUtVQP/eQ8Gfos8GpMvOVeiVy78ioEbe56PwrpxrtjiZ22z5Rn9CKF/UN8ed+hflj7jg==} - peerDependencies: - react: '*' + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - '@tamagui/checkbox-headless@1.125.23': - resolution: {integrity: sha512-kH5b3+Nes1Fbh+lg+SUGVh0tQ1m3CycBUTxlVjCDlKil+Xk6gb3eqtFnthgXCrl590S/KaQekTZZf0SiOi1zsA==} - peerDependencies: - react: '*' + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - '@tamagui/checkbox@1.125.23': - resolution: {integrity: sha512-egbNuVsmiVHnVRbqv5UVLRafaRcox94J9CVxXCmZR6k7/UzUvD6qUWUZdDC0DmQefudEeseWnCugJWHpZTuwPw==} - peerDependencies: - react: '*' + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tamagui/collapsible@1.125.23': - resolution: {integrity: sha512-j5CZHEAl+TuyQOAQi3BmWpEtvWkSHsJrizEhPcc5LfXntprq10gjBdydj8n/TSDlOLkBB/A1jh6JnJQAgMLdow==} - peerDependencies: - react: '*' + '@turbo/gen@2.4.4': + resolution: {integrity: sha512-02DR0UFngfVROq/L7j4VdkpT46Rnk5mm8i5AE985jV114SjuN57Y2oBjSK9Lzm6hlun/H0MQtdJBRybk4nRIAQ==} - '@tamagui/collection@1.125.23': - resolution: {integrity: sha512-zLHtxroqS/zDh3QreahqYqMPJpjRbQXd03CYyYmZigw/QZ/sk0nIP724sEIE3BjKgKVpA0EfdCGtxv9xjkfD+Q==} - peerDependencies: - react: '*' + '@turbo/workspaces@2.4.4': + resolution: {integrity: sha512-5CK3ZkVskSExf9YaXVMJ04F6Fb18HXKVZw8TMKVP7qOIvHGiBmT6YzWHqsQ3/PMNov2TzJ41ofRKX3zjR3G6Yg==} - '@tamagui/compose-refs@1.125.23': - resolution: {integrity: sha512-H4r0lQYmF7aIyWiPoxW7tDvLpk43NGj/rttZ93e4iCBfeSHLhSNFULlrXPMCQE1oDwFqdG7b0Yx9O93rviLtRg==} - peerDependencies: - react: '*' + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@tamagui/constants@1.125.23': - resolution: {integrity: sha512-r37NVwYwGjLaKnlxOVeecSALWq4o15Sc0MMFgvtRvM65Jg2T7wjo3r1BjzymWKhnMSDUiwmkQRPM76ly4Lfz5g==} - peerDependencies: - react: '*' + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - '@tamagui/core@1.125.23': - resolution: {integrity: sha512-lHI55xtxkfnMBiCJSxT8510fF0sulIuGmeXOm9ZhppNtCcrhnSXLAEFsVN2B+Ai+tjheIgLyhKCxWTrgOBCdhQ==} + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@tamagui/create-context@1.125.23': - resolution: {integrity: sha512-7LxRXPE3FSaiD4/q4PntfXxlVcNx8DL6nOPB7uwwTBoshsM8h9/AlhI0J1XE3lbZATCxsakZlZHbfwLIr/oV7g==} - peerDependencies: - react: '*' + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@tamagui/dialog@1.125.23': - resolution: {integrity: sha512-xVGNMgh6Cxnp3FZBdomgBiCOEzXXnTiXV8GSQHrPWsEwYHxk0jtKkUsa0cTeJfk17JTpIaddqpNw50Cc+hYYkw==} - peerDependencies: - react: '*' + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - '@tamagui/dismissable@1.125.23': - resolution: {integrity: sha512-DWWLHtrN0xOpU1MdHrtC/NGAOH7aq2AKG/+1CAOFNdoT06qaJgJkgA3Hw2Rmwyz5GjCNhaQ6zsNd0uOuzBsRfw==} - peerDependencies: - react: '*' + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@tamagui/elements@1.125.23': - resolution: {integrity: sha512-5cst7q1gnLshA1U+EbZ4PDLo8z0lHT1cwj93WSu/wUABGZDUlfdDeoXlRtQo3Dap8z36s2+rR/uXwU1GDmNJ3Q==} - peerDependencies: - react: '*' + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - '@tamagui/fake-react-native@1.125.23': - resolution: {integrity: sha512-3I391LBLo0Rw6rdhxKbh+p3B6hhZ6rkvWgKC/cjDESTy5+feIgJh+l4GxRm9riK7qL0FevAPUy5BZSPiJpqiAA==} + '@types/glob@7.2.0': + resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - '@tamagui/floating@1.125.23': - resolution: {integrity: sha512-4Z5KOGmceH/0ukHhyUyaX8WUHtMpq1pDTmuxUAcXXblvQIJ83UJyoCELRgkVAphHN4LsxHN1KWlES7iX3nCPJA==} - peerDependencies: - react: '*' + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@tamagui/focus-scope@1.125.23': - resolution: {integrity: sha512-GjGqaBSRxZB4+E1B/vkIg4hBy8XJtyv51AFOifYJulH8tptReDNauA483EcZSd2WFWPeik6DC2zk66nTPb3r+g==} - peerDependencies: - react: '*' + '@types/hammerjs@2.0.46': + resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} - '@tamagui/focusable@1.125.23': - resolution: {integrity: sha512-rd62D+a+FtGl3l+fUZymHbu0YXTE18RUtfz8i/O4fQSpo/haS8DHcCO6C9zKD4OtoyfmjBQWcK3dOQujYsc26w==} - peerDependencies: - react: '*' + '@types/inquirer@6.5.0': + resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} - '@tamagui/font-size@1.125.23': - resolution: {integrity: sha512-XlZ6jH7JRyUYbyeNvdNAsazFIWKZS1kB0DtaDRnfabMfTze04ZyYwQaXIgQOMAUCzif/wvhFYSkzBtnltE9f+g==} - peerDependencies: - react: '*' + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - '@tamagui/form@1.125.23': - resolution: {integrity: sha512-dPZvcRsHw97wrlenmuNKCj5hJhMSeIKL8o8z3dTMPEHbfsPOsbGhDGXEUfTRRFK4DlC/0eR7uJTMQL6UQ1FM+Q==} - peerDependencies: - react: '*' + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - '@tamagui/get-button-sized@1.125.23': - resolution: {integrity: sha512-fAj0LCOjEMr78+SI6U86+aJ9GYbyBBqRHklEZb2VSHbcHxN0kcHgu26dO8tLpTz+lT9HCUyVWcPI8SKgSurTgQ==} - peerDependencies: - react: '*' + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@tamagui/get-font-sized@1.125.23': - resolution: {integrity: sha512-SBgg6Wc9bIntyt/Ou3DnipOclNx9R1IpDhbgMqCouu4AGdIr2o0P2RL/bNXXHQfoNuWf9yocrtyKbc6RxcuwZQ==} - peerDependencies: - react: '*' - - '@tamagui/get-token@1.125.23': - resolution: {integrity: sha512-Aov4I7ZFPmJEdqIscZV3JJ49tidO0hVcuWgkIuSUKntcrbAweSIMuju0HBoXKoPnZ3UG4nokHs6yqkeZX0HLRg==} - peerDependencies: - react: '*' - - '@tamagui/group@1.125.23': - resolution: {integrity: sha512-TqpBP27yKYAq9X17Upwbw/P4gAISW95agu4Tt3WiFXw291gm/P4mWTV8MaNDwi9oajUjnQmHv+AqOydZkDoucg==} - peerDependencies: - react: '*' - - '@tamagui/helpers-tamagui@1.125.23': - resolution: {integrity: sha512-0u9ophyFv+7bVFr8FqnPlfW+zR1M8Q/Xu0YCR6pAIRbCn4yy9Qb6uGcPHtN3qLFnWIO5N+wSUHk1LVeytawcDg==} - peerDependencies: - react: '*' - - '@tamagui/helpers@1.125.23': - resolution: {integrity: sha512-Lw8IKpRfZC1/QZhx0Ck9E7rvR96kfNBUV2GYckqsRLOe8dei3cZYcYVWDTBjNx5GK0towmwJszfdjp+9yT/Qcw==} - - '@tamagui/image@1.125.23': - resolution: {integrity: sha512-aG72DH8GxImhiauBlB1eG+RldN91+8VFCrCRZzSiAPaEKEgrXa3VTvmNlkV3Lc1zvagvtBz1oNVNwCnCm9d6ig==} - peerDependencies: - react: '*' - - '@tamagui/label@1.125.23': - resolution: {integrity: sha512-B8HYoIaS/OZoU+5qQVb6mSVEPWqL/KsKMA0OhgtvrbabEy+LyZO6LVFW2ACMzNHDXgQRqpWcEu9bdXNPsjnoWg==} - peerDependencies: - react: '*' - react-native: '*' - - '@tamagui/linear-gradient@1.125.23': - resolution: {integrity: sha512-FYoSX7WERJvEy3bvHh81dFEDiEYfaA27vukaMPbQc8L+GGT9vohQxjrGkGRsdwSGhs8jRFqAIe7H4EXsE++ehg==} - peerDependencies: - react: '*' - - '@tamagui/list-item@1.125.23': - resolution: {integrity: sha512-1xY76RCqC553Ykcf1iOxGvvpGyybwPccsRw1Am3h57QRbapCdz8mQqRhe/jaQaK4Tzo2BHjYmWtARMKlI1FwZQ==} - peerDependencies: - react: '*' - - '@tamagui/normalize-css-color@1.125.23': - resolution: {integrity: sha512-bDn7+3uNNrPiljMlK1m4QcwNZ+q/5sI49XuqKB4brn1X30XIqn3J/I9lbOt1B+skAapos3Es2PEkI4RQc0AnaA==} - - '@tamagui/polyfill-dev@1.125.23': - resolution: {integrity: sha512-xm8VmbVXPpnxVM4kPMaqH9MEaTrSP3XiJFrWu1eYOqgxnlOSDBMyn/ZPDmQueisZfUqhG6IhBTIpE4G4j3v+RQ==} - - '@tamagui/popover@1.125.23': - resolution: {integrity: sha512-5n8PAzHfpoQO+UommLjD9yXemNY/Qt5SgI5ecr5zHvszIN9HHkINV5zQzs2ZYVfrbrag8gIotO3jAC0fjpKa6A==} - peerDependencies: - react: '*' - - '@tamagui/popper@1.125.23': - resolution: {integrity: sha512-9tzwXIzY0kmdSasLAsOtznCfVYkXMAuzI5MUezJlEFi0YmXV62Xi9l5c+VRHxKlESmJKrM2IJBb3gh2MNoaHzA==} - peerDependencies: - react: '*' - - '@tamagui/portal@1.125.23': - resolution: {integrity: sha512-ZFSEM7/k82vka29/1Ak6vc4TdZpptcfios0iO94ScrQoiUtqecXBzXZnkF6u1R9dTze/aoqtV024dmFLa/VZSg==} - - '@tamagui/progress@1.125.23': - resolution: {integrity: sha512-eSk9INMBp1tJDHV+e5o6xtezztqTT7kT28L0er2NZVNWdkSo6k+Uy6USR58qUWWeVkpfWO5xVGk8GigmTvW2mQ==} - peerDependencies: - react: '*' - - '@tamagui/radio-group@1.125.23': - resolution: {integrity: sha512-hSScfFxeUX+xandKdqg+ZxaYLg87sdquSIZBpHRDyPBNOwcBDMGM+xh4ttEH70/8hlQ4g5ksgNSNz+XitV2rxA==} - peerDependencies: - react: '*' - - '@tamagui/radio-headless@1.125.23': - resolution: {integrity: sha512-ftsA9mSB8Psbrm4OEZ4lgl1XDg/i7aeeftH15ngjocqyIuijyoiAkWmdUjpc/2qiON3GJYOTYMXkadxwwXEF9Q==} - peerDependencies: - react: '*' - - '@tamagui/react-native-media-driver@1.125.23': - resolution: {integrity: sha512-uzV3xBXwD64brbrZEJmSKOz+E5S77NPBdY9bYpcQzENe/GSO0euPyNIANwOy2zVQTT8i8A3yrHBae2s112Rirg==} - peerDependencies: - react-native: '*' - - '@tamagui/react-native-use-pressable@1.125.23': - resolution: {integrity: sha512-PmpsEcuhpf+2Xy9+6obLvtWsXudV7uBjhsqXM7jiaS7ELQIaWqtud+Izn+6Av7QLFPUqdFtoA0S0ZLU6mrUo5A==} - peerDependencies: - react: '*' - - '@tamagui/react-native-use-responder-events@1.125.23': - resolution: {integrity: sha512-R4ijteeeahDp7Gu9RH/nvTXvRimV73C+C4/EDK5h+NHb2oKIwlf9EKiqG6kUDYvGHNmKEL5ESVb1zutOQRf6pQ==} - peerDependencies: - react: '*' - - '@tamagui/remove-scroll@1.125.23': - resolution: {integrity: sha512-FGoCFQjexEBhvzfiYD4DdEL44S1Yg0wmciObsdX/YSx60USszjQQyKu8VyRhFZiydko1CBSETPHaP7NyKuRcXg==} - peerDependencies: - react: '*' - - '@tamagui/roving-focus@1.125.23': - resolution: {integrity: sha512-hGj+Ki0cVu1JaQfloryCZ7yoG3cnrsAb5p0md/I0WjrdTeVLO+7qqiaXF/3iWPvRzxW719/cMIjDKNhgED1UwA==} - peerDependencies: - react: '*' - - '@tamagui/scroll-view@1.125.23': - resolution: {integrity: sha512-9OWosnhPAhS0aPJNfxoUcM0XA/jjRtTbbFeOXW9ZZPsnYohmZrVtA/xkghtH/hzUEj3dfAQqxim39BRT+fBRXw==} - peerDependencies: - react: '*' - - '@tamagui/select@1.125.23': - resolution: {integrity: sha512-GMp7q9lfV75GgHuPsl/NVSrjxbQ+MT2bk8ODtXJRt0KMiWFyD9flKMNeeRB0ZoXIpE/C9tmbJLP6KjU+v+Msqg==} - peerDependencies: - react: '*' - - '@tamagui/separator@1.125.23': - resolution: {integrity: sha512-KRvo0t+mcyY2QkNFhOxqDUyPmexFgjr00q32UkrUMoqJJY9lWCTva4iNZT/plIpNmL4xUgz4VShtvYmUR8Ec2Q==} - peerDependencies: - react: '*' - - '@tamagui/shapes@1.125.23': - resolution: {integrity: sha512-KvCgKlpbj0/Fz2PXNoodKyONOpjlLHvy8NcWUhUu/G1pjKIvzbWpztJhWJgRlR4XBIZCALQhwjxCT4/i/4XC7A==} - peerDependencies: - react: '*' - - '@tamagui/sheet@1.125.23': - resolution: {integrity: sha512-UaJL9XmZ4sturqvGO71AWqoDKFDSdnNRnXs2K43QpvSgd2JD4FrVxpNjHTYlB44hcFj1vffM+kF7ElT8kvPsUw==} - peerDependencies: - react: '*' - - '@tamagui/simple-hash@1.125.23': - resolution: {integrity: sha512-128MZTO0CcPDY+RC9Uleu22CRGAFcIyA4dMiMhpQDzE+pxcBXeqy3aVfMoiK7awo0BsQ5BahXci4ZgCRaGwuog==} - - '@tamagui/slider@1.125.23': - resolution: {integrity: sha512-bCGO6krlrMeFwwgyofSsJHoE2cSwIuR2mfUFB0Gm+yakC25v6Mj6MFMkyTFGASulBerDOutqFwoELbvCHrU9UA==} - peerDependencies: - react: '*' - - '@tamagui/stacks@1.125.23': - resolution: {integrity: sha512-aQiwfgucjpi0k1W3Mjdg91XmSqSGNjdWMY1a7bcmH3dvb8tRWF6mUlLuFmiJt0u90cqzfrwCMr/eUDHOvyQNnQ==} - peerDependencies: - react: '*' - - '@tamagui/start-transition@1.125.23': - resolution: {integrity: sha512-h3hAX9QthgIo4qPgzfwb+S9mu9MZLnCLWh1r4q1xReG9gEluiOwd9AZqIDXqcj31+Kd4z97QY3EY2+jHia50SQ==} - - '@tamagui/switch-headless@1.125.23': - resolution: {integrity: sha512-YvJqqYpKjdd9+4EdxAa0yALz9FEXx04LO8178+NLnzd87K94xrzWlujWo5JgOWQ0DeGsSMk8ue3y90LcS2aEeQ==} - peerDependencies: - react: '*' - - '@tamagui/switch@1.125.23': - resolution: {integrity: sha512-FpfduhmUBkNUq0P9Ova6wlVEyt5YzeoIynyfNHWa8nF6rYH3Yb1l191mKhrBVWbEiBIHBA4IhGeVnWd88MHX1w==} - peerDependencies: - react: '*' - - '@tamagui/tabs@1.125.23': - resolution: {integrity: sha512-jj0l+I2kNtwd5EiM32VYYOeOAFuTX4VjYE8k3WK8mLN72uaw8M3+LvVuLVINDhEiD6ORBI3HSpxSYPaVZvnH6Q==} - peerDependencies: - react: '*' - - '@tamagui/text@1.125.23': - resolution: {integrity: sha512-Arwzqd7HG7IfM6sqG9CBgZsds8rQ5aZDzvyE+1OEDV5cZGH0QP1UVt8L9uwiWcCd62SMdTcsDLycwiXxUl2sQQ==} - peerDependencies: - react: '*' - - '@tamagui/theme@1.125.23': - resolution: {integrity: sha512-EZWu6CmxFem4Npj7Xjc+8AeRKD+Su3S9PGiQJw1egxrS0VBraV/k4t/53kpkykEtBV58sr6oGdIx8J3+8yZcng==} - peerDependencies: - react: '*' - - '@tamagui/timer@1.125.23': - resolution: {integrity: sha512-qNxMLA7RyCsKsC8Uwcl03iQtNhvJ0Zedjxbyr3RsX8ylIC0dHcV5lybt/kcK4aAYIfrQ9MQe5gvOMCh5owW6ug==} - - '@tamagui/toggle-group@1.125.23': - resolution: {integrity: sha512-j08jAZhMi4QAs5vKKVUYZYEePsxB+Aj85yWXs8/4TkKCdp7o5mU26NLlXIO3U2cJKHT3RvC61VnyMXvjkgEljg==} - peerDependencies: - react: '*' - - '@tamagui/tooltip@1.125.23': - resolution: {integrity: sha512-fZENmXDNE0kHhs0jOA/Xk4D4CQeAhpg6xxUCOQsHQjBSjdiW40f5Pvc3ZzM4WuQZ7npiNdkb3YD40GjcE0yK5g==} - peerDependencies: - react: '*' - - '@tamagui/types@1.125.23': - resolution: {integrity: sha512-YyBCFm0D82LshHIpVG1Zt8QRFsQMeg96TJLx+O6tTpWgfrXJy7agIRCAD6ePnKoMGZnFSllMCzxzx+hFTOoTrw==} - - '@tamagui/use-callback-ref@1.125.23': - resolution: {integrity: sha512-3DR724QaNCyec8vs0VSdNNnJw0VHURBcReOyS+oeV6piu9E7w5q2qgdBga5fLf2qD0xr7uU5OEWksO/+B1xAzQ==} - - '@tamagui/use-constant@1.125.23': - resolution: {integrity: sha512-/OOC+YtL18r4slTqx5Xh4Vw8HrDcDVhcIZp06CeroFKUCl1lxMbUEfkNYFM3qom66sm6ueEO6F7oKPgRu71jPw==} - peerDependencies: - react: '*' - - '@tamagui/use-controllable-state@1.125.23': - resolution: {integrity: sha512-n4WPAQpDCVZ/JSNjMoPRaHZ8xJtjHKLuCvbNbHPQD9AaOQ1fVvEQwtj8BFLdBSJ4IUT+jAFEjNnvGhrDEXdXew==} - peerDependencies: - react: '*' - - '@tamagui/use-debounce@1.125.23': - resolution: {integrity: sha512-rbqcpMsfUif7NPqMcpBBr156DH0jyUfZa8/npjYMw/v7ePUMET0dpozJ+vqylL68ClG6qEiTAVHW8Beli9bGbg==} - peerDependencies: - react: '*' - - '@tamagui/use-did-finish-ssr@1.125.23': - resolution: {integrity: sha512-D3/PpzU7n7toxsZfLHEFbKwYsDrsnfgU4TBJXV0ryP61Ffj6o/wcDeWNo7+7wAYy+3+H2N6zBMRW1rkWp3TTwg==} - peerDependencies: - react: '*' - - '@tamagui/use-direction@1.125.23': - resolution: {integrity: sha512-vW0qnYJwnYsfSKOzfE5dz763+6H+++h7jxDBTvc9LIqIIs+RePn/Br48qt7rJdBQvrfD1+XXRzlD4064KDtTKg==} - peerDependencies: - react: '*' - - '@tamagui/use-escape-keydown@1.125.23': - resolution: {integrity: sha512-TjqRNvsqtCI8eR+CfJPFCGZhrl8T734vlQS6sn2Ta62jWsDcoBmbxPCKraA6CN6zyEJqzDbdlO3SC871gOO+qg==} - - '@tamagui/use-event@1.125.23': - resolution: {integrity: sha512-uyZMepvoXngmYxJ6j+HNUN9g6XC76Zz2WGOZZRkxlincqIVXo46y66IExV0VPidevd9BxBC66374wqueZm3hGw==} - peerDependencies: - react: '*' - - '@tamagui/use-force-update@1.125.23': - resolution: {integrity: sha512-o+v2vUAhTo9d1uS46Jf1WWhwLfV26KA9oxAlplbCsDXDgkayqgfB2sIG9ZxkNCed5t7cYjF33HstrFC6oscBvQ==} - peerDependencies: - react: '*' - - '@tamagui/use-keyboard-visible@1.125.23': - resolution: {integrity: sha512-MAnp5/uRsqorYpWo8oBGs1y14snrmC3nSQ2uaF63HcZomgPuV4nFMLN0J/TBMcWOcnqtKeFX9WR0GFTE17ZEvA==} - peerDependencies: - react: '*' - - '@tamagui/use-presence@1.125.23': - resolution: {integrity: sha512-WaDwU3n60fdWG5rJTkjbYPrfpO1/nhgTT5QWbq1eWVx443TbJPttfwx+yGiQq5hqbaDKBeRylipqnhcgriA7lQ==} - peerDependencies: - react: '*' - - '@tamagui/use-previous@1.125.23': - resolution: {integrity: sha512-Av4XXrbTHFbP3B6E/GjtWqlnCMEDD8uJvZBz7V9yOZBNfG4EpSL5fjnSoK8aH7mlPOCAbuGcGi6FuA1Sws7qUQ==} - - '@tamagui/use-window-dimensions@1.125.23': - resolution: {integrity: sha512-RXpU/vbBQ8iW/XKCdDIvdNfw+bdrpAIEG7V9wD03sMqh9cDfc2ffUUVmmkx+bhUBVzmpbpg9AEvm0R6ov8Q+ZQ==} - peerDependencies: - react: '*' - - '@tamagui/visually-hidden@1.125.23': - resolution: {integrity: sha512-WgzBl/o1eKo93OPkliFI3aHoQMHbeM/KuIJz/rRdha83PlJN6tJzkz+9TWuqyLGbcdbrAUa0YtMW+bt72gx59g==} - peerDependencies: - react: '*' - - '@tamagui/web@1.125.23': - resolution: {integrity: sha512-IWYyssSMjVafcFBqGqi+yzdo9e0XgYTCNRDrd2q5s1Io164q3a+BXU+pycboodhWq3c2SOTeZCEECgBP8KUEgQ==} - peerDependencies: - react: '*' - react-dom: '*' - - '@tamagui/z-index-stack@1.125.23': - resolution: {integrity: sha512-u+krshexbayeQyG3vX58RMwyexDOVofyLuh48E6RePEG4s9eaDz/v+uKwHcOz0ChT1+c7HHZ1X6saSGKYzcPhQ==} - - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@turbo/gen@2.4.4': - resolution: {integrity: sha512-02DR0UFngfVROq/L7j4VdkpT46Rnk5mm8i5AE985jV114SjuN57Y2oBjSK9Lzm6hlun/H0MQtdJBRybk4nRIAQ==} - - '@turbo/workspaces@2.4.4': - resolution: {integrity: sha512-5CK3ZkVskSExf9YaXVMJ04F6Fb18HXKVZw8TMKVP7qOIvHGiBmT6YzWHqsQ3/PMNov2TzJ41ofRKX3zjR3G6Yg==} - - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - - '@types/glob@7.2.0': - resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} - - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - - '@types/hammerjs@2.0.46': - resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} - - '@types/inquirer@6.5.0': - resolution: {integrity: sha512-rjaYQ9b9y/VFGOpqBEXRavc3jh0a+e6evAbI31tMda8VlPaSy0AZJfXsvmIe3wklc7W6C3zCSfleuMXR7NOyXw==} - - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@29.5.14': - resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} + '@types/jest@29.5.14': + resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} '@types/jsdom@20.0.1': resolution: {integrity: sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==} @@ -2360,10 +2232,6 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-hidden@1.2.4: - resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} - engines: {node: '>=10'} - aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -2583,6 +2451,12 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -2591,6 +2465,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + cacache@18.0.4: resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} engines: {node: ^16.14.0 || >=18.0.0} @@ -2666,6 +2544,10 @@ packages: charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -2801,6 +2683,10 @@ packages: resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} engines: {node: '>= 0.10.0'} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} + constant-case@2.0.0: resolution: {integrity: sha512-eS0N9WwmjTqrOmR3o83F5vW8Z+9R1HnVz3xmzT2PMFug9ly+Au/fxRWlEBSb6LcZwspSsEn9Xs1uw9YgzAg1EQ==} @@ -2989,9 +2875,6 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - detect-node-es@1.1.0: - resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -3118,6 +3001,11 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + esbuild@0.25.1: + resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3636,10 +3524,6 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-nonce@1.0.1: - resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} - engines: {node: '>=6'} - get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} @@ -4281,6 +4165,10 @@ packages: join-component@1.1.0: resolution: {integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4455,9 +4343,17 @@ packages: resolution: {integrity: sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + loader-runner@4.3.0: resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} engines: {node: '>=6.11.5'} @@ -4483,6 +4379,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + lodash.throttle@4.1.1: resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} @@ -5059,6 +4958,24 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -5251,26 +5168,6 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} - react-remove-scroll-bar@2.3.8: - resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - - react-remove-scroll@2.6.3: - resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - react-server-dom-webpack@19.0.0-rc-6230622a1a-20240610: resolution: {integrity: sha512-nr+IsOVD07QdeCr4BLvR5TALfLaZLi9AIaoa6vXymBc051iDPWedJujYYrjRJy5+9jp9oCx3G8Tt/Bs//TckJw==} engines: {node: '>=0.10.0'} @@ -5284,18 +5181,8 @@ packages: peerDependencies: react: ^16.0.0 || ^17.0.0 || ^18.0.0 - react-style-singleton@2.2.3: - resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - react-test-renderer@18.3.1: - resolution: {integrity: sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==} + react-test-renderer@18.3.1: + resolution: {integrity: sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==} peerDependencies: react: ^18.3.1 @@ -5311,6 +5198,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + readline@1.3.0: resolution: {integrity: sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==} @@ -5434,6 +5325,11 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + rollup@4.35.0: + resolution: {integrity: sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -5669,6 +5565,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + split-on-first@1.1.0: resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} engines: {node: '>=6'} @@ -5865,14 +5765,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - - tamagui@1.125.23: - resolution: {integrity: sha512-FED+bH5zKf1C3EoLL1cLgZL0N3YpADDcIh2jICIvKEIcSPjPJzBc7721REg0Nehf+2pcWYxvvNKgztOykHK5cQ==} - peerDependencies: - react: '*' - tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -5938,6 +5830,9 @@ packages: tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.12: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} @@ -5970,10 +5865,17 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + tr46@3.0.0: resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} engines: {node: '>=12'} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + ts-api-utils@2.0.1: resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} engines: {node: '>=18.12'} @@ -6006,6 +5908,25 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsup@8.4.0: + resolution: {integrity: sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + turbo-darwin-64@2.4.4: resolution: {integrity: sha512-5kPvRkLAfmWI0MH96D+/THnDMGXlFNmjeqNRj5grLKiry+M9pKj3pRuScddAXPdlxjO5Ptz06UNaOQrrYGTx1g==} cpu: [x64] @@ -6181,31 +6102,11 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - use-callback-ref@1.3.3: - resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - use-latest-callback@0.2.3: resolution: {integrity: sha512-7vI3fBuyRcP91pazVboc4qu+6ZqM8izPWX9k7cRnT8hbD5svslcknsh3S9BUhaK11OmgTV4oWZZVSeQAiV53SQ==} peerDependencies: react: '>=16.8' - use-sidecar@1.1.3: - resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} - engines: {node: '>=10'} - peerDependencies: - '@types/react': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - use-sync-external-store@1.4.0: resolution: {integrity: sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==} peerDependencies: @@ -6268,6 +6169,9 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + webidl-conversions@5.0.0: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} engines: {node: '>=8'} @@ -6312,6 +6216,9 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -7379,6 +7286,81 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild/aix-ppc64@0.25.1': + optional: true + + '@esbuild/android-arm64@0.25.1': + optional: true + + '@esbuild/android-arm@0.25.1': + optional: true + + '@esbuild/android-x64@0.25.1': + optional: true + + '@esbuild/darwin-arm64@0.25.1': + optional: true + + '@esbuild/darwin-x64@0.25.1': + optional: true + + '@esbuild/freebsd-arm64@0.25.1': + optional: true + + '@esbuild/freebsd-x64@0.25.1': + optional: true + + '@esbuild/linux-arm64@0.25.1': + optional: true + + '@esbuild/linux-arm@0.25.1': + optional: true + + '@esbuild/linux-ia32@0.25.1': + optional: true + + '@esbuild/linux-loong64@0.25.1': + optional: true + + '@esbuild/linux-mips64el@0.25.1': + optional: true + + '@esbuild/linux-ppc64@0.25.1': + optional: true + + '@esbuild/linux-riscv64@0.25.1': + optional: true + + '@esbuild/linux-s390x@0.25.1': + optional: true + + '@esbuild/linux-x64@0.25.1': + optional: true + + '@esbuild/netbsd-arm64@0.25.1': + optional: true + + '@esbuild/netbsd-x64@0.25.1': + optional: true + + '@esbuild/openbsd-arm64@0.25.1': + optional: true + + '@esbuild/openbsd-x64@0.25.1': + optional: true + + '@esbuild/sunos-x64@0.25.1': + optional: true + + '@esbuild/win32-arm64@0.25.1': + optional: true + + '@esbuild/win32-ia32@0.25.1': + optional: true + + '@esbuild/win32-x64@0.25.1': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.22.0)': dependencies: eslint: 9.22.0 @@ -7722,37 +7704,6 @@ snapshots: find-up: 5.0.0 js-yaml: 4.1.0 - '@floating-ui/core@1.6.9': - dependencies: - '@floating-ui/utils': 0.2.9 - - '@floating-ui/dom@1.6.13': - dependencies: - '@floating-ui/core': 1.6.9 - '@floating-ui/utils': 0.2.9 - - '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/dom': 1.6.13 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@floating-ui/react-native@0.10.7(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/core': 1.6.9 - react: 19.0.0 - react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0) - - '@floating-ui/react@0.27.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@floating-ui/utils': 0.2.9 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - tabbable: 6.2.0 - - '@floating-ui/utils@0.2.9': {} - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -8251,8 +8202,6 @@ snapshots: - '@babel/preset-env' - supports-color - '@react-native/normalize-color@2.1.0': {} - '@react-native/normalize-colors@0.74.89': {} '@react-native/normalize-colors@0.76.7': {} @@ -8266,15 +8215,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.18 - '@react-native/virtualized-lists@0.76.7(@types/react@19.0.10)(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - invariant: 2.2.4 - nullthrows: 1.1.1 - react: 19.0.0 - react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.10 - '@react-navigation/bottom-tabs@7.2.1(@react-navigation/native@7.0.15(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.12.0(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native-screens@4.4.0(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1)': dependencies: '@react-navigation/elements': 2.2.6(@react-navigation/native@7.0.15(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.12.0(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1))(react@18.3.1) @@ -8323,898 +8263,111 @@ snapshots: '@react-navigation/core': 7.4.0(react@18.3.1) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 - nanoid: 3.3.8 - react: 18.3.1 - react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1) - use-latest-callback: 0.2.3(react@18.3.1) - - '@react-navigation/routers@7.2.0': - dependencies: - nanoid: 3.3.8 - - '@rtsao/scc@1.1.0': {} - - '@rushstack/eslint-patch@1.11.0': {} - - '@segment/loosely-validate-event@2.0.0': - dependencies: - component-type: 1.2.2 - join-component: 1.1.0 - - '@sinclair/typebox@0.27.8': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - - '@swc/counter@0.1.3': {} - - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 - - '@tamagui/accordion@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/collapsible': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/collection': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/adapt@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/z-index-stack': 1.125.23 - transitivePeerDependencies: - - react - - react-dom - - react-native - - '@tamagui/alert-dialog@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/aria-hidden': 1.125.23(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/dialog': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/dismissable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/focus-scope': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/popper': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/remove-scroll': 1.125.23(@types/react@19.0.10)(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native - - '@tamagui/animate-presence@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/use-constant': 1.125.23(react@19.0.0) - '@tamagui/use-force-update': 1.125.23(react@19.0.0) - '@tamagui/use-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - transitivePeerDependencies: - - react - - react-dom - - '@tamagui/animate@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - transitivePeerDependencies: - - react - - react-dom - - '@tamagui/animations-react-native@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/use-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - '@tamagui/aria-hidden@1.125.23(react@19.0.0)': - dependencies: - aria-hidden: 1.2.4 - react: 19.0.0 - - '@tamagui/avatar@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/image': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/shapes': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/button@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/font-size': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-button-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/card@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/checkbox-headless@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/checkbox@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/checkbox-headless': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/font-size': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/collapsible@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/collection@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/compose-refs@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@tamagui/constants@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@tamagui/core@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/react-native-media-driver': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/react-native-use-pressable': 1.125.23(react@19.0.0) - '@tamagui/react-native-use-responder-events': 1.125.23(react@19.0.0) - '@tamagui/use-event': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - transitivePeerDependencies: - - react - - react-dom - - react-native - - '@tamagui/create-context@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@tamagui/dialog@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/adapt': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/aria-hidden': 1.125.23(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/dismissable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/focus-scope': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/popper': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/remove-scroll': 1.125.23(@types/react@19.0.10)(react@19.0.0) - '@tamagui/sheet': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/z-index-stack': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native - - '@tamagui/dismissable@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/use-escape-keydown': 1.125.23 - '@tamagui/use-event': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/elements@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/fake-react-native@1.125.23': {} - - '@tamagui/floating@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@floating-ui/react-native': 0.10.7(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/focus-scope@1.125.23(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/start-transition': 1.125.23 - '@tamagui/use-event': 1.125.23(react@19.0.0) - react: 19.0.0 - - '@tamagui/focusable@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - '@tamagui/font-size@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/form@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-button-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-font-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/get-button-sized@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - '@tamagui/get-font-sized@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/get-token@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - '@tamagui/group@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/helpers-tamagui@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - '@tamagui/helpers@1.125.23(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/simple-hash': 1.125.23 - transitivePeerDependencies: - - react - - '@tamagui/image@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/label@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-button-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-font-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0) - transitivePeerDependencies: - - react-dom - - '@tamagui/linear-gradient@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/list-item@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/font-size': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-font-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/normalize-css-color@1.125.23': - dependencies: - '@react-native/normalize-color': 2.1.0 - - '@tamagui/polyfill-dev@1.125.23': {} - - '@tamagui/popover@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react': 0.27.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/adapt': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/animate': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/aria-hidden': 1.125.23(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/dismissable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/floating': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/focus-scope': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/popper': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/remove-scroll': 1.125.23(@types/react@19.0.10)(react@19.0.0) - '@tamagui/scroll-view': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/sheet': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - react-freeze: 1.0.4(react@19.0.0) - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native - - '@tamagui/popper@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/floating': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/start-transition': 1.125.23 - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/portal@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/start-transition': 1.125.23 - '@tamagui/use-did-finish-ssr': 1.125.23(react@19.0.0) - '@tamagui/use-event': 1.125.23(react@19.0.0) - '@tamagui/z-index-stack': 1.125.23 - transitivePeerDependencies: - - react - - react-dom - - react-native - - '@tamagui/progress@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/radio-group@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/radio-headless': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/roving-focus': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/radio-headless@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/react-native-media-driver@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0) - transitivePeerDependencies: - - react - - react-dom - - '@tamagui/react-native-use-pressable@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@tamagui/react-native-use-responder-events@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@tamagui/remove-scroll@1.125.23(@types/react@19.0.10)(react@19.0.0)': - dependencies: - react: 19.0.0 - react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0) - transitivePeerDependencies: - - '@types/react' - - '@tamagui/roving-focus@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/collection': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-direction': 1.125.23(react@19.0.0) - '@tamagui/use-event': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/scroll-view@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native - - '@tamagui/select@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react': 0.27.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@floating-ui/react-native': 0.10.7(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/adapt': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/dismissable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/focus-scope': 1.125.23(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/list-item': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/remove-scroll': 1.125.23(@types/react@19.0.10)(react@19.0.0) - '@tamagui/separator': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/sheet': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-debounce': 1.125.23(react@19.0.0) - '@tamagui/use-event': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native + nanoid: 3.3.8 + react: 18.3.1 + react-native: 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@18.3.18)(react@18.3.1) + use-latest-callback: 0.2.3(react@18.3.1) - '@tamagui/separator@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': + '@react-navigation/routers@7.2.0': dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + nanoid: 3.3.8 - '@tamagui/shapes@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-android-arm-eabi@4.35.0': + optional: true - '@tamagui/sheet@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/adapt': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/animations-react-native': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/remove-scroll': 1.125.23(@types/react@19.0.10)(react@19.0.0) - '@tamagui/scroll-view': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-constant': 1.125.23(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-did-finish-ssr': 1.125.23(react@19.0.0) - '@tamagui/use-keyboard-visible': 1.125.23(react@19.0.0) - '@tamagui/z-index-stack': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native + '@rollup/rollup-android-arm64@4.35.0': + optional: true - '@tamagui/simple-hash@1.125.23': {} + '@rollup/rollup-darwin-arm64@4.35.0': + optional: true - '@tamagui/slider@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-debounce': 1.125.23(react@19.0.0) - '@tamagui/use-direction': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-darwin-x64@4.35.0': + optional: true - '@tamagui/stacks@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-freebsd-arm64@4.35.0': + optional: true - '@tamagui/start-transition@1.125.23': {} + '@rollup/rollup-freebsd-x64@4.35.0': + optional: true - '@tamagui/switch-headless@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-linux-arm-gnueabihf@4.35.0': + optional: true - '@tamagui/switch@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/switch-headless': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-previous': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-linux-arm-musleabihf@4.35.0': + optional: true - '@tamagui/tabs@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/get-button-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/group': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/roving-focus': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-direction': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-linux-arm64-gnu@4.35.0': + optional: true - '@tamagui/text@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/get-font-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-linux-arm64-musl@4.35.0': + optional: true - '@tamagui/theme@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom + '@rollup/rollup-linux-loongarch64-gnu@4.35.0': + optional: true - '@tamagui/timer@1.125.23': {} - - '@tamagui/toggle-group@1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/font-size': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/group': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/roving-focus': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-direction': 1.125.23(react@19.0.0) - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom - - react-native + '@rollup/rollup-linux-powerpc64le-gnu@4.35.0': + optional: true - '@tamagui/tooltip@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react': 0.27.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/floating': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/popover': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/popper': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native + '@rollup/rollup-linux-riscv64-gnu@4.35.0': + optional: true - '@tamagui/types@1.125.23': {} + '@rollup/rollup-linux-s390x-gnu@4.35.0': + optional: true - '@tamagui/use-callback-ref@1.125.23': {} + '@rollup/rollup-linux-x64-gnu@4.35.0': + optional: true - '@tamagui/use-constant@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 + '@rollup/rollup-linux-x64-musl@4.35.0': + optional: true - '@tamagui/use-controllable-state@1.125.23(react@19.0.0)': - dependencies: - '@tamagui/start-transition': 1.125.23 - '@tamagui/use-event': 1.125.23(react@19.0.0) - react: 19.0.0 + '@rollup/rollup-win32-arm64-msvc@4.35.0': + optional: true - '@tamagui/use-debounce@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 + '@rollup/rollup-win32-ia32-msvc@4.35.0': + optional: true - '@tamagui/use-did-finish-ssr@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 + '@rollup/rollup-win32-x64-msvc@4.35.0': + optional: true - '@tamagui/use-direction@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 + '@rtsao/scc@1.1.0': {} - '@tamagui/use-escape-keydown@1.125.23': - dependencies: - '@tamagui/use-callback-ref': 1.125.23 + '@rushstack/eslint-patch@1.11.0': {} - '@tamagui/use-event@1.125.23(react@19.0.0)': + '@segment/loosely-validate-event@2.0.0': dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - react: 19.0.0 + component-type: 1.2.2 + join-component: 1.1.0 - '@tamagui/use-force-update@1.125.23(react@19.0.0)': - dependencies: - react: 19.0.0 + '@sinclair/typebox@0.27.8': {} - '@tamagui/use-keyboard-visible@1.125.23(react@19.0.0)': + '@sinonjs/commons@3.0.1': dependencies: - react: 19.0.0 + type-detect: 4.0.8 - '@tamagui/use-presence@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@sinonjs/fake-timers@10.3.0': dependencies: - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - transitivePeerDependencies: - - react-dom + '@sinonjs/commons': 3.0.1 - '@tamagui/use-previous@1.125.23': {} + '@swc/counter@0.1.3': {} - '@tamagui/use-window-dimensions@1.125.23(react@19.0.0)': + '@swc/helpers@0.5.15': dependencies: - '@tamagui/constants': 1.125.23(react@19.0.0) - react: 19.0.0 + tslib: 2.8.1 + + '@tanstack/query-core@5.68.0': {} + + '@tanstack/query-devtools@5.67.2': {} - '@tamagui/visually-hidden@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@tanstack/react-query-devtools@5.68.0(@tanstack/react-query@5.68.0(react@19.0.0))(react@19.0.0)': dependencies: - '@tamagui/web': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@tanstack/query-devtools': 5.67.2 + '@tanstack/react-query': 5.68.0(react@19.0.0) react: 19.0.0 - transitivePeerDependencies: - - react-dom - '@tamagui/web@1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/helpers': 1.125.23(react@19.0.0) - '@tamagui/normalize-css-color': 1.125.23 - '@tamagui/timer': 1.125.23 - '@tamagui/types': 1.125.23 - '@tamagui/use-did-finish-ssr': 1.125.23(react@19.0.0) - '@tamagui/use-event': 1.125.23(react@19.0.0) - '@tamagui/use-force-update': 1.125.23(react@19.0.0) + '@tanstack/react-query@5.68.0(react@19.0.0)': + dependencies: + '@tanstack/query-core': 5.68.0 react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@tamagui/z-index-stack@1.125.23': {} '@tootallnate/once@2.0.0': {} @@ -9673,10 +8826,6 @@ snapshots: argparse@2.0.1: {} - aria-hidden@1.2.4: - dependencies: - tslib: 2.8.1 - aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -9970,12 +9119,19 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bundle-require@5.1.0(esbuild@0.25.1): + dependencies: + esbuild: 0.25.1 + load-tsconfig: 0.2.5 + busboy@1.6.0: dependencies: streamsearch: 1.1.0 bytes@3.1.2: {} + cac@6.7.14: {} + cacache@18.0.4: dependencies: '@npmcli/fs': 3.1.1 @@ -10076,6 +9232,10 @@ snapshots: charenc@0.0.2: {} + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chownr@2.0.0: {} chrome-launcher@0.15.2: @@ -10209,6 +9369,8 @@ snapshots: transitivePeerDependencies: - supports-color + consola@3.4.0: {} + constant-case@2.0.0: dependencies: snake-case: 2.1.0 @@ -10400,8 +9562,6 @@ snapshots: detect-newline@3.1.0: {} - detect-node-es@1.1.0: {} - diff-sequences@29.6.3: {} diff@4.0.2: {} @@ -10575,6 +9735,34 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + esbuild@0.25.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.1 + '@esbuild/android-arm': 0.25.1 + '@esbuild/android-arm64': 0.25.1 + '@esbuild/android-x64': 0.25.1 + '@esbuild/darwin-arm64': 0.25.1 + '@esbuild/darwin-x64': 0.25.1 + '@esbuild/freebsd-arm64': 0.25.1 + '@esbuild/freebsd-x64': 0.25.1 + '@esbuild/linux-arm': 0.25.1 + '@esbuild/linux-arm64': 0.25.1 + '@esbuild/linux-ia32': 0.25.1 + '@esbuild/linux-loong64': 0.25.1 + '@esbuild/linux-mips64el': 0.25.1 + '@esbuild/linux-ppc64': 0.25.1 + '@esbuild/linux-riscv64': 0.25.1 + '@esbuild/linux-s390x': 0.25.1 + '@esbuild/linux-x64': 0.25.1 + '@esbuild/netbsd-arm64': 0.25.1 + '@esbuild/netbsd-x64': 0.25.1 + '@esbuild/openbsd-arm64': 0.25.1 + '@esbuild/openbsd-x64': 0.25.1 + '@esbuild/sunos-x64': 0.25.1 + '@esbuild/win32-arm64': 0.25.1 + '@esbuild/win32-ia32': 0.25.1 + '@esbuild/win32-x64': 0.25.1 + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -11245,8 +10433,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-package-type@0.1.0: {} get-port@3.2.0: {} @@ -12165,6 +11351,8 @@ snapshots: join-component@1.1.0: {} + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -12352,8 +11540,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.27.0 lightningcss-win32-x64-msvc: 1.27.0 + lilconfig@3.1.3: {} + lines-and-columns@1.2.4: {} + load-tsconfig@0.2.5: {} + loader-runner@4.3.0: {} locate-path@3.0.0: @@ -12375,6 +11567,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash.sortby@4.7.0: {} + lodash.throttle@4.1.1: {} lodash@4.17.21: {} @@ -12705,7 +11899,7 @@ snapshots: netmask@2.0.2: {} - next@15.2.2(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next@15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0): dependencies: '@next/env': 15.2.2 '@swc/counter': 0.1.3 @@ -12715,7 +11909,7 @@ snapshots: postcss: 8.4.31 react: 19.0.0 react-dom: 19.0.0(react@19.0.0) - styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.0.0) + styled-jsx: 5.1.6(react@19.0.0) optionalDependencies: '@next/swc-darwin-arm64': 15.2.2 '@next/swc-darwin-x64': 15.2.2 @@ -13059,6 +12253,12 @@ snapshots: possible-typed-array-names@1.1.0: {} + postcss-load-config@6.0.1(postcss@8.4.49): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.4.49 + postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -13191,10 +12391,6 @@ snapshots: dependencies: react: 18.3.1 - react-freeze@1.0.4(react@19.0.0): - dependencies: - react: 19.0.0 - react-helmet-async@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.26.10 @@ -13335,79 +12531,8 @@ snapshots: - supports-color - utf-8-validate - react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0): - dependencies: - '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.76.7 - '@react-native/codegen': 0.76.7(@babel/preset-env@7.26.9(@babel/core@7.26.10)) - '@react-native/community-cli-plugin': 0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10)) - '@react-native/gradle-plugin': 0.76.7 - '@react-native/js-polyfills': 0.76.7 - '@react-native/normalize-colors': 0.76.7 - '@react-native/virtualized-lists': 0.76.7(@types/react@19.0.10)(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - abort-controller: 3.0.0 - anser: 1.4.10 - ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.26.10) - babel-plugin-syntax-hermes-parser: 0.23.1 - base64-js: 1.5.1 - chalk: 4.1.2 - commander: 12.1.0 - event-target-shim: 5.0.1 - flow-enums-runtime: 0.0.6 - glob: 7.2.3 - invariant: 2.2.4 - jest-environment-node: 29.7.0 - jsc-android: 250231.0.0 - memoize-one: 5.2.1 - metro-runtime: 0.81.3 - metro-source-map: 0.81.3 - mkdirp: 0.5.6 - nullthrows: 1.1.1 - pretty-format: 29.7.0 - promise: 8.3.0 - react: 19.0.0 - react-devtools-core: 5.3.2 - react-refresh: 0.14.2 - regenerator-runtime: 0.13.11 - scheduler: 0.24.0-canary-efb381bbf-20230505 - semver: 7.7.1 - stacktrace-parser: 0.1.11 - whatwg-fetch: 3.6.20 - ws: 6.2.3 - yargs: 17.7.2 - optionalDependencies: - '@types/react': 19.0.10 - transitivePeerDependencies: - - '@babel/core' - - '@babel/preset-env' - - '@react-native-community/cli-server-api' - - bufferutil - - encoding - - supports-color - - utf-8-validate - react-refresh@0.14.2: {} - react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0): - dependencies: - react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.0.10 - - react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0): - dependencies: - react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.10 - react-server-dom-webpack@19.0.0-rc-6230622a1a-20240610(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(webpack@5.98.0): dependencies: acorn-loose: 8.4.0 @@ -13422,14 +12547,6 @@ snapshots: react: 18.3.1 react-is: 18.3.1 - react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): - dependencies: - get-nonce: 1.0.1 - react: 19.0.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.0.10 - react-test-renderer@18.3.1(react@18.3.1): dependencies: react: 18.3.1 @@ -13449,6 +12566,8 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdirp@4.1.2: {} + readline@1.3.0: {} recast@0.21.5: @@ -13582,6 +12701,31 @@ snapshots: dependencies: glob: 7.2.3 + rollup@4.35.0: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.35.0 + '@rollup/rollup-android-arm64': 4.35.0 + '@rollup/rollup-darwin-arm64': 4.35.0 + '@rollup/rollup-darwin-x64': 4.35.0 + '@rollup/rollup-freebsd-arm64': 4.35.0 + '@rollup/rollup-freebsd-x64': 4.35.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.35.0 + '@rollup/rollup-linux-arm-musleabihf': 4.35.0 + '@rollup/rollup-linux-arm64-gnu': 4.35.0 + '@rollup/rollup-linux-arm64-musl': 4.35.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.35.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.35.0 + '@rollup/rollup-linux-riscv64-gnu': 4.35.0 + '@rollup/rollup-linux-s390x-gnu': 4.35.0 + '@rollup/rollup-linux-x64-gnu': 4.35.0 + '@rollup/rollup-linux-x64-musl': 4.35.0 + '@rollup/rollup-win32-arm64-msvc': 4.35.0 + '@rollup/rollup-win32-ia32-msvc': 4.35.0 + '@rollup/rollup-win32-x64-msvc': 4.35.0 + fsevents: 2.3.3 + run-async@2.4.1: {} run-parallel@1.2.0: @@ -13877,6 +13021,10 @@ snapshots: source-map@0.6.1: {} + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + split-on-first@1.1.0: {} split@1.0.1: @@ -14030,12 +13178,10 @@ snapshots: structured-headers@0.4.1: {} - styled-jsx@5.1.6(@babel/core@7.26.10)(react@19.0.0): + styled-jsx@5.1.6(react@19.0.0): dependencies: client-only: 0.0.1 react: 19.0.0 - optionalDependencies: - '@babel/core': 7.26.10 styleq@0.1.3: {} @@ -14079,69 +13225,6 @@ snapshots: symbol-tree@3.2.4: {} - tabbable@6.2.0: {} - - tamagui@1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0): - dependencies: - '@tamagui/accordion': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/adapt': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/alert-dialog': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/animate-presence': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/avatar': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/button': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/card': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/checkbox': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/compose-refs': 1.125.23(react@19.0.0) - '@tamagui/constants': 1.125.23(react@19.0.0) - '@tamagui/core': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/create-context': 1.125.23(react@19.0.0) - '@tamagui/dialog': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/elements': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/fake-react-native': 1.125.23 - '@tamagui/focusable': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/font-size': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/form': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-button-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/get-font-sized': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/get-token': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/group': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/helpers-tamagui': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/image': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/label': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/linear-gradient': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/list-item': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/polyfill-dev': 1.125.23 - '@tamagui/popover': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/popper': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/portal': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/progress': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/radio-group': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/react-native-media-driver': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/scroll-view': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/select': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/separator': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/shapes': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/sheet': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/slider': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/stacks': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/switch': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/tabs': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/text': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/theme': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/toggle-group': 1.125.23(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/tooltip': 1.125.23(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react-native@0.76.7(@babel/core@7.26.10)(@babel/preset-env@7.26.9(@babel/core@7.26.10))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0) - '@tamagui/use-controllable-state': 1.125.23(react@19.0.0) - '@tamagui/use-debounce': 1.125.23(react@19.0.0) - '@tamagui/use-force-update': 1.125.23(react@19.0.0) - '@tamagui/use-window-dimensions': 1.125.23(react@19.0.0) - '@tamagui/visually-hidden': 1.125.23(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tamagui/z-index-stack': 1.125.23 - react: 19.0.0 - transitivePeerDependencies: - - '@types/react' - - react-dom - - react-native - tapable@2.2.1: {} tar@6.2.1: @@ -14208,6 +13291,8 @@ snapshots: tinycolor2@1.6.0: {} + tinyexec@0.3.2: {} + tinyglobby@0.2.12: dependencies: fdir: 6.4.3(picomatch@4.0.2) @@ -14244,10 +13329,16 @@ snapshots: tr46@0.0.3: {} + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + tr46@3.0.0: dependencies: punycode: 2.3.1 + tree-kill@1.2.2: {} + ts-api-utils@2.0.1(typescript@5.8.2): dependencies: typescript: 5.8.2 @@ -14301,6 +13392,33 @@ snapshots: tslib@2.8.1: {} + tsup@8.4.0(postcss@8.4.49)(typescript@5.8.2): + dependencies: + bundle-require: 5.1.0(esbuild@0.25.1) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.0 + debug: 4.4.0 + esbuild: 0.25.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.4.49) + resolve-from: 5.0.0 + rollup: 4.35.0 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.12 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.4.49 + typescript: 5.8.2 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + turbo-darwin-64@2.4.4: optional: true @@ -14464,25 +13582,10 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0): - dependencies: - react: 19.0.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.0.10 - use-latest-callback@0.2.3(react@18.3.1): dependencies: react: 18.3.1 - use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0): - dependencies: - detect-node-es: 1.1.0 - react: 19.0.0 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 19.0.10 - use-sync-external-store@1.4.0(react@18.3.1): dependencies: react: 18.3.1 @@ -14532,6 +13635,8 @@ snapshots: webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + webidl-conversions@5.0.0: {} webidl-conversions@7.0.0: {} @@ -14592,6 +13697,12 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/turbo.json b/turbo.json index d6a7fe0..4e06ab9 100644 --- a/turbo.json +++ b/turbo.json @@ -5,7 +5,7 @@ "build": { "dependsOn": ["^build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], - "outputs": [".next/**", "!.next/cache/**"] + "outputs": [".next/**", "!.next/cache/**", "dist/**"] }, "lint": { "dependsOn": ["^lint"] @@ -14,6 +14,7 @@ "dependsOn": ["^check-types"] }, "dev": { + "dependsOn": ["^build"], "cache": false, "persistent": true } From 78a2a4dba1ec3864cf33d45c2ff8e4f58cff77ac Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 17 Mar 2025 15:16:30 +0900 Subject: [PATCH 02/54] =?UTF-8?q?fix=20:=20gpt=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gpt-review.yml | 109 ++++--------------------------- 1 file changed, 13 insertions(+), 96 deletions(-) diff --git a/.github/workflows/gpt-review.yml b/.github/workflows/gpt-review.yml index f2f5091..613e433 100644 --- a/.github/workflows/gpt-review.yml +++ b/.github/workflows/gpt-review.yml @@ -1,105 +1,22 @@ -name: PR Blog Summarizer - -on: - pull_request: - types: [opened, synchronize] +name: Code Review permissions: - issues: write + contents: read pull-requests: write +on: + pull_request: + types: [opened, reopened, synchronize] + jobs: - process: + test: runs-on: ubuntu-latest - steps: - # Step 1: Check out the code - - name: Check out code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - # Step 2: Set up Python - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - # Step 3: Install dependencies - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install openai requests - - # Step 4: Get Changed Files - - name: Get Changed Files - id: get_files - run: | - CHANGED_FILES=$(git diff --name-only origin/main ${{ github.sha }}) - - if [ -z "$CHANGED_FILES" ]; then - echo "changed_files=" >> $GITHUB_ENV - else - # .md 파일만 필터링 - MD_FILES=$(echo "$CHANGED_FILES" | grep -E '^week[0-9]+/.*\.md$') - echo $MD_FILES - - if [ -z "$MD_FILES" ]; then - echo "changed_files=" >> $GITHUB_ENV - else - # 환경 변수에 저장 - echo "changed_files=$MD_FILES" >> $GITHUB_ENV - fi - fi - - # Step 5: Process Changed Files - - name: Process Changed Files + - uses: anc95/ChatGPT-CodeReview@main env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} OPENAI_API_KEY: ${{ secrets.GPT_KEY }} - changed_files: ${{ env.changed_files }} - run: | - if [ -z "$changed_files" ]; then - echo "No changed files to process." - exit 0 - fi - - SUMMARY_MESSAGE="### Automated Review\n" - IFS=$'\n' - for file_path in $changed_files; do - # Skip link.md files - if [[ "$file_path" == *link.md ]]; then - continue - fi - - # Process non-link.md files - echo "Processing file: $file_path" - FILE_CONTENT=$(cat "$file_path" || echo "") - if [ -z "$FILE_CONTENT" ]; then - SUMMARY_MESSAGE+="Error: Could not read content of $file_path\n" - continue - fi - - # Run feedback.py - FEEDBACK=$(python feedback.py --content "$FILE_CONTENT" || echo "Error: feedback.py failed for $file_path") - SUMMARY_MESSAGE+="Feedback for file: $file_path\n" - SUMMARY_MESSAGE+="$FEEDBACK\n" - done - - echo "summary_message<> $GITHUB_ENV - echo -e "$SUMMARY_MESSAGE" >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - # Step 6: Post Comment on Pull Request - - name: Post Comment on Pull Request - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const summary = process.env.summary_message; - if (summary && summary.trim().length > 0) { - await github.rest.issues.createComment({ - ...context.repo, - issue_number: context.payload.pull_request.number, - body: summary - }); - } + # optional + LANGUAGE: Korean + PROMPT: + IGNORE_PATTERNS: /node_modules,*.md # Regex pattern to ignore files, separated by comma From 78f0cb3b2e350eaa40f33a8e52171c958e88dae4 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 17 Mar 2025 15:37:17 +0900 Subject: [PATCH 03/54] =?UTF-8?q?chore=20:=20type=20index=EC=97=90?= =?UTF-8?q?=EC=84=9C=20export=20=ED=95=98=EA=B2=8C=20=EB=B3=80=EA=B2=BD.?= =?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=20=EC=88=98=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/src/index.ts | 1 + packages/query/src/index.ts | 7 +++++++ packages/query/src/types.ts | 2 +- packages/query/src/useComposer.ts | 26 ++++++++++++++++++++++++++ packages/query/src/useLyricist.ts | 26 ++++++++++++++++++++++++++ packages/query/src/useNo.ts | 26 ++++++++++++++++++++++++++ packages/query/src/usePopular.ts | 26 ++++++++++++++++++++++++++ packages/query/src/useRelease.ts | 26 ++++++++++++++++++++++++++ packages/query/src/useSinger.ts | 26 ++++++++++++++++++++++++++ packages/query/src/useSong.ts | 3 +-- 10 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 packages/query/src/useComposer.ts create mode 100644 packages/query/src/useLyricist.ts create mode 100644 packages/query/src/useNo.ts create mode 100644 packages/query/src/usePopular.ts create mode 100644 packages/query/src/useRelease.ts create mode 100644 packages/query/src/useSinger.ts diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 27b0056..281824a 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -5,3 +5,4 @@ export { default as getLyricist } from './getLyricist'; export { default as getNo } from './getNo'; export { default as getRelease } from './getRelease'; export { default as getPopular } from './getPopular'; +export type { Brand, Period, ResponseType, InstanceResponse } from './types'; diff --git a/packages/query/src/index.ts b/packages/query/src/index.ts index e2c8c88..228f28c 100644 --- a/packages/query/src/index.ts +++ b/packages/query/src/index.ts @@ -1 +1,8 @@ export { default as useSong } from './useSong'; +export { default as useSinger } from './useSinger'; +export { default as useComposer } from './useComposer'; +export { default as useLyricist } from './useLyricist'; +export { default as useNo } from './useNo'; +export { default as useRelease } from './useRelease'; +export { default as usePopular } from './usePopular'; +export type { UseQueryReturn } from './types'; diff --git a/packages/query/src/types.ts b/packages/query/src/types.ts index 462075c..0de00ea 100644 --- a/packages/query/src/types.ts +++ b/packages/query/src/types.ts @@ -1,4 +1,4 @@ -import { ResponseType } from '@repo/api/src/types'; +import { ResponseType } from '@repo/api'; export interface UseQueryReturn { data: ResponseType | null | undefined; diff --git a/packages/query/src/useComposer.ts b/packages/query/src/useComposer.ts new file mode 100644 index 0000000..ce7d051 --- /dev/null +++ b/packages/query/src/useComposer.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getComposer, Brand } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetComposerProps { + composer: string; + brand?: Brand; +} + +const useComposer = (props: GetComposerProps): UseQueryReturn => { + const { composer, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['composer', { composer, brand }], + queryFn: () => getComposer(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useComposer; diff --git a/packages/query/src/useLyricist.ts b/packages/query/src/useLyricist.ts new file mode 100644 index 0000000..031fe92 --- /dev/null +++ b/packages/query/src/useLyricist.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getLyricist, Brand } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetLyricistProps { + lyricist: string; + brand?: Brand; +} + +const useLyricist = (props: GetLyricistProps): UseQueryReturn => { + const { lyricist, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['lyricist', { lyricist, brand }], + queryFn: () => getLyricist(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useLyricist; diff --git a/packages/query/src/useNo.ts b/packages/query/src/useNo.ts new file mode 100644 index 0000000..153c4b9 --- /dev/null +++ b/packages/query/src/useNo.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getNo, Brand } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetNoProps { + no: string; + brand?: Brand; +} + +const useNo = (props: GetNoProps): UseQueryReturn => { + const { no, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['no', { no, brand }], + queryFn: () => getNo(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useNo; diff --git a/packages/query/src/usePopular.ts b/packages/query/src/usePopular.ts new file mode 100644 index 0000000..ca782dc --- /dev/null +++ b/packages/query/src/usePopular.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getPopular, Brand, Period } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetPopularProps { + brand: Brand; + period: Period; +} + +const usePopular = (props: GetPopularProps): UseQueryReturn => { + const { brand, period } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['popular', { brand, period }], + queryFn: () => getPopular(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default usePopular; diff --git a/packages/query/src/useRelease.ts b/packages/query/src/useRelease.ts new file mode 100644 index 0000000..56a87a3 --- /dev/null +++ b/packages/query/src/useRelease.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getRelease, Brand } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetReleaseProps { + release: string; + brand?: Brand; +} + +const useRelease = (props: GetReleaseProps): UseQueryReturn => { + const { release, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['release', { release, brand }], + queryFn: () => getRelease(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useRelease; diff --git a/packages/query/src/useSinger.ts b/packages/query/src/useSinger.ts new file mode 100644 index 0000000..d83c43d --- /dev/null +++ b/packages/query/src/useSinger.ts @@ -0,0 +1,26 @@ +import { useQuery } from '@tanstack/react-query'; +import { getSinger, Brand } from '@repo/api'; +import { UseQueryReturn } from './types'; + +interface GetSingerProps { + singer: string; + brand?: Brand; +} + +const useSinger = (props: GetSingerProps): UseQueryReturn => { + const { singer, brand } = props; + + const { data, isLoading, isError, error } = useQuery({ + queryKey: ['singer', { singer, brand }], + queryFn: () => getSinger(props), + }); + + return { + data, + isLoading, + isError, + error, + }; +}; + +export default useSinger; diff --git a/packages/query/src/useSong.ts b/packages/query/src/useSong.ts index 760baf3..6966027 100644 --- a/packages/query/src/useSong.ts +++ b/packages/query/src/useSong.ts @@ -1,6 +1,5 @@ import { useQuery } from '@tanstack/react-query'; -import { getSong } from '@repo/api'; -import { Brand } from '@repo/api/src/types'; +import { getSong, Brand, ResponseType } from '@repo/api'; import { UseQueryReturn } from './types'; interface GetSongProps { From b5b51563aaf770c0e0a49e244980b1c3dbc80221 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Tue, 18 Mar 2025 17:51:11 +0900 Subject: [PATCH 04/54] =?UTF-8?q?feat=20:=20gpt=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=88=98=EC=A0=95.=20=EC=BF=BC=EB=A6=AC=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=20=ED=82=A4=20=EC=88=98=EC=A0=95.=20next.js=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gpt-review.yml | 10 ++- .../songs/[type]/[param]/composer/route.ts | 29 +++++++++ .../songs/[type]/[param]/lyricist/route.ts | 29 +++++++++ .../app/api/songs/[type]/[param]/no/route.ts | 29 +++++++++ .../api/songs/[type]/[param]/popular/route.ts | 33 ++++++++++ .../api/songs/[type]/[param]/release/route.ts | 29 +++++++++ .../web/app/api/songs/[type]/[param]/route.ts | 61 +++++++++++++++++++ .../api/songs/[type]/[param]/singer/route.ts | 29 +++++++++ .../api/songs/[type]/[param]/title/route.ts | 29 +++++++++ apps/web/app/test/SearchForm.tsx | 41 +++++++++++++ apps/web/app/test/page.tsx | 48 +++++++++++++++ apps/web/app/test2/page.tsx | 0 packages/query/src/useComposer.ts | 7 ++- packages/query/src/useLyricist.ts | 7 ++- packages/query/src/useNo.ts | 7 ++- packages/query/src/usePopular.ts | 5 +- packages/query/src/useRelease.ts | 7 ++- packages/query/src/useSinger.ts | 7 ++- packages/query/src/useSong.ts | 7 ++- 19 files changed, 397 insertions(+), 17 deletions(-) create mode 100644 apps/web/app/api/songs/[type]/[param]/composer/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/lyricist/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/no/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/popular/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/release/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/singer/route.ts create mode 100644 apps/web/app/api/songs/[type]/[param]/title/route.ts create mode 100644 apps/web/app/test/SearchForm.tsx create mode 100644 apps/web/app/test/page.tsx create mode 100644 apps/web/app/test2/page.tsx diff --git a/.github/workflows/gpt-review.yml b/.github/workflows/gpt-review.yml index 613e433..74c0228 100644 --- a/.github/workflows/gpt-review.yml +++ b/.github/workflows/gpt-review.yml @@ -10,7 +10,8 @@ on: jobs: test: - runs-on: ubuntu-latest + + if: ${{ contains(github.event.head_commit.message, '#gpt') || contains(github.event.head_commit.message, '#gpt-review') }} runs-on: ubuntu-latest steps: - uses: anc95/ChatGPT-CodeReview@main env: @@ -18,5 +19,8 @@ jobs: OPENAI_API_KEY: ${{ secrets.GPT_KEY }} # optional LANGUAGE: Korean - PROMPT: - IGNORE_PATTERNS: /node_modules,*.md # Regex pattern to ignore files, separated by comma + max_tokens: 10000 + MAX_PATCH_LENGTH: 10000 + + IGNORE_PATTERNS: /dist, /node_modules,*.md # Regex pattern to ignore files, separated by comma + INCLUDE_PATTERNS: "*.js, *.ts, *.jsx, *.tsx" # glob pattern or regex pattern to include files, separated by comma \ No newline at end of file diff --git a/apps/web/app/api/songs/[type]/[param]/composer/route.ts b/apps/web/app/api/songs/[type]/[param]/composer/route.ts new file mode 100644 index 0000000..6584228 --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/composer/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getComposer, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const composer = searchParams.get('composer'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!composer) { + return NextResponse.json({ error: '작곡가 이름(composer)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getComposer({ composer, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts b/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts new file mode 100644 index 0000000..9bb74c1 --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getLyricist, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const lyricist = searchParams.get('lyricist'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!lyricist) { + return NextResponse.json({ error: '작사가 이름(lyricist)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getLyricist({ lyricist, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/no/route.ts b/apps/web/app/api/songs/[type]/[param]/no/route.ts new file mode 100644 index 0000000..85853bd --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/no/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getNo, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const no = searchParams.get('no'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!no) { + return NextResponse.json({ error: '노래 번호(no)는 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getNo({ no, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/popular/route.ts b/apps/web/app/api/songs/[type]/[param]/popular/route.ts new file mode 100644 index 0000000..da16dbf --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/popular/route.ts @@ -0,0 +1,33 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getPopular, Brand, Period } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const brand = searchParams.get('brand') as Brand; + const period = searchParams.get('period') as Period; + + // 필수 파라미터 검증 + if (!brand) { + return NextResponse.json({ error: '브랜드(brand)는 필수 파라미터입니다.' }, { status: 400 }); + } + + if (!period) { + return NextResponse.json({ error: '기간(period)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getPopular({ brand, period }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '인기 노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('인기 노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/release/route.ts b/apps/web/app/api/songs/[type]/[param]/release/route.ts new file mode 100644 index 0000000..dbc7721 --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/release/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getRelease, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const release = searchParams.get('release'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!release) { + return NextResponse.json({ error: '발매일(release)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getRelease({ release, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/route.ts b/apps/web/app/api/songs/[type]/[param]/route.ts new file mode 100644 index 0000000..05c14fc --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/route.ts @@ -0,0 +1,61 @@ +// app/api/songs/[type]/[param]/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import { getSong, getSinger, getComposer, getLyricist, getNo, getRelease, getPopular, Brand, Period } from '@repo/api'; + +export async function GET(request: NextRequest, { params }: { params: { type: string; param: string } }) { + try { + const { type, param } = params; + const searchParams = request.nextUrl.searchParams; + const brand = searchParams.get('brand') as Brand | undefined; + + let result = null; + + switch (type) { + case 'title': + result = await getSong({ title: param, brand }); + break; + + case 'singer': + result = await getSinger({ singer: param, brand }); + break; + + case 'composer': + result = await getComposer({ composer: param, brand }); + break; + + case 'lyricist': + result = await getLyricist({ lyricist: param, brand }); + break; + + case 'no': + result = await getNo({ no: param, brand }); + break; + + case 'release': + result = await getRelease({ release: param, brand }); + break; + + case 'popular': + // popular의 경우는 좀 특별하게 처리 + // param은 brand 값이 되고, period는 쿼리 파라미터로 받음 + const period = searchParams.get('period') as Period; + if (!period) { + return NextResponse.json({ error: '기간(period)은 필수 파라미터입니다.' }, { status: 400 }); + } + result = await getPopular({ brand: param as Brand, period }); + break; + + default: + return NextResponse.json({ error: '지원하지 않는 검색 유형입니다' }, { status: 400 }); + } + + if (!result) { + return NextResponse.json({ error: '검색 결과가 없습니다' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('API 요청 오류:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/singer/route.ts b/apps/web/app/api/songs/[type]/[param]/singer/route.ts new file mode 100644 index 0000000..f5feb1d --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/singer/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getSinger, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const singer = searchParams.get('singer'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!singer) { + return NextResponse.json({ error: '가수 이름(singer)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getSinger({ singer, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/api/songs/[type]/[param]/title/route.ts b/apps/web/app/api/songs/[type]/[param]/title/route.ts new file mode 100644 index 0000000..6ec1641 --- /dev/null +++ b/apps/web/app/api/songs/[type]/[param]/title/route.ts @@ -0,0 +1,29 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { getSong, Brand } from '@repo/api'; + +export async function GET(request: NextRequest) { + try { + // URL에서 쿼리 파라미터 추출 + const searchParams = request.nextUrl.searchParams; + const title = searchParams.get('title'); + const brand = searchParams.get('brand') as Brand | undefined; + + // 필수 파라미터 검증 + if (!title) { + return NextResponse.json({ error: '노래 제목(title)은 필수 파라미터입니다.' }, { status: 400 }); + } + + // API 호출 + const result = await getSong({ title, brand }); + + // 결과 반환 + if (!result) { + return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); + } + + return NextResponse.json({ data: result }); + } catch (error) { + console.error('노래 검색 중 오류 발생:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); + } +} diff --git a/apps/web/app/test/SearchForm.tsx b/apps/web/app/test/SearchForm.tsx new file mode 100644 index 0000000..b6e102c --- /dev/null +++ b/apps/web/app/test/SearchForm.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { useState, FormEvent } from 'react'; +import { useRouter } from 'next/navigation'; + +interface SearchFormProps { + initialSinger?: string; +} + +export function SearchForm({ initialSinger = '' }: SearchFormProps) { + const router = useRouter(); + const [singer, setSinger] = useState(initialSinger); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + + // URL 쿼리 파라미터 업데이트 + if (singer) { + router.push(`/test?singer=${encodeURIComponent(singer)}`); + } else { + router.push('/test'); + } + }; + + return ( +
+
+ setSinger(e.target.value)} + placeholder="가수 이름을 입력하세요" + className="flex-1 p-2 border border-gray-300 rounded" + /> + +
+
+ ); +} diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx new file mode 100644 index 0000000..0c85383 --- /dev/null +++ b/apps/web/app/test/page.tsx @@ -0,0 +1,48 @@ +import { getSinger } from '@repo/api'; +import { SearchForm } from './SearchForm'; + +// 서버 컴포넌트 (기본적으로 서버에서 실행됨) +export default async function TestPage({ searchParams }: { searchParams: { singer?: string } }) { + // URL 쿼리 파라미터에서 singer 값을 가져옴 + const temp = await searchParams; + console.log('SearchParams', temp); + const singer = await searchParams.singer; + + // 검색어가 있을 때만 API 호출 + let data = null; + let error = null; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; + + if (singer) { + try { + data = await getSinger({ singer }); + // const other = await fetch(`http://localhost:3000/api/songs/singer?singer=아이유`); + const other = await fetch(`${baseUrl}/api/songs/singer?singer=아이유`); + const otherData = await other.json(); + console.log('other : ', otherData); + } catch (err) { + error = err instanceof Error ? err.message : '알 수 없는 오류'; + console.error('API 호출 오류:', err); + } + } + + return ( +
+

가수 검색

+ + {/* 클라이언트 컴포넌트 (사용자 입력 처리) */} + + + {/* 검색 결과 표시 (서버 컴포넌트) */} + {singer && !data && !error &&

'{singer}'에 대한 검색 결과가 없습니다.

} + {error &&

오류: {error}

} + + {data && ( +
+

'{singer}'에 대한 검색 결과:

+
{JSON.stringify(data, null, 2)}
+
+ )} +
+ ); +} diff --git a/apps/web/app/test2/page.tsx b/apps/web/app/test2/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/packages/query/src/useComposer.ts b/packages/query/src/useComposer.ts index ce7d051..3cd9625 100644 --- a/packages/query/src/useComposer.ts +++ b/packages/query/src/useComposer.ts @@ -10,9 +10,12 @@ interface GetComposerProps { const useComposer = (props: GetComposerProps): UseQueryReturn => { const { composer, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['composer', { composer, brand }], - queryFn: () => getComposer(props), + queryKey: ['composer', composer, brandKey], + queryFn: () => getComposer({ composer, brand }), }); return { diff --git a/packages/query/src/useLyricist.ts b/packages/query/src/useLyricist.ts index 031fe92..611342a 100644 --- a/packages/query/src/useLyricist.ts +++ b/packages/query/src/useLyricist.ts @@ -10,9 +10,12 @@ interface GetLyricistProps { const useLyricist = (props: GetLyricistProps): UseQueryReturn => { const { lyricist, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['lyricist', { lyricist, brand }], - queryFn: () => getLyricist(props), + queryKey: ['lyricist', lyricist, brandKey], + queryFn: () => getLyricist({ lyricist, brand }), }); return { diff --git a/packages/query/src/useNo.ts b/packages/query/src/useNo.ts index 153c4b9..321b44b 100644 --- a/packages/query/src/useNo.ts +++ b/packages/query/src/useNo.ts @@ -10,9 +10,12 @@ interface GetNoProps { const useNo = (props: GetNoProps): UseQueryReturn => { const { no, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['no', { no, brand }], - queryFn: () => getNo(props), + queryKey: ['no', no, brandKey], + queryFn: () => getNo({ no, brand }), }); return { diff --git a/packages/query/src/usePopular.ts b/packages/query/src/usePopular.ts index ca782dc..c06059d 100644 --- a/packages/query/src/usePopular.ts +++ b/packages/query/src/usePopular.ts @@ -11,8 +11,9 @@ const usePopular = (props: GetPopularProps): UseQueryReturn => { const { brand, period } = props; const { data, isLoading, isError, error } = useQuery({ - queryKey: ['popular', { brand, period }], - queryFn: () => getPopular(props), + queryKey: ['popular', brand, period], + queryFn: () => getPopular({ brand, period }), + enabled: Boolean(brand) && Boolean(period), }); return { diff --git a/packages/query/src/useRelease.ts b/packages/query/src/useRelease.ts index 56a87a3..0a2f5c6 100644 --- a/packages/query/src/useRelease.ts +++ b/packages/query/src/useRelease.ts @@ -10,9 +10,12 @@ interface GetReleaseProps { const useRelease = (props: GetReleaseProps): UseQueryReturn => { const { release, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['release', { release, brand }], - queryFn: () => getRelease(props), + queryKey: ['release', release, brandKey], + queryFn: () => getRelease({ release, brand }), }); return { diff --git a/packages/query/src/useSinger.ts b/packages/query/src/useSinger.ts index d83c43d..fce97a8 100644 --- a/packages/query/src/useSinger.ts +++ b/packages/query/src/useSinger.ts @@ -10,9 +10,12 @@ interface GetSingerProps { const useSinger = (props: GetSingerProps): UseQueryReturn => { const { singer, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['singer', { singer, brand }], - queryFn: () => getSinger(props), + queryKey: ['singer', singer, brandKey], + queryFn: () => getSinger({ singer, brand }), }); return { diff --git a/packages/query/src/useSong.ts b/packages/query/src/useSong.ts index 6966027..7d2b4a6 100644 --- a/packages/query/src/useSong.ts +++ b/packages/query/src/useSong.ts @@ -10,9 +10,12 @@ interface GetSongProps { const useSong = (props: GetSongProps): UseQueryReturn => { const { title, brand } = props; + // queryKey를 위한 brandKey 생성 (없으면 'all' 사용) + const brandKey = brand || 'all'; + const { data, isLoading, isError, error } = useQuery({ - queryKey: ['song', { title, brand }], - queryFn: () => getSong(props), + queryKey: ['song', title, brandKey], + queryFn: () => getSong({ title, brand }), }); return { From 0325fb74cd9019b81cbbd24d8192702df766c07f Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Tue, 18 Mar 2025 20:13:18 +0900 Subject: [PATCH 05/54] =?UTF-8?q?feat=20:=20route=20api=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD.=20=ED=95=B4=EC=99=B8=EA=B3=A1=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20=EB=B6=88=EA=B0=80=20=EC=9D=B4=EC=8A=88.=20=ED=95=B4?= =?UTF-8?q?=EC=99=B8=EA=B3=A1=20=EA=B3=A0=EB=AF=BC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../songs/[type]/[param]/composer/route.ts | 29 ---------------- .../songs/[type]/[param]/lyricist/route.ts | 29 ---------------- .../app/api/songs/[type]/[param]/no/route.ts | 29 ---------------- .../api/songs/[type]/[param]/popular/route.ts | 33 ------------------- .../api/songs/[type]/[param]/release/route.ts | 29 ---------------- .../api/songs/[type]/[param]/singer/route.ts | 29 ---------------- .../api/songs/[type]/[param]/title/route.ts | 29 ---------------- apps/web/app/test/SearchForm.tsx | 2 +- apps/web/app/test/page.tsx | 22 ++++++++----- 9 files changed, 14 insertions(+), 217 deletions(-) delete mode 100644 apps/web/app/api/songs/[type]/[param]/composer/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/lyricist/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/no/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/popular/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/release/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/singer/route.ts delete mode 100644 apps/web/app/api/songs/[type]/[param]/title/route.ts diff --git a/apps/web/app/api/songs/[type]/[param]/composer/route.ts b/apps/web/app/api/songs/[type]/[param]/composer/route.ts deleted file mode 100644 index 6584228..0000000 --- a/apps/web/app/api/songs/[type]/[param]/composer/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getComposer, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const composer = searchParams.get('composer'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!composer) { - return NextResponse.json({ error: '작곡가 이름(composer)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getComposer({ composer, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts b/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts deleted file mode 100644 index 9bb74c1..0000000 --- a/apps/web/app/api/songs/[type]/[param]/lyricist/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getLyricist, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const lyricist = searchParams.get('lyricist'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!lyricist) { - return NextResponse.json({ error: '작사가 이름(lyricist)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getLyricist({ lyricist, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/no/route.ts b/apps/web/app/api/songs/[type]/[param]/no/route.ts deleted file mode 100644 index 85853bd..0000000 --- a/apps/web/app/api/songs/[type]/[param]/no/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getNo, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const no = searchParams.get('no'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!no) { - return NextResponse.json({ error: '노래 번호(no)는 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getNo({ no, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/popular/route.ts b/apps/web/app/api/songs/[type]/[param]/popular/route.ts deleted file mode 100644 index da16dbf..0000000 --- a/apps/web/app/api/songs/[type]/[param]/popular/route.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getPopular, Brand, Period } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const brand = searchParams.get('brand') as Brand; - const period = searchParams.get('period') as Period; - - // 필수 파라미터 검증 - if (!brand) { - return NextResponse.json({ error: '브랜드(brand)는 필수 파라미터입니다.' }, { status: 400 }); - } - - if (!period) { - return NextResponse.json({ error: '기간(period)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getPopular({ brand, period }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '인기 노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('인기 노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/release/route.ts b/apps/web/app/api/songs/[type]/[param]/release/route.ts deleted file mode 100644 index dbc7721..0000000 --- a/apps/web/app/api/songs/[type]/[param]/release/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getRelease, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const release = searchParams.get('release'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!release) { - return NextResponse.json({ error: '발매일(release)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getRelease({ release, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/singer/route.ts b/apps/web/app/api/songs/[type]/[param]/singer/route.ts deleted file mode 100644 index f5feb1d..0000000 --- a/apps/web/app/api/songs/[type]/[param]/singer/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getSinger, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const singer = searchParams.get('singer'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!singer) { - return NextResponse.json({ error: '가수 이름(singer)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getSinger({ singer, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/api/songs/[type]/[param]/title/route.ts b/apps/web/app/api/songs/[type]/[param]/title/route.ts deleted file mode 100644 index 6ec1641..0000000 --- a/apps/web/app/api/songs/[type]/[param]/title/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getSong, Brand } from '@repo/api'; - -export async function GET(request: NextRequest) { - try { - // URL에서 쿼리 파라미터 추출 - const searchParams = request.nextUrl.searchParams; - const title = searchParams.get('title'); - const brand = searchParams.get('brand') as Brand | undefined; - - // 필수 파라미터 검증 - if (!title) { - return NextResponse.json({ error: '노래 제목(title)은 필수 파라미터입니다.' }, { status: 400 }); - } - - // API 호출 - const result = await getSong({ title, brand }); - - // 결과 반환 - if (!result) { - return NextResponse.json({ error: '노래를 찾을 수 없습니다.' }, { status: 404 }); - } - - return NextResponse.json({ data: result }); - } catch (error) { - console.error('노래 검색 중 오류 발생:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다.' }, { status: 500 }); - } -} diff --git a/apps/web/app/test/SearchForm.tsx b/apps/web/app/test/SearchForm.tsx index b6e102c..2de0661 100644 --- a/apps/web/app/test/SearchForm.tsx +++ b/apps/web/app/test/SearchForm.tsx @@ -16,7 +16,7 @@ export function SearchForm({ initialSinger = '' }: SearchFormProps) { // URL 쿼리 파라미터 업데이트 if (singer) { - router.push(`/test?singer=${encodeURIComponent(singer)}`); + router.push(`/test?search=${encodeURIComponent(singer)}`); } else { router.push('/test'); } diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx index 0c85383..339efc7 100644 --- a/apps/web/app/test/page.tsx +++ b/apps/web/app/test/page.tsx @@ -1,25 +1,29 @@ -import { getSinger } from '@repo/api'; +import { getSinger, getNo, getPopular } from '@repo/api'; import { SearchForm } from './SearchForm'; // 서버 컴포넌트 (기본적으로 서버에서 실행됨) -export default async function TestPage({ searchParams }: { searchParams: { singer?: string } }) { +export default async function TestPage({ searchParams }: { searchParams: { search?: string } }) { // URL 쿼리 파라미터에서 singer 값을 가져옴 const temp = await searchParams; console.log('SearchParams', temp); - const singer = await searchParams.singer; + const search = await searchParams.search; // 검색어가 있을 때만 API 호출 let data = null; let error = null; const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; - if (singer) { + if (search) { try { - data = await getSinger({ singer }); + // TJ, 금영의 경우 해외(일본)곡 검색 못 함 + // 크롤링 돌려서 DB에다가 집어넣어야 할 듯 + data = await getSinger({ singer: search, brand: 'tj' }); + // data = await getPopular({ brand: 'tj', period: 'weekly' }); // const other = await fetch(`http://localhost:3000/api/songs/singer?singer=아이유`); - const other = await fetch(`${baseUrl}/api/songs/singer?singer=아이유`); + const other = await fetch(`${baseUrl}/api/songs/singer/IU?brand=tj`); const otherData = await other.json(); console.log('other : ', otherData); + console.log('datajson : ', JSON.stringify(data, null, 2)); } catch (err) { error = err instanceof Error ? err.message : '알 수 없는 오류'; console.error('API 호출 오류:', err); @@ -31,15 +35,15 @@ export default async function TestPage({ searchParams }: { searchParams: { singe

가수 검색

{/* 클라이언트 컴포넌트 (사용자 입력 처리) */} - + {/* 검색 결과 표시 (서버 컴포넌트) */} - {singer && !data && !error &&

'{singer}'에 대한 검색 결과가 없습니다.

} + {search && !data && !error &&

'{search}'에 대한 검색 결과가 없습니다.

} {error &&

오류: {error}

} {data && (
-

'{singer}'에 대한 검색 결과:

+

'{search}'에 대한 검색 결과:

{JSON.stringify(data, null, 2)}
)} From 2fd39b66f99f07ee4a4dd320ee5ed527063a777b Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Thu, 20 Mar 2025 18:04:20 +0900 Subject: [PATCH 06/54] =?UTF-8?q?feat=20:=20supabase=20=EC=84=B8=ED=8C=85.?= =?UTF-8?q?=20=EB=8F=99=EC=9E=91=20=ED=99=95=EC=9D=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/error.tsx | 11 +++ apps/web/app/error/page.tsx | 5 ++ apps/web/app/login/actions.ts | 46 +++++++++++++ apps/web/app/login/page.tsx | 14 ++++ apps/web/app/supabase/client.ts | 7 ++ apps/web/app/supabase/middleware.ts | 55 +++++++++++++++ apps/web/app/supabase/server.ts | 23 +++++++ apps/web/app/test/page.tsx | 12 ++-- apps/web/app/testing-table/button.tsx | 21 ++++++ apps/web/app/testing-table/page.tsx | 20 ++++++ apps/web/package.json | 2 + apps/web/tsconfig.json | 2 +- pnpm-lock.yaml | 98 +++++++++++++++++++++++++++ 13 files changed, 309 insertions(+), 7 deletions(-) create mode 100644 apps/web/app/error.tsx create mode 100644 apps/web/app/error/page.tsx create mode 100644 apps/web/app/login/actions.ts create mode 100644 apps/web/app/login/page.tsx create mode 100644 apps/web/app/supabase/client.ts create mode 100644 apps/web/app/supabase/middleware.ts create mode 100644 apps/web/app/supabase/server.ts create mode 100644 apps/web/app/testing-table/button.tsx create mode 100644 apps/web/app/testing-table/page.tsx diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx new file mode 100644 index 0000000..6c169ad --- /dev/null +++ b/apps/web/app/error.tsx @@ -0,0 +1,11 @@ +'use client'; + +export default function Error({ error, reset }: { error: Error; reset: () => void }) { + return ( +
+

오류가 발생했습니다.

+

{error.message || '서버에서 오류가 발생했습니다.'}

+ +
+ ); +} diff --git a/apps/web/app/error/page.tsx b/apps/web/app/error/page.tsx new file mode 100644 index 0000000..39e7468 --- /dev/null +++ b/apps/web/app/error/page.tsx @@ -0,0 +1,5 @@ +'use client'; + +export default function ErrorPage() { + return

Sorry, something went wrong

; +} diff --git a/apps/web/app/login/actions.ts b/apps/web/app/login/actions.ts new file mode 100644 index 0000000..9c472b6 --- /dev/null +++ b/apps/web/app/login/actions.ts @@ -0,0 +1,46 @@ +'use server'; + +import { revalidatePath } from 'next/cache'; +import { redirect } from 'next/navigation'; + +import { createClient } from '@/supabase/server'; + +export async function login(formData: FormData) { + const supabase = await createClient(); + + // type-casting here for convenience + // in practice, you should validate your inputs + const data = { + email: formData.get('email') as string, + password: formData.get('password') as string, + }; + + const response = await supabase.auth.signInWithPassword(data); + console.log('response : ', response); + if (response.error) { + redirect('/error'); + } + + revalidatePath('/', 'layout'); + redirect('/'); +} + +export async function register(formData: FormData) { + const supabase = await createClient(); + + // type-casting here for convenience + // in practice, you should validate your inputs + const data = { + email: formData.get('email') as string, + password: formData.get('password') as string, + }; + + const { error } = await supabase.auth.signUp(data); + + if (error) { + redirect('/error'); + } + + revalidatePath('/', 'layout'); + redirect('/'); +} diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx new file mode 100644 index 0000000..ba895e3 --- /dev/null +++ b/apps/web/app/login/page.tsx @@ -0,0 +1,14 @@ +import { login, register } from './actions'; + +export default function LoginPage() { + return ( +
+ + + + + + +
+ ); +} diff --git a/apps/web/app/supabase/client.ts b/apps/web/app/supabase/client.ts new file mode 100644 index 0000000..5bf8381 --- /dev/null +++ b/apps/web/app/supabase/client.ts @@ -0,0 +1,7 @@ +import { createBrowserClient } from '@supabase/ssr'; + +export function createClient() { + // CSR에서는 Next_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY 사용 + + return createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!); +} diff --git a/apps/web/app/supabase/middleware.ts b/apps/web/app/supabase/middleware.ts new file mode 100644 index 0000000..666efe0 --- /dev/null +++ b/apps/web/app/supabase/middleware.ts @@ -0,0 +1,55 @@ +import { createServerClient } from '@supabase/ssr'; +import { NextResponse, type NextRequest } from 'next/server'; + +export async function updateSession(request: NextRequest) { + let supabaseResponse = NextResponse.next({ + request, + }); + + const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { + cookies: { + getAll() { + return request.cookies.getAll(); + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)); + supabaseResponse = NextResponse.next({ + request, + }); + cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options)); + }, + }, + }); + + // Do not run code between createServerClient and + // supabase.auth.getUser(). A simple mistake could make it very hard to debug + // issues with users being randomly logged out. + + // IMPORTANT: DO NOT REMOVE auth.getUser() + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user && !request.nextUrl.pathname.startsWith('/login') && !request.nextUrl.pathname.startsWith('/auth')) { + // no user, potentially respond by redirecting the user to the login page + const url = request.nextUrl.clone(); + url.pathname = '/login'; + return NextResponse.redirect(url); + } + + // IMPORTANT: You *must* return the supabaseResponse object as it is. + // If you're creating a new response object with NextResponse.next() make sure to: + // 1. Pass the request in it, like so: + // const myNewResponse = NextResponse.next({ request }) + // 2. Copy over the cookies, like so: + // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll()) + // 3. Change the myNewResponse object to fit your needs, but avoid changing + // the cookies! + // 4. Finally: + // return myNewResponse + // If this is not done, you may be causing the browser and server to go out + // of sync and terminate the user's session prematurely! + + return supabaseResponse; +} diff --git a/apps/web/app/supabase/server.ts b/apps/web/app/supabase/server.ts new file mode 100644 index 0000000..46bee8b --- /dev/null +++ b/apps/web/app/supabase/server.ts @@ -0,0 +1,23 @@ +import { createServerClient } from '@supabase/ssr'; +import { cookies } from 'next/headers'; + +export async function createClient() { + const cookieStore = await cookies(); + + return createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { + cookies: { + getAll() { + return cookieStore.getAll(); + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); + } catch { + // The `setAll` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } + }, + }, + }); +} diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx index 339efc7..1a33bbe 100644 --- a/apps/web/app/test/page.tsx +++ b/apps/web/app/test/page.tsx @@ -4,9 +4,8 @@ import { SearchForm } from './SearchForm'; // 서버 컴포넌트 (기본적으로 서버에서 실행됨) export default async function TestPage({ searchParams }: { searchParams: { search?: string } }) { // URL 쿼리 파라미터에서 singer 값을 가져옴 - const temp = await searchParams; - console.log('SearchParams', temp); - const search = await searchParams.search; + + const search = searchParams.search; // 검색어가 있을 때만 API 호출 let data = null; @@ -18,11 +17,12 @@ export default async function TestPage({ searchParams }: { searchParams: { searc // TJ, 금영의 경우 해외(일본)곡 검색 못 함 // 크롤링 돌려서 DB에다가 집어넣어야 할 듯 data = await getSinger({ singer: search, brand: 'tj' }); + const other = await getSinger({ singer: search, brand: 'kumyoung' }); // data = await getPopular({ brand: 'tj', period: 'weekly' }); // const other = await fetch(`http://localhost:3000/api/songs/singer?singer=아이유`); - const other = await fetch(`${baseUrl}/api/songs/singer/IU?brand=tj`); - const otherData = await other.json(); - console.log('other : ', otherData); + // const other = await fetch(`${baseUrl}/api/songs/singer/IU?brand=tj`); + // const otherData = await other.json(); + console.log('other : ', other); console.log('datajson : ', JSON.stringify(data, null, 2)); } catch (err) { error = err instanceof Error ? err.message : '알 수 없는 오류'; diff --git a/apps/web/app/testing-table/button.tsx b/apps/web/app/testing-table/button.tsx new file mode 100644 index 0000000..6e62a34 --- /dev/null +++ b/apps/web/app/testing-table/button.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { createClient } from '@/supabase/client'; + +const Button = () => { + const supabase = createClient(); + + const handleInsertData = async () => { + const { data, error } = await supabase.from('test').insert({ name: 'testing' }); + console.log('data : ', data); + console.log('error : ', error); + }; + + return ( + + ); +}; + +export default Button; diff --git a/apps/web/app/testing-table/page.tsx b/apps/web/app/testing-table/page.tsx new file mode 100644 index 0000000..eda9224 --- /dev/null +++ b/apps/web/app/testing-table/page.tsx @@ -0,0 +1,20 @@ +import { createClient } from '@/supabase/server'; +import Button from './button'; + +export default async function Instruments() { + const supabase = await createClient(); + console.log('supabase : ', supabase); + const data = await supabase.from('test').select(); + + const newData = await supabase.from('test').select('*'); + + console.log('data : ', data); + console.log('newData : ', newData); + + return ( +
+      {JSON.stringify(newData, null, 2)}
+      
+ ); +} diff --git a/apps/web/package.json b/apps/web/package.json index 404c34a..12d1fa1 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -12,6 +12,8 @@ "dependencies": { "@repo/api": "workspace:*", "@repo/query": "workspace:*", + "@supabase/ssr": "^0.6.1", + "@supabase/supabase-js": "^2.49.1", "@tanstack/react-query": "^5.68.0", "@tanstack/react-query-devtools": "^5.68.0", "axios": "^1.5.0", diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 8e68406..ba741d9 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -20,7 +20,7 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./app/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dda29cd..60b3b8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,12 @@ importers: '@repo/query': specifier: workspace:* version: link:../../packages/query + '@supabase/ssr': + specifier: ^0.6.1 + version: 0.6.1(@supabase/supabase-js@2.49.1) + '@supabase/supabase-js': + specifier: ^2.49.1 + version: 2.49.1 '@tanstack/react-query': specifier: ^5.68.0 version: 5.68.0(react@19.0.0) @@ -1841,6 +1847,33 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@supabase/auth-js@2.68.0': + resolution: {integrity: sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==} + + '@supabase/functions-js@2.4.4': + resolution: {integrity: sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==} + + '@supabase/node-fetch@2.6.15': + resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} + engines: {node: 4.x || >=6.0.0} + + '@supabase/postgrest-js@1.19.2': + resolution: {integrity: sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==} + + '@supabase/realtime-js@2.11.2': + resolution: {integrity: sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==} + + '@supabase/ssr@0.6.1': + resolution: {integrity: sha512-QtQgEMvaDzr77Mk3vZ3jWg2/y+D8tExYF7vcJT+wQ8ysuvOeGGjYbZlvj5bHYsj/SpC0bihcisnwPrM4Gp5G4g==} + peerDependencies: + '@supabase/supabase-js': ^2.43.4 + + '@supabase/storage-js@2.7.1': + resolution: {integrity: sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==} + + '@supabase/supabase-js@2.49.1': + resolution: {integrity: sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==} + '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -1958,6 +1991,9 @@ packages: '@types/node@22.13.9': resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==} + '@types/phoenix@1.6.6': + resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} + '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} @@ -1987,6 +2023,9 @@ packages: '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/ws@8.18.0': + resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2693,6 +2732,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + core-js-compat@3.41.0: resolution: {integrity: sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==} @@ -8348,6 +8391,53 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@supabase/auth-js@2.68.0': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/functions-js@2.4.4': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/node-fetch@2.6.15': + dependencies: + whatwg-url: 5.0.0 + + '@supabase/postgrest-js@1.19.2': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/realtime-js@2.11.2': + dependencies: + '@supabase/node-fetch': 2.6.15 + '@types/phoenix': 1.6.6 + '@types/ws': 8.18.0 + ws: 8.18.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@supabase/ssr@0.6.1(@supabase/supabase-js@2.49.1)': + dependencies: + '@supabase/supabase-js': 2.49.1 + cookie: 1.0.2 + + '@supabase/storage-js@2.7.1': + dependencies: + '@supabase/node-fetch': 2.6.15 + + '@supabase/supabase-js@2.49.1': + dependencies: + '@supabase/auth-js': 2.68.0 + '@supabase/functions-js': 2.4.4 + '@supabase/node-fetch': 2.6.15 + '@supabase/postgrest-js': 1.19.2 + '@supabase/realtime-js': 2.11.2 + '@supabase/storage-js': 2.7.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@swc/counter@0.1.3': {} '@swc/helpers@0.5.15': @@ -8507,6 +8597,8 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/phoenix@1.6.6': {} + '@types/prop-types@15.7.14': {} '@types/react-dom@19.0.4(@types/react@19.0.10)': @@ -8536,6 +8628,10 @@ snapshots: '@types/tough-cookie@4.0.5': {} + '@types/ws@8.18.0': + dependencies: + '@types/node': 22.13.10 + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.33': @@ -9378,6 +9474,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.0.2: {} + core-js-compat@3.41.0: dependencies: browserslist: 4.24.4 From 961165fbf92068d4d2d43c71daa67c628a05bba6 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Fri, 21 Mar 2025 18:32:46 +0900 Subject: [PATCH 07/54] =?UTF-8?q?feat=20:=20supabase=20auth=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/api/auth/confirm.ts | 45 ++++++++++++++++++++++++++++++++ apps/web/app/error.tsx | 44 +++++++++++++++++++++++++++---- apps/web/app/error/page.tsx | 22 +++++++++++++++- apps/web/app/errorWrapper.tsx | 29 ++++++++++++++++++++ apps/web/app/login/actions.ts | 27 ++++++++++++------- apps/web/app/login/page.tsx | 13 ++++++++- apps/web/app/page.tsx | 21 ++++++++------- apps/web/app/supabase/api.ts | 26 ++++++++++++++++++ apps/web/app/supabase/client.ts | 2 ++ apps/web/app/supabase/server.ts | 1 + apps/web/app/test2/page.tsx | 0 11 files changed, 204 insertions(+), 26 deletions(-) create mode 100644 apps/web/app/api/auth/confirm.ts create mode 100644 apps/web/app/errorWrapper.tsx create mode 100644 apps/web/app/supabase/api.ts delete mode 100644 apps/web/app/test2/page.tsx diff --git a/apps/web/app/api/auth/confirm.ts b/apps/web/app/api/auth/confirm.ts new file mode 100644 index 0000000..41dcdda --- /dev/null +++ b/apps/web/app/api/auth/confirm.ts @@ -0,0 +1,45 @@ +import { type EmailOtpType } from '@supabase/supabase-js'; +import type { NextApiRequest, NextApiResponse } from 'next'; + +import createClient from '@/supabase/api'; + +function stringOrFirstString(item: string | string[] | undefined) { + return Array.isArray(item) ? item[0] : item; +} + +// API Route에서는 throw 대신 적절한 HTTP 응답 필요. 리다이렉팅의 이유? + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'GET') { + res.status(405).appendHeader('Allow', 'GET').end(); + return; + } + + const queryParams = req.query; + const token_hash = stringOrFirstString(queryParams.token_hash); + const type = stringOrFirstString(queryParams.type); + const nextUrl = stringOrFirstString(queryParams.next) || '/'; + + try { + if (!token_hash || !type) { + return res.redirect('/error?message=missing-parameters'); + } + + const supabase = createClient(req, res); + const { error } = await supabase.auth.verifyOtp({ + type: type as EmailOtpType, + token_hash, + }); + + if (error) { + console.error('인증 에러:', error); + return res.redirect(`/error?message=${encodeURIComponent(error.message)}`); + } + + // 성공 시 nextUrl로 리다이렉트 + return res.redirect(nextUrl); + } catch (error) { + console.error('예상치 못한 에러:', error); + return res.redirect('/error?message=unexpected-error'); + } +} diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index 6c169ad..00ea7d3 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -1,11 +1,45 @@ 'use client'; -export default function Error({ error, reset }: { error: Error; reset: () => void }) { +type ErrorPageProps = { + error: Error; + reset: () => void; +}; + +interface AuthError { + code: string; + message: string; + type: string; +} + +export default function Error({ error, reset }: ErrorPageProps) { + const errorMessage = error.message; + let errorDetails: AuthError | null = null; + + // 에러 메시지 파싱 시도 + try { + errorDetails = JSON.parse(error.message) as AuthError; + } catch { + // 파싱 실패 시 기본 메시지 사용 + } + return ( -
-

오류가 발생했습니다.

-

{error.message || '서버에서 오류가 발생했습니다.'}

- +
+

인증 오류

+ + {errorDetails ? ( +
+

에러 코드: {errorDetails.code}

+

{decodeURIComponent(errorDetails.message)}

+ {errorDetails.type === 'access_denied' &&

인증 링크가 만료되었거나 유효하지 않습니다.

} +
+ ) : ( +

{errorMessage || '서버에서 오류가 발생했습니다.'}

+ )} + +
+ + +
); } diff --git a/apps/web/app/error/page.tsx b/apps/web/app/error/page.tsx index 39e7468..f086661 100644 --- a/apps/web/app/error/page.tsx +++ b/apps/web/app/error/page.tsx @@ -1,5 +1,25 @@ 'use client'; +import { useSearchParams } from 'next/navigation'; + export default function ErrorPage() { - return

Sorry, something went wrong

; + const searchParams = useSearchParams(); + const errorMessage = searchParams.get('message'); + + return ( +
+

인증 오류

+
+

{errorMessage ? errorMessage : '알 수 없는 오류가 발생했습니다.'}

+
+ + +
+
+
+ ); } diff --git a/apps/web/app/errorWrapper.tsx b/apps/web/app/errorWrapper.tsx new file mode 100644 index 0000000..1507d08 --- /dev/null +++ b/apps/web/app/errorWrapper.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { useSearchParams } from 'next/navigation'; +import { useEffect } from 'react'; + +export default function ErrorWrapper({ children }: { children: React.ReactNode }) { + const searchParams = useSearchParams(); + + useEffect(() => { + // 에러 파라미터 확인 + const error = searchParams.get('error'); + const errorCode = searchParams.get('error_code'); + const errorDescription = searchParams.get('error_description'); + + if (error) { + // 에러 정보를 구조화 + const errorMessage = { + code: errorCode || 'unknown', + message: errorDescription?.replace(/\+/g, ' ') || '인증 오류가 발생했습니다.', + type: error, + }; + + // 에러를 throw하여 Error Boundary 트리거 + throw new Error(JSON.stringify(errorMessage)); + } + }, [searchParams]); + + return children; +} diff --git a/apps/web/app/login/actions.ts b/apps/web/app/login/actions.ts index 9c472b6..155ff92 100644 --- a/apps/web/app/login/actions.ts +++ b/apps/web/app/login/actions.ts @@ -8,17 +8,19 @@ import { createClient } from '@/supabase/server'; export async function login(formData: FormData) { const supabase = await createClient(); - // type-casting here for convenience - // in practice, you should validate your inputs const data = { email: formData.get('email') as string, password: formData.get('password') as string, }; const response = await supabase.auth.signInWithPassword(data); - console.log('response : ', response); if (response.error) { - redirect('/error'); + throw new Error( + JSON.stringify({ + code: response.error.status || 500, + message: response.error.message, + }), + ); } revalidatePath('/', 'layout'); @@ -28,17 +30,22 @@ export async function login(formData: FormData) { export async function register(formData: FormData) { const supabase = await createClient(); - // type-casting here for convenience - // in practice, you should validate your inputs const data = { email: formData.get('email') as string, password: formData.get('password') as string, }; - const { error } = await supabase.auth.signUp(data); - - if (error) { - redirect('/error'); + const response = await supabase.auth.signUp(data); + console.log('response : ', response); + if (response.error) { + // 에러를 클라이언트에 전달 + + throw new Error( + JSON.stringify({ + code: response.error.status || 500, + message: response.error.message, + }), + ); } revalidatePath('/', 'layout'); diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index ba895e3..b29e998 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -1,6 +1,17 @@ +import { redirect } from 'next/navigation'; +import { createClient } from '@/supabase/server'; import { login, register } from './actions'; -export default function LoginPage() { +export default async function LoginPage() { + const supabase = await createClient(); + + const { data } = await supabase.auth.getUser(); + // data 있으면 라우팅 + if (data) { + console.log('data : ', data); + redirect('/'); + } + return (
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 4860714..20b0dd6 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -2,19 +2,22 @@ // import styles from './page.module.css'; import { useSong } from '@repo/query'; +import ErrorWrapper from '@/errorWrapper'; export default function Home() { const { data } = useSong({ title: '불나방' }); - console.log(data); + console.log('data : ', data); return ( -
-
-

Hello World

-
-
-

fotter

-
-
+ +
+
+

Hello World

+
+
+

fotter

+
+
+
); } diff --git a/apps/web/app/supabase/api.ts b/apps/web/app/supabase/api.ts new file mode 100644 index 0000000..204fcea --- /dev/null +++ b/apps/web/app/supabase/api.ts @@ -0,0 +1,26 @@ +import { createServerClient, serializeCookieHeader } from '@supabase/ssr'; +import { type NextApiRequest, type NextApiResponse } from 'next'; + +// API client + +export default function createClient(req: NextApiRequest, res: NextApiResponse) { + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return Object.keys(req.cookies).map((name) => ({ name, value: req.cookies[name] || '' })); + }, + setAll(cookiesToSet) { + res.setHeader( + 'Set-Cookie', + cookiesToSet.map(({ name, value, options }) => serializeCookieHeader(name, value, options)), + ); + }, + }, + }, + ); + + return supabase; +} diff --git a/apps/web/app/supabase/client.ts b/apps/web/app/supabase/client.ts index 5bf8381..2824ccf 100644 --- a/apps/web/app/supabase/client.ts +++ b/apps/web/app/supabase/client.ts @@ -1,5 +1,7 @@ import { createBrowserClient } from '@supabase/ssr'; +// Component client + export function createClient() { // CSR에서는 Next_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY 사용 diff --git a/apps/web/app/supabase/server.ts b/apps/web/app/supabase/server.ts index 46bee8b..09736eb 100644 --- a/apps/web/app/supabase/server.ts +++ b/apps/web/app/supabase/server.ts @@ -1,6 +1,7 @@ import { createServerClient } from '@supabase/ssr'; import { cookies } from 'next/headers'; +// Server client export async function createClient() { const cookieStore = await cookies(); diff --git a/apps/web/app/test2/page.tsx b/apps/web/app/test2/page.tsx deleted file mode 100644 index e69de29..0000000 From d90e121207e4be14eadaa40cf6f2d915ff0d200c Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Fri, 21 Mar 2025 18:35:19 +0900 Subject: [PATCH 08/54] =?UTF-8?q?feat=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?auth=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/api/auth/callback/route.ts | 30 +++++++++++++++++++++++++ apps/web/app/login/KakaoLoginButton.tsx | 21 +++++++++++++++++ apps/web/app/login/page.tsx | 11 +++++++-- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 apps/web/app/api/auth/callback/route.ts create mode 100644 apps/web/app/login/KakaoLoginButton.tsx diff --git a/apps/web/app/api/auth/callback/route.ts b/apps/web/app/api/auth/callback/route.ts new file mode 100644 index 0000000..e4123b7 --- /dev/null +++ b/apps/web/app/api/auth/callback/route.ts @@ -0,0 +1,30 @@ +import { NextResponse } from 'next/server'; +// The client you created from the Server-Side Auth instructions +import { createClient } from '@/supabase/server'; + +export async function GET(request: Request) { + const { searchParams, origin } = new URL(request.url); + const code = searchParams.get('code'); + // if "next" is in param, use it as the redirect URL + const next = searchParams.get('next') ?? '/'; + + if (code) { + const supabase = await createClient(); + const { error } = await supabase.auth.exchangeCodeForSession(code); + if (!error) { + const forwardedHost = request.headers.get('x-forwarded-host'); // original origin before load balancer + const isLocalEnv = process.env.NODE_ENV === 'development'; + if (isLocalEnv) { + // we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host + return NextResponse.redirect(`${origin}${next}`); + } else if (forwardedHost) { + return NextResponse.redirect(`https://${forwardedHost}${next}`); + } else { + return NextResponse.redirect(`${origin}${next}`); + } + } + } + + // return the user to an error page with instructions + return NextResponse.redirect(`${origin}/auth/auth-code-error`); +} diff --git a/apps/web/app/login/KakaoLoginButton.tsx b/apps/web/app/login/KakaoLoginButton.tsx new file mode 100644 index 0000000..a5825f7 --- /dev/null +++ b/apps/web/app/login/KakaoLoginButton.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { createClient } from '@/supabase/client'; // 클라이언트용 Supabase 클라이언트 + +export default function KakaoLoginButton() { + const handleKakaoLogin = async () => { + const supabase = createClient(); + const { data, error } = await supabase.auth.signInWithOAuth({ + provider: 'kakao', + }); + + console.log('data : ', data); + console.log('error : ', error); + }; + + return ( + + ); +} diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index b29e998..3651e75 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -1,17 +1,23 @@ import { redirect } from 'next/navigation'; import { createClient } from '@/supabase/server'; import { login, register } from './actions'; +import KakaoLoginButton from './KakaoLoginButton'; // 새로운 클라이언트 컴포넌트 export default async function LoginPage() { const supabase = await createClient(); const { data } = await supabase.auth.getUser(); - // data 있으면 라우팅 + if (data) { console.log('data : ', data); - redirect('/'); + // redirect('/'); } + // const kakao_client_id = process.env.KAKAO_REST_API_KEY; + // const kakao_redirect_uri = process.env.KAKAO_REDIRECT_URI; + + // const kakao_url = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${kakao_client_id}&redirect_uri=${kakao_redirect_uri}`; + return ( @@ -20,6 +26,7 @@ export default async function LoginPage() { + ); } From 1f62268f79f646d18140908817a0ab99ab2bdc0e Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Sun, 23 Mar 2025 19:09:30 +0900 Subject: [PATCH 09/54] =?UTF-8?q?feat=20:=20tailwind,=20prettier=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/mobile/app/(tabs)/_layout.tsx | 3 +- apps/mobile/app/(tabs)/explore.tsx | 31 +- apps/mobile/app/(tabs)/index.tsx | 20 +- apps/mobile/components/Collapsible.tsx | 5 +- apps/mobile/components/HelloWave.tsx | 2 +- apps/mobile/components/ParallaxScrollView.tsx | 25 +- apps/mobile/components/ThemedText.tsx | 8 +- apps/mobile/components/ui/IconSymbol.tsx | 5 +- apps/mobile/hooks/useThemeColor.ts | 2 +- apps/mobile/scripts/reset-project.js | 53 +- apps/web/.prettierrc.cjs | 12 + apps/web/app/api/auth/callback/route.ts | 26 +- apps/web/app/api/auth/confirm.ts | 36 +- .../web/app/api/songs/[type]/[param]/route.ts | 72 ++- apps/web/app/error.tsx | 26 +- apps/web/app/error/page.tsx | 14 +- apps/web/app/errorWrapper.tsx | 22 +- apps/web/app/find/page.tsx | 14 +- apps/web/app/globals.css | 158 +++++- apps/web/app/layout.tsx | 19 +- apps/web/app/login/KakaoLoginButton.tsx | 16 +- apps/web/app/login/actions.ts | 34 +- apps/web/app/login/page.tsx | 21 +- apps/web/app/page.tsx | 17 +- apps/web/app/query.tsx | 14 +- apps/web/app/supabase/api.ts | 19 +- apps/web/app/supabase/client.ts | 7 +- apps/web/app/supabase/middleware.ts | 34 +- apps/web/app/supabase/server.ts | 12 +- apps/web/app/test/SearchForm.tsx | 31 +- apps/web/app/test/page.tsx | 26 +- apps/web/app/testing-table/button.tsx | 20 +- apps/web/app/testing-table/page.tsx | 18 +- apps/web/eslint.config.mjs | 18 +- apps/web/next.config.ts | 6 +- apps/web/package.json | 7 +- apps/web/postcss.config.mjs | 6 + package.json | 3 +- packages/eslint-config/base.js | 14 +- packages/eslint-config/next.js | 26 +- packages/eslint-config/react-internal.js | 20 +- packages/ui/eslint.config.mjs | 2 +- packages/ui/src/button.tsx | 9 +- packages/ui/src/card.tsx | 2 +- packages/ui/src/code.tsx | 10 +- packages/ui/turbo/generators/config.ts | 22 +- .../turbo/generators/templates/component.hbs | 16 +- pnpm-lock.yaml | 525 +++++++++++++++--- pnpm-workspace.yaml | 4 +- turbo.json | 3 + 50 files changed, 1005 insertions(+), 510 deletions(-) create mode 100644 apps/web/.prettierrc.cjs create mode 100644 apps/web/postcss.config.mjs diff --git a/apps/mobile/app/(tabs)/_layout.tsx b/apps/mobile/app/(tabs)/_layout.tsx index cfbc1e2..6ff9b73 100644 --- a/apps/mobile/app/(tabs)/_layout.tsx +++ b/apps/mobile/app/(tabs)/_layout.tsx @@ -25,7 +25,8 @@ export default function TabLayout() { }, default: {}, }), - }}> + }} + > - }> + } + > Explore This app includes example code to help you get started. - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} + This app has two screens: app/(tabs)/index.tsx and{' '} app/(tabs)/explore.tsx - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. + The layout file in app/(tabs)/_layout.tsx sets up the tab + navigator. Learn more @@ -46,8 +46,7 @@ export default function TabTwoScreen() { For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities + @3x suffixes to provide files for different screen densities @@ -57,9 +56,7 @@ export default function TabTwoScreen() { Open app/_layout.tsx to see how to load{' '} - - custom fonts such as this one. - + custom fonts such as this one. Learn more @@ -68,8 +65,8 @@ export default function TabTwoScreen() { This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. + useColorScheme() hook lets you inspect what the user's current + color scheme is, and so you can adjust UI colors accordingly. Learn more @@ -78,15 +75,15 @@ export default function TabTwoScreen() { This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful react-native-reanimated{' '} - library to create a waving hand animation. + components/HelloWave.tsx component uses the powerful{' '} + react-native-reanimated library to create a waving hand + animation. {Platform.select({ ios: ( - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. + The components/ParallaxScrollView.tsx component provides a + parallax effect for the header image. ), })} diff --git a/apps/mobile/app/(tabs)/index.tsx b/apps/mobile/app/(tabs)/index.tsx index 886b079..51a2187 100644 --- a/apps/mobile/app/(tabs)/index.tsx +++ b/apps/mobile/app/(tabs)/index.tsx @@ -9,12 +9,8 @@ export default function HomeScreen() { return ( - }> + headerImage={} + > Welcome! @@ -22,13 +18,12 @@ export default function HomeScreen() { Step 1: Try it - Edit app/(tabs)/index.tsx to see changes. - Press{' '} + Edit app/(tabs)/index.tsx to see changes. Press{' '} {Platform.select({ ios: 'cmd + d', android: 'cmd + m', - web: 'F12' + web: 'F12', })} {' '} to open developer tools. @@ -36,15 +31,12 @@ export default function HomeScreen() { Step 2: Explore - - Tap the Explore tab to learn more about what's included in this starter app. - + Tap the Explore tab to learn more about what's included in this starter app. Step 3: Get a fresh start - When you're ready, run{' '} - npm run reset-project to get a fresh{' '} + When you're ready, run npm run reset-project to get a fresh{' '} app directory. This will move the current{' '} app to{' '} app-example. diff --git a/apps/mobile/components/Collapsible.tsx b/apps/mobile/components/Collapsible.tsx index 55bff2f..4eb12e2 100644 --- a/apps/mobile/components/Collapsible.tsx +++ b/apps/mobile/components/Collapsible.tsx @@ -13,10 +13,7 @@ export function Collapsible({ children, title }: PropsWithChildren & { title: st return ( - setIsOpen((value) => !value)} - activeOpacity={0.8}> + setIsOpen((value) => !value)} activeOpacity={0.8}> { rotationAnimation.value = withRepeat( withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })), - 4 // Run the animation 4 times + 4, // Run the animation 4 times ); }, []); diff --git a/apps/mobile/components/ParallaxScrollView.tsx b/apps/mobile/components/ParallaxScrollView.tsx index 5df1d75..eeb3891 100644 --- a/apps/mobile/components/ParallaxScrollView.tsx +++ b/apps/mobile/components/ParallaxScrollView.tsx @@ -1,11 +1,6 @@ import type { PropsWithChildren, ReactElement } from 'react'; import { StyleSheet } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollViewOffset, -} from 'react-native-reanimated'; +import Animated, { interpolate, useAnimatedRef, useAnimatedStyle, useScrollViewOffset } from 'react-native-reanimated'; import { ThemedView } from '@/components/ThemedView'; import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; @@ -18,11 +13,7 @@ type Props = PropsWithChildren<{ headerBackgroundColor: { dark: string; light: string }; }>; -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { +export default function ParallaxScrollView({ children, headerImage, headerBackgroundColor }: Props) { const colorScheme = useColorScheme() ?? 'light'; const scrollRef = useAnimatedRef(); const scrollOffset = useScrollViewOffset(scrollRef); @@ -34,7 +25,7 @@ export default function ParallaxScrollView({ translateY: interpolate( scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] + [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75], ), }, { @@ -50,13 +41,11 @@ export default function ParallaxScrollView({ ref={scrollRef} scrollEventThrottle={16} scrollIndicatorInsets={{ bottom }} - contentContainerStyle={{ paddingBottom: bottom }}> + contentContainerStyle={{ paddingBottom: bottom }} + > + style={[styles.header, { backgroundColor: headerBackgroundColor[colorScheme] }, headerAnimatedStyle]} + > {headerImage} {children} diff --git a/apps/mobile/components/ThemedText.tsx b/apps/mobile/components/ThemedText.tsx index c0e1a78..c8b4931 100644 --- a/apps/mobile/components/ThemedText.tsx +++ b/apps/mobile/components/ThemedText.tsx @@ -8,13 +8,7 @@ export type ThemedTextProps = TextProps & { type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; }; -export function ThemedText({ - style, - lightColor, - darkColor, - type = 'default', - ...rest -}: ThemedTextProps) { +export function ThemedText({ style, lightColor, darkColor, type = 'default', ...rest }: ThemedTextProps) { const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); return ( diff --git a/apps/mobile/components/ui/IconSymbol.tsx b/apps/mobile/components/ui/IconSymbol.tsx index f1fabd4..91282bd 100644 --- a/apps/mobile/components/ui/IconSymbol.tsx +++ b/apps/mobile/components/ui/IconSymbol.tsx @@ -14,10 +14,7 @@ const MAPPING = { 'chevron.left.forwardslash.chevron.right': 'code', 'chevron.right': 'chevron-right', } as Partial< - Record< - import('expo-symbols').SymbolViewProps['name'], - React.ComponentProps['name'] - > + Record['name']> >; export type IconSymbolName = keyof typeof MAPPING; diff --git a/apps/mobile/hooks/useThemeColor.ts b/apps/mobile/hooks/useThemeColor.ts index 0608e73..08a3019 100644 --- a/apps/mobile/hooks/useThemeColor.ts +++ b/apps/mobile/hooks/useThemeColor.ts @@ -8,7 +8,7 @@ import { useColorScheme } from '@/hooks/useColorScheme'; export function useThemeColor( props: { light?: string; dark?: string }, - colorName: keyof typeof Colors.light & keyof typeof Colors.dark + colorName: keyof typeof Colors.light & keyof typeof Colors.dark, ) { const theme = useColorScheme() ?? 'light'; const colorFromProps = props[theme]; diff --git a/apps/mobile/scripts/reset-project.js b/apps/mobile/scripts/reset-project.js index 51dff15..59665be 100644 --- a/apps/mobile/scripts/reset-project.js +++ b/apps/mobile/scripts/reset-project.js @@ -6,14 +6,14 @@ * You can remove the `reset-project` script from package.json and safely delete this file after running it. */ -const fs = require("fs"); -const path = require("path"); -const readline = require("readline"); +const fs = require('fs'); +const path = require('path'); +const readline = require('readline'); const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const exampleDir = "app-example"; -const newAppDir = "app"; +const oldDirs = ['app', 'components', 'hooks', 'constants', 'scripts']; +const exampleDir = 'app-example'; +const newAppDir = 'app'; const exampleDirPath = path.join(root, exampleDir); const indexContent = `import { Text, View } from "react-native"; @@ -47,7 +47,7 @@ const rl = readline.createInterface({ const moveDirectories = async (userInput) => { try { - if (userInput === "y") { + if (userInput === 'y') { // Create the app-example directory await fs.promises.mkdir(exampleDirPath, { recursive: true }); console.log(`📁 /${exampleDir} directory created.`); @@ -57,7 +57,7 @@ const moveDirectories = async (userInput) => { for (const dir of oldDirs) { const oldDirPath = path.join(root, dir); if (fs.existsSync(oldDirPath)) { - if (userInput === "y") { + if (userInput === 'y') { const newDirPath = path.join(root, exampleDir, dir); await fs.promises.rename(oldDirPath, newDirPath); console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); @@ -73,40 +73,35 @@ const moveDirectories = async (userInput) => { // Create new /app directory const newAppDirPath = path.join(root, newAppDir); await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\n📁 New /app directory created."); + console.log('\n📁 New /app directory created.'); // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); + const indexPath = path.join(newAppDirPath, 'index.tsx'); await fs.promises.writeFile(indexPath, indexContent); - console.log("📄 app/index.tsx created."); + console.log('📄 app/index.tsx created.'); // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); + const layoutPath = path.join(newAppDirPath, '_layout.tsx'); await fs.promises.writeFile(layoutPath, layoutContent); - console.log("📄 app/_layout.tsx created."); + console.log('📄 app/_layout.tsx created.'); - console.log("\n✅ Project reset complete. Next steps:"); + console.log('\n✅ Project reset complete. Next steps:'); console.log( `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ - userInput === "y" - ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` - : "" - }` + userInput === 'y' ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` : '' + }`, ); } catch (error) { console.error(`❌ Error during script execution: ${error.message}`); } }; -rl.question( - "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", - (answer) => { - const userInput = answer.trim().toLowerCase() || "y"; - if (userInput === "y" || userInput === "n") { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("❌ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } +rl.question('Do you want to move existing files to /app-example instead of deleting them? (Y/n): ', (answer) => { + const userInput = answer.trim().toLowerCase() || 'y'; + if (userInput === 'y' || userInput === 'n') { + moveDirectories(userInput).finally(() => rl.close()); + } else { + console.log("❌ Invalid input. Please enter 'Y' or 'N'."); + rl.close(); } -); +}); diff --git a/apps/web/.prettierrc.cjs b/apps/web/.prettierrc.cjs new file mode 100644 index 0000000..df3725d --- /dev/null +++ b/apps/web/.prettierrc.cjs @@ -0,0 +1,12 @@ +module.exports = { + plugins: ['prettier-plugin-tailwindcss'], // Tailwind 클래스 정렬 (문자열로 변경) + printWidth: 100, // 한 줄 최대 길이 + tabWidth: 2, // 탭 크기 (스페이스 2칸) + singleQuote: true, // 작은따옴표 사용 + trailingComma: 'all', // 여러 줄일 때 항상 쉼표 사용 + arrowParens: 'avoid', // 화살표 함수 괄호 생략 (ex: x => x) + semi: false, // 세미콜론 사용 안 함 + bracketSpacing: true, // 중괄호 간격 유지 (ex: { foo: bar }) + jsxSingleQuote: false, // JSX에서 작은따옴표 사용 안 함 + endOfLine: 'lf', // 줄바꿈 형식 (LF 고정) +} diff --git a/apps/web/app/api/auth/callback/route.ts b/apps/web/app/api/auth/callback/route.ts index e4123b7..b8b9ac3 100644 --- a/apps/web/app/api/auth/callback/route.ts +++ b/apps/web/app/api/auth/callback/route.ts @@ -1,30 +1,30 @@ -import { NextResponse } from 'next/server'; +import { NextResponse } from 'next/server' // The client you created from the Server-Side Auth instructions -import { createClient } from '@/supabase/server'; +import { createClient } from '@/supabase/server' export async function GET(request: Request) { - const { searchParams, origin } = new URL(request.url); - const code = searchParams.get('code'); + const { searchParams, origin } = new URL(request.url) + const code = searchParams.get('code') // if "next" is in param, use it as the redirect URL - const next = searchParams.get('next') ?? '/'; + const next = searchParams.get('next') ?? '/' if (code) { - const supabase = await createClient(); - const { error } = await supabase.auth.exchangeCodeForSession(code); + const supabase = await createClient() + const { error } = await supabase.auth.exchangeCodeForSession(code) if (!error) { - const forwardedHost = request.headers.get('x-forwarded-host'); // original origin before load balancer - const isLocalEnv = process.env.NODE_ENV === 'development'; + const forwardedHost = request.headers.get('x-forwarded-host') // original origin before load balancer + const isLocalEnv = process.env.NODE_ENV === 'development' if (isLocalEnv) { // we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host - return NextResponse.redirect(`${origin}${next}`); + return NextResponse.redirect(`${origin}${next}`) } else if (forwardedHost) { - return NextResponse.redirect(`https://${forwardedHost}${next}`); + return NextResponse.redirect(`https://${forwardedHost}${next}`) } else { - return NextResponse.redirect(`${origin}${next}`); + return NextResponse.redirect(`${origin}${next}`) } } } // return the user to an error page with instructions - return NextResponse.redirect(`${origin}/auth/auth-code-error`); + return NextResponse.redirect(`${origin}/auth/auth-code-error`) } diff --git a/apps/web/app/api/auth/confirm.ts b/apps/web/app/api/auth/confirm.ts index 41dcdda..c018367 100644 --- a/apps/web/app/api/auth/confirm.ts +++ b/apps/web/app/api/auth/confirm.ts @@ -1,45 +1,45 @@ -import { type EmailOtpType } from '@supabase/supabase-js'; -import type { NextApiRequest, NextApiResponse } from 'next'; +import { type EmailOtpType } from '@supabase/supabase-js' +import type { NextApiRequest, NextApiResponse } from 'next' -import createClient from '@/supabase/api'; +import createClient from '@/supabase/api' function stringOrFirstString(item: string | string[] | undefined) { - return Array.isArray(item) ? item[0] : item; + return Array.isArray(item) ? item[0] : item } // API Route에서는 throw 대신 적절한 HTTP 응답 필요. 리다이렉팅의 이유? export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'GET') { - res.status(405).appendHeader('Allow', 'GET').end(); - return; + res.status(405).appendHeader('Allow', 'GET').end() + return } - const queryParams = req.query; - const token_hash = stringOrFirstString(queryParams.token_hash); - const type = stringOrFirstString(queryParams.type); - const nextUrl = stringOrFirstString(queryParams.next) || '/'; + const queryParams = req.query + const token_hash = stringOrFirstString(queryParams.token_hash) + const type = stringOrFirstString(queryParams.type) + const nextUrl = stringOrFirstString(queryParams.next) || '/' try { if (!token_hash || !type) { - return res.redirect('/error?message=missing-parameters'); + return res.redirect('/error?message=missing-parameters') } - const supabase = createClient(req, res); + const supabase = createClient(req, res) const { error } = await supabase.auth.verifyOtp({ type: type as EmailOtpType, token_hash, - }); + }) if (error) { - console.error('인증 에러:', error); - return res.redirect(`/error?message=${encodeURIComponent(error.message)}`); + console.error('인증 에러:', error) + return res.redirect(`/error?message=${encodeURIComponent(error.message)}`) } // 성공 시 nextUrl로 리다이렉트 - return res.redirect(nextUrl); + return res.redirect(nextUrl) } catch (error) { - console.error('예상치 못한 에러:', error); - return res.redirect('/error?message=unexpected-error'); + console.error('예상치 못한 에러:', error) + return res.redirect('/error?message=unexpected-error') } } diff --git a/apps/web/app/api/songs/[type]/[param]/route.ts b/apps/web/app/api/songs/[type]/[param]/route.ts index 05c14fc..9ea1a3c 100644 --- a/apps/web/app/api/songs/[type]/[param]/route.ts +++ b/apps/web/app/api/songs/[type]/[param]/route.ts @@ -1,61 +1,77 @@ // app/api/songs/[type]/[param]/route.ts -import { NextRequest, NextResponse } from 'next/server'; -import { getSong, getSinger, getComposer, getLyricist, getNo, getRelease, getPopular, Brand, Period } from '@repo/api'; +import { NextRequest, NextResponse } from 'next/server' +import { + getSong, + getSinger, + getComposer, + getLyricist, + getNo, + getRelease, + getPopular, + Brand, + Period, +} from '@repo/api' -export async function GET(request: NextRequest, { params }: { params: { type: string; param: string } }) { +export async function GET( + request: NextRequest, + { params }: { params: { type: string; param: string } }, +) { try { - const { type, param } = params; - const searchParams = request.nextUrl.searchParams; - const brand = searchParams.get('brand') as Brand | undefined; + const { type, param } = params + const searchParams = request.nextUrl.searchParams + const brand = searchParams.get('brand') as Brand | undefined - let result = null; + let result = null switch (type) { case 'title': - result = await getSong({ title: param, brand }); - break; + result = await getSong({ title: param, brand }) + break case 'singer': - result = await getSinger({ singer: param, brand }); - break; + result = await getSinger({ singer: param, brand }) + break case 'composer': - result = await getComposer({ composer: param, brand }); - break; + result = await getComposer({ composer: param, brand }) + break case 'lyricist': - result = await getLyricist({ lyricist: param, brand }); - break; + result = await getLyricist({ lyricist: param, brand }) + break case 'no': - result = await getNo({ no: param, brand }); - break; + result = await getNo({ no: param, brand }) + break case 'release': - result = await getRelease({ release: param, brand }); - break; + result = await getRelease({ release: param, brand }) + break case 'popular': // popular의 경우는 좀 특별하게 처리 // param은 brand 값이 되고, period는 쿼리 파라미터로 받음 - const period = searchParams.get('period') as Period; + const period = searchParams.get('period') as Period if (!period) { - return NextResponse.json({ error: '기간(period)은 필수 파라미터입니다.' }, { status: 400 }); + return NextResponse.json( + { error: '기간(period)은 필수 파라미터입니다.' }, + { status: 400 }, + ) } - result = await getPopular({ brand: param as Brand, period }); - break; + result = await getPopular({ brand: param as Brand, period }) + break default: - return NextResponse.json({ error: '지원하지 않는 검색 유형입니다' }, { status: 400 }); + return NextResponse.json({ error: '지원하지 않는 검색 유형입니다' }, { status: 400 }) } if (!result) { - return NextResponse.json({ error: '검색 결과가 없습니다' }, { status: 404 }); + return NextResponse.json({ error: '검색 결과가 없습니다' }, { status: 404 }) } - return NextResponse.json({ data: result }); + return NextResponse.json({ data: result }) } catch (error) { - console.error('API 요청 오류:', error); - return NextResponse.json({ error: '서버 오류가 발생했습니다' }, { status: 500 }); + console.error('API 요청 오류:', error) + return NextResponse.json({ error: '서버 오류가 발생했습니다' }, { status: 500 }) } } diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index 00ea7d3..169dcca 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -1,23 +1,23 @@ -'use client'; +'use client' type ErrorPageProps = { - error: Error; - reset: () => void; -}; + error: Error + reset: () => void +} interface AuthError { - code: string; - message: string; - type: string; + code: string + message: string + type: string } export default function Error({ error, reset }: ErrorPageProps) { - const errorMessage = error.message; - let errorDetails: AuthError | null = null; + const errorMessage = error.message + let errorDetails: AuthError | null = null // 에러 메시지 파싱 시도 try { - errorDetails = JSON.parse(error.message) as AuthError; + errorDetails = JSON.parse(error.message) as AuthError } catch { // 파싱 실패 시 기본 메시지 사용 } @@ -30,7 +30,9 @@ export default function Error({ error, reset }: ErrorPageProps) {

에러 코드: {errorDetails.code}

{decodeURIComponent(errorDetails.message)}

- {errorDetails.type === 'access_denied' &&

인증 링크가 만료되었거나 유효하지 않습니다.

} + {errorDetails.type === 'access_denied' && ( +

인증 링크가 만료되었거나 유효하지 않습니다.

+ )}
) : (

{errorMessage || '서버에서 오류가 발생했습니다.'}

@@ -41,5 +43,5 @@ export default function Error({ error, reset }: ErrorPageProps) {
- ); + ) } diff --git a/apps/web/app/error/page.tsx b/apps/web/app/error/page.tsx index f086661..f4fa6df 100644 --- a/apps/web/app/error/page.tsx +++ b/apps/web/app/error/page.tsx @@ -1,16 +1,18 @@ -'use client'; +'use client' -import { useSearchParams } from 'next/navigation'; +import { useSearchParams } from 'next/navigation' export default function ErrorPage() { - const searchParams = useSearchParams(); - const errorMessage = searchParams.get('message'); + const searchParams = useSearchParams() + const errorMessage = searchParams.get('message') return (

인증 오류

-

{errorMessage ? errorMessage : '알 수 없는 오류가 발생했습니다.'}

+

+ {errorMessage ? errorMessage : '알 수 없는 오류가 발생했습니다.'} +

- ); + ) } diff --git a/apps/web/app/errorWrapper.tsx b/apps/web/app/errorWrapper.tsx index 1507d08..58c3a2a 100644 --- a/apps/web/app/errorWrapper.tsx +++ b/apps/web/app/errorWrapper.tsx @@ -1,16 +1,16 @@ -'use client'; +'use client' -import { useSearchParams } from 'next/navigation'; -import { useEffect } from 'react'; +import { useSearchParams } from 'next/navigation' +import { useEffect } from 'react' export default function ErrorWrapper({ children }: { children: React.ReactNode }) { - const searchParams = useSearchParams(); + const searchParams = useSearchParams() useEffect(() => { // 에러 파라미터 확인 - const error = searchParams.get('error'); - const errorCode = searchParams.get('error_code'); - const errorDescription = searchParams.get('error_description'); + const error = searchParams.get('error') + const errorCode = searchParams.get('error_code') + const errorDescription = searchParams.get('error_description') if (error) { // 에러 정보를 구조화 @@ -18,12 +18,12 @@ export default function ErrorWrapper({ children }: { children: React.ReactNode } code: errorCode || 'unknown', message: errorDescription?.replace(/\+/g, ' ') || '인증 오류가 발생했습니다.', type: error, - }; + } // 에러를 throw하여 Error Boundary 트리거 - throw new Error(JSON.stringify(errorMessage)); + throw new Error(JSON.stringify(errorMessage)) } - }, [searchParams]); + }, [searchParams]) - return children; + return children } diff --git a/apps/web/app/find/page.tsx b/apps/web/app/find/page.tsx index 1ce90b9..e67b130 100644 --- a/apps/web/app/find/page.tsx +++ b/apps/web/app/find/page.tsx @@ -1,14 +1,14 @@ -'use client'; +'use client' -import { getComposer } from '@repo/api'; -import { useState } from 'react'; +import { getComposer } from '@repo/api' +import { useState } from 'react' export default function Home() { - const [search, setSearch] = useState(''); + const [search, setSearch] = useState('') const handleSearch = (e: React.ChangeEvent) => { - setSearch(e.target.value); - }; + setSearch(e.target.value) + } return (
@@ -17,5 +17,5 @@ export default function Home() {

fotter

- ); + ) } diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index e3734be..336d113 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -1,33 +1,137 @@ -:root { - --background: #ffffff; - --foreground: #171717; -} +@import 'tailwindcss'; -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } +@theme { + --color-background: #121212; + --color-primary: #1db954; + --color-secondary: #191414; + --color-text-primary: #ffffff; + --color-text-secondary: #b3b3b3; + --color-text-accent: #1ed760; } html, -body { - max-width: 100vw; - overflow-x: hidden; +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, +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; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; } - body { - color: var(--foreground); - background: var(--background); - font-family: Arial, Helvetica, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + line-height: 1; } - -* { - box-sizing: border-box; - padding: 0; - margin: 0; +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; } a { @@ -35,8 +139,8 @@ a { text-decoration: none; } -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } +html, +body { + max-width: 100vw; + overflow-x: hidden; } diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index b54140f..2ee4990 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,25 +1,22 @@ -import type { Metadata } from 'next'; -import QueryProvider from './query'; - -// import { Geist, Geist_Mono } from "next/font/google"; -// import "./globals.css"; +import type { Metadata } from 'next' +import QueryProvider from './query' +import './globals.css' export const metadata: Metadata = { title: 'Singcode', description: 'Singcode', -}; +} export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { return ( - - {/* */} - + + {children} - ); + ) } diff --git a/apps/web/app/login/KakaoLoginButton.tsx b/apps/web/app/login/KakaoLoginButton.tsx index a5825f7..4fec03f 100644 --- a/apps/web/app/login/KakaoLoginButton.tsx +++ b/apps/web/app/login/KakaoLoginButton.tsx @@ -1,21 +1,21 @@ -'use client'; +'use client' -import { createClient } from '@/supabase/client'; // 클라이언트용 Supabase 클라이언트 +import { createClient } from '@/supabase/client' // 클라이언트용 Supabase 클라이언트 export default function KakaoLoginButton() { const handleKakaoLogin = async () => { - const supabase = createClient(); + const supabase = createClient() const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'kakao', - }); + }) - console.log('data : ', data); - console.log('error : ', error); - }; + console.log('data : ', data) + console.log('error : ', error) + } return ( - ); + ) } diff --git a/apps/web/app/login/actions.ts b/apps/web/app/login/actions.ts index 155ff92..912c928 100644 --- a/apps/web/app/login/actions.ts +++ b/apps/web/app/login/actions.ts @@ -1,42 +1,42 @@ -'use server'; +'use server' -import { revalidatePath } from 'next/cache'; -import { redirect } from 'next/navigation'; +import { revalidatePath } from 'next/cache' +import { redirect } from 'next/navigation' -import { createClient } from '@/supabase/server'; +import { createClient } from '@/supabase/server' export async function login(formData: FormData) { - const supabase = await createClient(); + const supabase = await createClient() const data = { email: formData.get('email') as string, password: formData.get('password') as string, - }; + } - const response = await supabase.auth.signInWithPassword(data); + const response = await supabase.auth.signInWithPassword(data) if (response.error) { throw new Error( JSON.stringify({ code: response.error.status || 500, message: response.error.message, }), - ); + ) } - revalidatePath('/', 'layout'); - redirect('/'); + revalidatePath('/', 'layout') + redirect('/') } export async function register(formData: FormData) { - const supabase = await createClient(); + const supabase = await createClient() const data = { email: formData.get('email') as string, password: formData.get('password') as string, - }; + } - const response = await supabase.auth.signUp(data); - console.log('response : ', response); + const response = await supabase.auth.signUp(data) + console.log('response : ', response) if (response.error) { // 에러를 클라이언트에 전달 @@ -45,9 +45,9 @@ export async function register(formData: FormData) { code: response.error.status || 500, message: response.error.message, }), - ); + ) } - revalidatePath('/', 'layout'); - redirect('/'); + revalidatePath('/', 'layout') + redirect('/') } diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index 3651e75..9ee21a5 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -1,23 +1,18 @@ -import { redirect } from 'next/navigation'; -import { createClient } from '@/supabase/server'; -import { login, register } from './actions'; -import KakaoLoginButton from './KakaoLoginButton'; // 새로운 클라이언트 컴포넌트 +import { redirect } from 'next/navigation' +import { createClient } from '@/supabase/server' +import { login, register } from './actions' +import KakaoLoginButton from './KakaoLoginButton' // 새로운 클라이언트 컴포넌트 export default async function LoginPage() { - const supabase = await createClient(); + const supabase = await createClient() - const { data } = await supabase.auth.getUser(); + const { data } = await supabase.auth.getUser() if (data) { - console.log('data : ', data); + console.log('data : ', data) // redirect('/'); } - // const kakao_client_id = process.env.KAKAO_REST_API_KEY; - // const kakao_redirect_uri = process.env.KAKAO_REDIRECT_URI; - - // const kakao_url = `https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=${kakao_client_id}&redirect_uri=${kakao_redirect_uri}`; - return (
@@ -28,5 +23,5 @@ export default async function LoginPage() { - ); + ) } diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 20b0dd6..8fa97d3 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,23 +1,24 @@ -'use client'; -// import styles from './page.module.css'; +'use client' -import { useSong } from '@repo/query'; -import ErrorWrapper from '@/errorWrapper'; +import { useSong } from '@repo/query' +import ErrorWrapper from '@/errorWrapper' export default function Home() { - const { data } = useSong({ title: '불나방' }); - console.log('data : ', data); + const { data } = useSong({ title: '불나방' }) + console.log('data : ', data) return (
-
+

Hello World

+

Hello world!

+

fotter

- ); + ) } diff --git a/apps/web/app/query.tsx b/apps/web/app/query.tsx index f38e2f9..4cc5c73 100644 --- a/apps/web/app/query.tsx +++ b/apps/web/app/query.tsx @@ -1,11 +1,11 @@ -'use client'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { useState } from 'react'; +'use client' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { useState } from 'react' const QueryProvider = ({ children }: { children: React.ReactNode }) => { - const [queryClient] = useState(() => new QueryClient()); + const [queryClient] = useState(() => new QueryClient()) - return {children}; -}; + return {children} +} -export default QueryProvider; +export default QueryProvider diff --git a/apps/web/app/supabase/api.ts b/apps/web/app/supabase/api.ts index 204fcea..8e33bc1 100644 --- a/apps/web/app/supabase/api.ts +++ b/apps/web/app/supabase/api.ts @@ -1,5 +1,5 @@ -import { createServerClient, serializeCookieHeader } from '@supabase/ssr'; -import { type NextApiRequest, type NextApiResponse } from 'next'; +import { createServerClient, serializeCookieHeader } from '@supabase/ssr' +import { type NextApiRequest, type NextApiResponse } from 'next' // API client @@ -10,17 +10,22 @@ export default function createClient(req: NextApiRequest, res: NextApiResponse) { cookies: { getAll() { - return Object.keys(req.cookies).map((name) => ({ name, value: req.cookies[name] || '' })); + return Object.keys(req.cookies).map(name => ({ + name, + value: req.cookies[name] || '', + })) }, setAll(cookiesToSet) { res.setHeader( 'Set-Cookie', - cookiesToSet.map(({ name, value, options }) => serializeCookieHeader(name, value, options)), - ); + cookiesToSet.map(({ name, value, options }) => + serializeCookieHeader(name, value, options), + ), + ) }, }, }, - ); + ) - return supabase; + return supabase } diff --git a/apps/web/app/supabase/client.ts b/apps/web/app/supabase/client.ts index 2824ccf..cd34411 100644 --- a/apps/web/app/supabase/client.ts +++ b/apps/web/app/supabase/client.ts @@ -1,9 +1,12 @@ -import { createBrowserClient } from '@supabase/ssr'; +import { createBrowserClient } from '@supabase/ssr' // Component client export function createClient() { // CSR에서는 Next_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY 사용 - return createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!); + return createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + ) } diff --git a/apps/web/app/supabase/middleware.ts b/apps/web/app/supabase/middleware.ts index 666efe0..0ba7ed3 100644 --- a/apps/web/app/supabase/middleware.ts +++ b/apps/web/app/supabase/middleware.ts @@ -1,25 +1,27 @@ -import { createServerClient } from '@supabase/ssr'; -import { NextResponse, type NextRequest } from 'next/server'; +import { createServerClient } from '@supabase/ssr' +import { NextResponse, type NextRequest } from 'next/server' export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ request, - }); + }) const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { - return request.cookies.getAll(); + return request.cookies.getAll() }, setAll(cookiesToSet) { - cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)); + cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) supabaseResponse = NextResponse.next({ request, - }); - cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options)); + }) + cookiesToSet.forEach(({ name, value, options }) => + supabaseResponse.cookies.set(name, value, options), + ) }, }, - }); + }) // Do not run code between createServerClient and // supabase.auth.getUser(). A simple mistake could make it very hard to debug @@ -29,13 +31,17 @@ export async function updateSession(request: NextRequest) { const { data: { user }, - } = await supabase.auth.getUser(); + } = await supabase.auth.getUser() - if (!user && !request.nextUrl.pathname.startsWith('/login') && !request.nextUrl.pathname.startsWith('/auth')) { + if ( + !user && + !request.nextUrl.pathname.startsWith('/login') && + !request.nextUrl.pathname.startsWith('/auth') + ) { // no user, potentially respond by redirecting the user to the login page - const url = request.nextUrl.clone(); - url.pathname = '/login'; - return NextResponse.redirect(url); + const url = request.nextUrl.clone() + url.pathname = '/login' + return NextResponse.redirect(url) } // IMPORTANT: You *must* return the supabaseResponse object as it is. @@ -51,5 +57,5 @@ export async function updateSession(request: NextRequest) { // If this is not done, you may be causing the browser and server to go out // of sync and terminate the user's session prematurely! - return supabaseResponse; + return supabaseResponse } diff --git a/apps/web/app/supabase/server.ts b/apps/web/app/supabase/server.ts index 09736eb..2709b36 100644 --- a/apps/web/app/supabase/server.ts +++ b/apps/web/app/supabase/server.ts @@ -1,18 +1,18 @@ -import { createServerClient } from '@supabase/ssr'; -import { cookies } from 'next/headers'; +import { createServerClient } from '@supabase/ssr' +import { cookies } from 'next/headers' // Server client export async function createClient() { - const cookieStore = await cookies(); + const cookieStore = await cookies() return createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { - return cookieStore.getAll(); + return cookieStore.getAll() }, setAll(cookiesToSet) { try { - cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); + cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) } catch { // The `setAll` method was called from a Server Component. // This can be ignored if you have middleware refreshing @@ -20,5 +20,5 @@ export async function createClient() { } }, }, - }); + }) } diff --git a/apps/web/app/test/SearchForm.tsx b/apps/web/app/test/SearchForm.tsx index 2de0661..a29a17e 100644 --- a/apps/web/app/test/SearchForm.tsx +++ b/apps/web/app/test/SearchForm.tsx @@ -1,26 +1,26 @@ -'use client'; +'use client' -import { useState, FormEvent } from 'react'; -import { useRouter } from 'next/navigation'; +import { useState, FormEvent } from 'react' +import { useRouter } from 'next/navigation' interface SearchFormProps { - initialSinger?: string; + initialSinger?: string } export function SearchForm({ initialSinger = '' }: SearchFormProps) { - const router = useRouter(); - const [singer, setSinger] = useState(initialSinger); + const router = useRouter() + const [singer, setSinger] = useState(initialSinger) const handleSubmit = (e: FormEvent) => { - e.preventDefault(); + e.preventDefault() // URL 쿼리 파라미터 업데이트 if (singer) { - router.push(`/test?search=${encodeURIComponent(singer)}`); + router.push(`/test?search=${encodeURIComponent(singer)}`) } else { - router.push('/test'); + router.push('/test') } - }; + } return (
@@ -28,14 +28,17 @@ export function SearchForm({ initialSinger = '' }: SearchFormProps) { setSinger(e.target.value)} + onChange={e => setSinger(e.target.value)} placeholder="가수 이름을 입력하세요" - className="flex-1 p-2 border border-gray-300 rounded" + className="flex-1 rounded border border-gray-300 p-2" /> -
- ); + ) } diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx index 1a33bbe..8c2ca6b 100644 --- a/apps/web/app/test/page.tsx +++ b/apps/web/app/test/page.tsx @@ -1,32 +1,32 @@ -import { getSinger, getNo, getPopular } from '@repo/api'; -import { SearchForm } from './SearchForm'; +import { getSinger, getNo, getPopular } from '@repo/api' +import { SearchForm } from './SearchForm' // 서버 컴포넌트 (기본적으로 서버에서 실행됨) export default async function TestPage({ searchParams }: { searchParams: { search?: string } }) { // URL 쿼리 파라미터에서 singer 값을 가져옴 - const search = searchParams.search; + const search = searchParams.search // 검색어가 있을 때만 API 호출 - let data = null; - let error = null; - const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; + let data = null + let error = null + const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000' if (search) { try { // TJ, 금영의 경우 해외(일본)곡 검색 못 함 // 크롤링 돌려서 DB에다가 집어넣어야 할 듯 - data = await getSinger({ singer: search, brand: 'tj' }); - const other = await getSinger({ singer: search, brand: 'kumyoung' }); + data = await getSinger({ singer: search, brand: 'tj' }) + const other = await getSinger({ singer: search, brand: 'kumyoung' }) // data = await getPopular({ brand: 'tj', period: 'weekly' }); // const other = await fetch(`http://localhost:3000/api/songs/singer?singer=아이유`); // const other = await fetch(`${baseUrl}/api/songs/singer/IU?brand=tj`); // const otherData = await other.json(); - console.log('other : ', other); - console.log('datajson : ', JSON.stringify(data, null, 2)); + console.log('other : ', other) + console.log('datajson : ', JSON.stringify(data, null, 2)) } catch (err) { - error = err instanceof Error ? err.message : '알 수 없는 오류'; - console.error('API 호출 오류:', err); + error = err instanceof Error ? err.message : '알 수 없는 오류' + console.error('API 호출 오류:', err) } } @@ -48,5 +48,5 @@ export default async function TestPage({ searchParams }: { searchParams: { searc )} - ); + ) } diff --git a/apps/web/app/testing-table/button.tsx b/apps/web/app/testing-table/button.tsx index 6e62a34..f1c8ecd 100644 --- a/apps/web/app/testing-table/button.tsx +++ b/apps/web/app/testing-table/button.tsx @@ -1,21 +1,21 @@ -'use client'; +'use client' -import { createClient } from '@/supabase/client'; +import { createClient } from '@/supabase/client' const Button = () => { - const supabase = createClient(); + const supabase = createClient() const handleInsertData = async () => { - const { data, error } = await supabase.from('test').insert({ name: 'testing' }); - console.log('data : ', data); - console.log('error : ', error); - }; + const { data, error } = await supabase.from('test').insert({ name: 'testing' }) + console.log('data : ', data) + console.log('error : ', error) + } return ( - ); -}; + ) +} -export default Button; +export default Button diff --git a/apps/web/app/testing-table/page.tsx b/apps/web/app/testing-table/page.tsx index eda9224..01bff9e 100644 --- a/apps/web/app/testing-table/page.tsx +++ b/apps/web/app/testing-table/page.tsx @@ -1,20 +1,20 @@ -import { createClient } from '@/supabase/server'; -import Button from './button'; +import { createClient } from '@/supabase/server' +import Button from './button' export default async function Instruments() { - const supabase = await createClient(); - console.log('supabase : ', supabase); - const data = await supabase.from('test').select(); + const supabase = await createClient() + console.log('supabase : ', supabase) + const data = await supabase.from('test').select() - const newData = await supabase.from('test').select('*'); + const newData = await supabase.from('test').select('*') - console.log('data : ', data); - console.log('newData : ', newData); + console.log('data : ', data) + console.log('newData : ', newData) return (
       {JSON.stringify(newData, null, 2)}
       
- ); + ) } diff --git a/apps/web/eslint.config.mjs b/apps/web/eslint.config.mjs index c85fb67..46f02ae 100644 --- a/apps/web/eslint.config.mjs +++ b/apps/web/eslint.config.mjs @@ -1,16 +1,14 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; +import { dirname } from 'path' +import { fileURLToPath } from 'url' +import { FlatCompat } from '@eslint/eslintrc' -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) const compat = new FlatCompat({ baseDirectory: __dirname, -}); +}) -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; +const eslintConfig = [...compat.extends('next/core-web-vitals', 'next/typescript')] -export default eslintConfig; +export default eslintConfig diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index e9ffa30..7329063 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,7 +1,7 @@ -import type { NextConfig } from "next"; +import type { NextConfig } from 'next' const nextConfig: NextConfig = { /* config options here */ -}; +} -export default nextConfig; +export default nextConfig diff --git a/apps/web/package.json b/apps/web/package.json index 12d1fa1..70cb758 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -7,7 +7,8 @@ "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "dependencies": { "@repo/api": "workspace:*", @@ -23,11 +24,15 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4.0.15", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "autoprefixer": "^10.4.21", "eslint": "^9", "eslint-config-next": "15.2.2", + "postcss": "^8.5.3", + "tailwindcss": "^4.0.15", "typescript": "^5" }, "peerDependencies": { diff --git a/apps/web/postcss.config.mjs b/apps/web/postcss.config.mjs new file mode 100644 index 0000000..317d71c --- /dev/null +++ b/apps/web/postcss.config.mjs @@ -0,0 +1,6 @@ +const config = { + plugins: { + '@tailwindcss/postcss': {}, + }, +} +export default config diff --git a/package.json b/package.json index e7bfaa3..110d520 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", - "format": "prettier --write \"**/*.{ts,tsx,md}\"", + "format": "turbo run format", "check-types": "turbo run check-types" }, "packageManager": "pnpm@9.0.0", @@ -17,6 +17,7 @@ }, "devDependencies": { "prettier": "^3.5.3", + "prettier-plugin-tailwindcss": "^0.6.11", "turbo": "^2.4.4", "typescript": "5.8.2" } diff --git a/packages/eslint-config/base.js b/packages/eslint-config/base.js index 09d316e..9bcbf88 100644 --- a/packages/eslint-config/base.js +++ b/packages/eslint-config/base.js @@ -1,8 +1,8 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import turboPlugin from "eslint-plugin-turbo"; -import tseslint from "typescript-eslint"; -import onlyWarn from "eslint-plugin-only-warn"; +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import turboPlugin from 'eslint-plugin-turbo'; +import tseslint from 'typescript-eslint'; +import onlyWarn from 'eslint-plugin-only-warn'; /** * A shared ESLint configuration for the repository. @@ -18,7 +18,7 @@ export const config = [ turbo: turboPlugin, }, rules: { - "turbo/no-undeclared-env-vars": "warn", + 'turbo/no-undeclared-env-vars': 'warn', }, }, { @@ -27,6 +27,6 @@ export const config = [ }, }, { - ignores: ["dist/**"], + ignores: ['dist/**'], }, ]; diff --git a/packages/eslint-config/next.js b/packages/eslint-config/next.js index 6bf01a7..364681d 100644 --- a/packages/eslint-config/next.js +++ b/packages/eslint-config/next.js @@ -1,11 +1,11 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import tseslint from "typescript-eslint"; -import pluginReactHooks from "eslint-plugin-react-hooks"; -import pluginReact from "eslint-plugin-react"; -import globals from "globals"; -import pluginNext from "@next/eslint-plugin-next"; -import { config as baseConfig } from "./base.js"; +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import tseslint from 'typescript-eslint'; +import pluginReactHooks from 'eslint-plugin-react-hooks'; +import pluginReact from 'eslint-plugin-react'; +import globals from 'globals'; +import pluginNext from '@next/eslint-plugin-next'; +import { config as baseConfig } from './base.js'; /** * A custom ESLint configuration for libraries that use Next.js. @@ -28,22 +28,22 @@ export const nextJsConfig = [ }, { plugins: { - "@next/next": pluginNext, + '@next/next': pluginNext, }, rules: { ...pluginNext.configs.recommended.rules, - ...pluginNext.configs["core-web-vitals"].rules, + ...pluginNext.configs['core-web-vitals'].rules, }, }, { plugins: { - "react-hooks": pluginReactHooks, + 'react-hooks': pluginReactHooks, }, - settings: { react: { version: "detect" } }, + settings: { react: { version: 'detect' } }, rules: { ...pluginReactHooks.configs.recommended.rules, // React scope no longer necessary with new JSX transform. - "react/react-in-jsx-scope": "off", + 'react/react-in-jsx-scope': 'off', }, }, ]; diff --git a/packages/eslint-config/react-internal.js b/packages/eslint-config/react-internal.js index daeccba..b8b50e7 100644 --- a/packages/eslint-config/react-internal.js +++ b/packages/eslint-config/react-internal.js @@ -1,10 +1,10 @@ -import js from "@eslint/js"; -import eslintConfigPrettier from "eslint-config-prettier"; -import tseslint from "typescript-eslint"; -import pluginReactHooks from "eslint-plugin-react-hooks"; -import pluginReact from "eslint-plugin-react"; -import globals from "globals"; -import { config as baseConfig } from "./base.js"; +import js from '@eslint/js'; +import eslintConfigPrettier from 'eslint-config-prettier'; +import tseslint from 'typescript-eslint'; +import pluginReactHooks from 'eslint-plugin-react-hooks'; +import pluginReact from 'eslint-plugin-react'; +import globals from 'globals'; +import { config as baseConfig } from './base.js'; /** * A custom ESLint configuration for libraries that use React. @@ -27,13 +27,13 @@ export const config = [ }, { plugins: { - "react-hooks": pluginReactHooks, + 'react-hooks': pluginReactHooks, }, - settings: { react: { version: "detect" } }, + settings: { react: { version: 'detect' } }, rules: { ...pluginReactHooks.configs.recommended.rules, // React scope no longer necessary with new JSX transform. - "react/react-in-jsx-scope": "off", + 'react/react-in-jsx-scope': 'off', }, }, ]; diff --git a/packages/ui/eslint.config.mjs b/packages/ui/eslint.config.mjs index 19170f8..fe059de 100644 --- a/packages/ui/eslint.config.mjs +++ b/packages/ui/eslint.config.mjs @@ -1,4 +1,4 @@ -import { config } from "@repo/eslint-config/react-internal"; +import { config } from '@repo/eslint-config/react-internal'; /** @type {import("eslint").Linter.Config} */ export default config; diff --git a/packages/ui/src/button.tsx b/packages/ui/src/button.tsx index 78e5420..7b117d2 100644 --- a/packages/ui/src/button.tsx +++ b/packages/ui/src/button.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import { ReactNode } from "react"; +import { ReactNode } from 'react'; interface ButtonProps { children: ReactNode; @@ -10,10 +10,7 @@ interface ButtonProps { export const Button = ({ children, className, appName }: ButtonProps) => { return ( - ); diff --git a/packages/ui/src/card.tsx b/packages/ui/src/card.tsx index 7b98893..002db20 100644 --- a/packages/ui/src/card.tsx +++ b/packages/ui/src/card.tsx @@ -1,4 +1,4 @@ -import { type JSX } from "react"; +import { type JSX } from 'react'; export function Card({ className, diff --git a/packages/ui/src/code.tsx b/packages/ui/src/code.tsx index f7cbd22..cee06ef 100644 --- a/packages/ui/src/code.tsx +++ b/packages/ui/src/code.tsx @@ -1,11 +1,5 @@ -import { type JSX } from "react"; +import { type JSX } from 'react'; -export function Code({ - children, - className, -}: { - children: React.ReactNode; - className?: string; -}): JSX.Element { +export function Code({ children, className }: { children: React.ReactNode; className?: string }): JSX.Element { return {children}; } diff --git a/packages/ui/turbo/generators/config.ts b/packages/ui/turbo/generators/config.ts index 40100ba..58c565f 100644 --- a/packages/ui/turbo/generators/config.ts +++ b/packages/ui/turbo/generators/config.ts @@ -1,27 +1,27 @@ -import type { PlopTypes } from "@turbo/gen"; +import type { PlopTypes } from '@turbo/gen'; // Learn more about Turborepo Generators at https://turbo.build/repo/docs/core-concepts/monorepos/code-generation export default function generator(plop: PlopTypes.NodePlopAPI): void { // A simple generator to add a new React component to the internal UI library - plop.setGenerator("react-component", { - description: "Adds a new react component", + plop.setGenerator('react-component', { + description: 'Adds a new react component', prompts: [ { - type: "input", - name: "name", - message: "What is the name of the component?", + type: 'input', + name: 'name', + message: 'What is the name of the component?', }, ], actions: [ { - type: "add", - path: "src/{{kebabCase name}}.tsx", - templateFile: "templates/component.hbs", + type: 'add', + path: 'src/{{kebabCase name}}.tsx', + templateFile: 'templates/component.hbs', }, { - type: "append", - path: "package.json", + type: 'append', + path: 'package.json', pattern: /"exports": {(?)/g, template: ' "./{{kebabCase name}}": "./src/{{kebabCase name}}.tsx",', }, diff --git a/packages/ui/turbo/generators/templates/component.hbs b/packages/ui/turbo/generators/templates/component.hbs index d968b9e..5a3ace4 100644 --- a/packages/ui/turbo/generators/templates/component.hbs +++ b/packages/ui/turbo/generators/templates/component.hbs @@ -1,8 +1,8 @@ -export const {{ pascalCase name }} = ({ children }: { children: React.ReactNode }) => { - return ( -
-

{{ pascalCase name }} Component

- {children} -
- ); -}; +export const +{{pascalCase name}} += ({ children }: { children: React.ReactNode }) => { return ( +
+

{{pascalCase name}} Component

+ {children} +
+); }; \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60b3b8d..d377f5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,9 @@ importers: prettier: specifier: ^3.5.3 version: 3.5.3 + prettier-plugin-tailwindcss: + specifier: ^0.6.11 + version: 0.6.11(prettier@3.5.3) turbo: specifier: ^2.4.4 version: 2.4.4 @@ -158,6 +161,9 @@ importers: '@eslint/eslintrc': specifier: ^3 version: 3.3.0 + '@tailwindcss/postcss': + specifier: ^4.0.15 + version: 4.0.15 '@types/node': specifier: ^20 version: 20.17.24 @@ -167,12 +173,21 @@ importers: '@types/react-dom': specifier: ^19 version: 19.0.4(@types/react@19.0.10) + autoprefixer: + specifier: ^10.4.21 + version: 10.4.21(postcss@8.5.3) eslint: specifier: ^9 - version: 9.22.0 + version: 9.22.0(jiti@2.4.2) eslint-config-next: specifier: 15.2.2 - version: 15.2.2(eslint@9.22.0)(typescript@5.8.2) + version: 15.2.2(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + postcss: + specifier: ^8.5.3 + version: 8.5.3 + tailwindcss: + specifier: ^4.0.15 + version: 4.0.15 typescript: specifier: ^5 version: 5.8.2 @@ -191,7 +206,7 @@ importers: version: 10.9.2(@types/node@22.13.10)(typescript@5.8.2) tsup: specifier: ^8.4.0 - version: 8.4.0(postcss@8.4.49)(typescript@5.8.2) + version: 8.4.0(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2) typescript: specifier: ^5.8.2 version: 5.8.2 @@ -206,22 +221,22 @@ importers: version: 15.2.1 eslint: specifier: ^9.22.0 - version: 9.22.0 + version: 9.22.0(jiti@2.4.2) eslint-config-prettier: specifier: ^10.1.1 - version: 10.1.1(eslint@9.22.0) + version: 10.1.1(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-only-warn: specifier: ^1.1.0 version: 1.1.0 eslint-plugin-react: specifier: ^7.37.4 - version: 7.37.4(eslint@9.22.0) + version: 7.37.4(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-react-hooks: specifier: ^5.2.0 - version: 5.2.0(eslint@9.22.0) + version: 5.2.0(eslint@9.22.0(jiti@2.4.2)) eslint-plugin-turbo: specifier: ^2.4.4 - version: 2.4.4(eslint@9.22.0)(turbo@2.4.4) + version: 2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.4.4) globals: specifier: ^16.0.0 version: 16.0.0 @@ -230,7 +245,7 @@ importers: version: 5.8.2 typescript-eslint: specifier: ^8.26.0 - version: 8.26.0(eslint@9.22.0)(typescript@5.8.2) + version: 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) packages/query: dependencies: @@ -243,7 +258,7 @@ importers: devDependencies: tsup: specifier: ^8.4.0 - version: 8.4.0(postcss@8.4.49)(typescript@5.8.2) + version: 8.4.0(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2) typescript: specifier: ^5.8.2 version: 5.8.2 @@ -279,7 +294,7 @@ importers: version: 19.0.4(@types/react@19.0.10) eslint: specifier: ^9.22.0 - version: 9.22.0 + version: 9.22.0(jiti@2.4.2) typescript: specifier: 5.8.2 version: 5.8.2 @@ -294,6 +309,10 @@ packages: graphql: optional: true + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -1880,6 +1899,82 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tailwindcss/node@4.0.15': + resolution: {integrity: sha512-IODaJjNmiasfZX3IoS+4Em3iu0fD2HS0/tgrnkYfW4hyUor01Smnr5eY3jc4rRgaTDrJlDmBTHbFO0ETTDaxWA==} + + '@tailwindcss/oxide-android-arm64@4.0.15': + resolution: {integrity: sha512-EBuyfSKkom7N+CB3A+7c0m4+qzKuiN0WCvzPvj5ZoRu4NlQadg/mthc1tl5k9b5ffRGsbDvP4k21azU4VwVk3Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.0.15': + resolution: {integrity: sha512-ObVAnEpLepMhV9VoO0JSit66jiN5C4YCqW3TflsE9boo2Z7FIjV80RFbgeL2opBhtxbaNEDa6D0/hq/EP03kgQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.0.15': + resolution: {integrity: sha512-IElwoFhUinOr9MyKmGTPNi1Rwdh68JReFgYWibPWTGuevkHkLWKEflZc2jtI5lWZ5U9JjUnUfnY43I4fEXrc4g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.0.15': + resolution: {integrity: sha512-6BLLqyx7SIYRBOnTZ8wgfXANLJV5TQd3PevRJZp0vn42eO58A2LykRKdvL1qyPfdpmEVtF+uVOEZ4QTMqDRAWA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15': + resolution: {integrity: sha512-Zy63EVqO9241Pfg6G0IlRIWyY5vNcWrL5dd2WAKVJZRQVeolXEf1KfjkyeAAlErDj72cnyXObEZjMoPEKHpdNw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.15': + resolution: {integrity: sha512-2NemGQeaTbtIp1Z2wyerbVEJZTkAWhMDOhhR5z/zJ75yMNf8yLnE+sAlyf6yGDNr+1RqvWrRhhCFt7i0CIxe4Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.0.15': + resolution: {integrity: sha512-342GVnhH/6PkVgKtEzvNVuQ4D+Q7B7qplvuH20Cfz9qEtydG6IQczTZ5IT4JPlh931MG1NUCVxg+CIorr1WJyw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.0.15': + resolution: {integrity: sha512-g76GxlKH124RuGqacCEFc2nbzRl7bBrlC8qDQMiUABkiifDRHOIUjgKbLNG4RuR9hQAD/MKsqZ7A8L08zsoBrw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.0.15': + resolution: {integrity: sha512-Gg/Y1XrKEvKpq6WeNt2h8rMIKOBj/W3mNa5NMvkQgMC7iO0+UNLrYmt6zgZufht66HozNpn+tJMbbkZ5a3LczA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.15': + resolution: {integrity: sha512-7QtSSJwYZ7ZK1phVgcNZpuf7c7gaCj8Wb0xjliligT5qCGCp79OV2n3SJummVZdw4fbTNKUOYMO7m1GinppZyA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.0.15': + resolution: {integrity: sha512-JQ5H+5MLhOjpgNp6KomouE0ZuKmk3hO5h7/ClMNAQ8gZI2zkli3IH8ZqLbd2DVfXDbdxN2xvooIEeIlkIoSCqw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.0.15': + resolution: {integrity: sha512-e0uHrKfPu7JJGMfjwVNyt5M0u+OP8kUmhACwIRlM+JNBuReDVQ63yAD1NWe5DwJtdaHjugNBil76j+ks3zlk6g==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.0.15': + resolution: {integrity: sha512-qyrpoDKIO7wzkRbKCvGLo7gXRjT9/Njf7ZJiJhG4njrfZkvOhjwnaHpYbpxYeDysEg+9pB1R4jcd+vQ7ZUDsmQ==} + '@tanstack/query-core@5.68.0': resolution: {integrity: sha512-r8rFYYo8/sY/LNaOqX84h12w7EQev4abFXDWy4UoDVUJzJ5d9Fbmb8ayTi7ScG+V0ap44SF3vNs/45mkzDGyGw==} @@ -2339,6 +2434,13 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + autoprefixer@10.4.21: + resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2554,6 +2656,9 @@ packages: caniuse-lite@1.0.30001701: resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} + caniuse-lite@1.0.30001707: + resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3505,6 +3610,9 @@ packages: resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} engines: {node: '>= 6'} + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + freeport-async@2.0.0: resolution: {integrity: sha512-K7od3Uw45AJg00XUmy15+Hae2hOcgKcmN3/EF6Y7i01O0gaqiRx8sUSpsb9+BRNL8RPBrhzPsVfy8q9ADlJuWQ==} engines: {node: '>=8'} @@ -4205,6 +4313,10 @@ packages: jimp-compact@0.16.1: resolution: {integrity: sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==} + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + join-component@1.1.0: resolution: {integrity: sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==} @@ -4328,64 +4440,128 @@ packages: cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.27.0: resolution: {integrity: sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.27.0: resolution: {integrity: sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.27.0: resolution: {integrity: sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.27.0: resolution: {integrity: sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + lightningcss-linux-arm64-musl@1.27.0: resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + lightningcss-linux-x64-gnu@1.27.0: resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + lightningcss-linux-x64-musl@1.27.0: resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + lightningcss-win32-arm64-msvc@1.27.0: resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.27.0: resolution: {integrity: sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.27.0: resolution: {integrity: sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -4733,6 +4909,10 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + npm-package-arg@11.0.3: resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} engines: {node: ^16.14.0 || >=18.0.0} @@ -5030,10 +5210,69 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-tailwindcss@0.6.11: + resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==} + engines: {node: '>=14.21.3'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-import-sort: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-style-order: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + prettier@3.5.3: resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} @@ -5808,6 +6047,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwindcss@4.0.15: + resolution: {integrity: sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==} + tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -6410,6 +6652,8 @@ snapshots: '@0no-co/graphql.web@1.1.2': {} + '@alloc/quick-lru@5.2.0': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -7404,9 +7648,9 @@ snapshots: '@esbuild/win32-x64@0.25.1': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.22.0)': + '@eslint-community/eslint-utils@4.4.1(eslint@9.22.0(jiti@2.4.2))': dependencies: - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} @@ -8444,6 +8688,68 @@ snapshots: dependencies: tslib: 2.8.1 + '@tailwindcss/node@4.0.15': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.15 + + '@tailwindcss/oxide-android-arm64@4.0.15': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.0.15': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.0.15': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.0.15': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.15': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.15': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.0.15': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.0.15': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.0.15': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.15': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.0.15': + optional: true + + '@tailwindcss/oxide@4.0.15': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.15 + '@tailwindcss/oxide-darwin-arm64': 4.0.15 + '@tailwindcss/oxide-darwin-x64': 4.0.15 + '@tailwindcss/oxide-freebsd-x64': 4.0.15 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.15 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.15 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.15 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.15 + '@tailwindcss/oxide-linux-x64-musl': 4.0.15 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.15 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.15 + + '@tailwindcss/postcss@4.0.15': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.0.15 + '@tailwindcss/oxide': 4.0.15 + lightningcss: 1.29.2 + postcss: 8.5.3 + tailwindcss: 4.0.15 + '@tanstack/query-core@5.68.0': {} '@tanstack/query-devtools@5.67.2': {} @@ -8638,15 +8944,15 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint@9.22.0)(typescript@5.8.2)': + '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.26.0(eslint@9.22.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) '@typescript-eslint/scope-manager': 8.26.0 - '@typescript-eslint/type-utils': 8.26.0(eslint@9.22.0)(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.0(eslint@9.22.0)(typescript@5.8.2) + '@typescript-eslint/type-utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) '@typescript-eslint/visitor-keys': 8.26.0 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -8655,14 +8961,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2)': + '@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': dependencies: '@typescript-eslint/scope-manager': 8.26.0 '@typescript-eslint/types': 8.26.0 '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -8672,12 +8978,12 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/type-utils@8.26.0(eslint@9.22.0)(typescript@5.8.2)': + '@typescript-eslint/type-utils@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': dependencies: '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.0(eslint@9.22.0)(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) debug: 4.4.0 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) ts-api-utils: 2.0.1(typescript@5.8.2) typescript: 5.8.2 transitivePeerDependencies: @@ -8699,13 +9005,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.26.0(eslint@9.22.0)(typescript@5.8.2)': + '@typescript-eslint/utils@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.26.0 '@typescript-eslint/types': 8.26.0 '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.8.2) - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) typescript: 5.8.2 transitivePeerDependencies: - supports-color @@ -9010,6 +9316,16 @@ snapshots: at-least-node@1.0.0: {} + autoprefixer@10.4.21(postcss@8.5.3): + dependencies: + browserslist: 4.24.4 + caniuse-lite: 1.0.30001707 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.3 + postcss-value-parser: 4.2.0 + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 @@ -9190,7 +9506,7 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001701 + caniuse-lite: 1.0.30001707 electron-to-chromium: 1.5.114 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) @@ -9283,6 +9599,8 @@ snapshots: caniuse-lite@1.0.30001701: {} + caniuse-lite@1.0.30001707: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -9655,8 +9973,7 @@ snapshots: detect-libc@1.0.3: {} - detect-libc@2.0.3: - optional: true + detect-libc@2.0.3: {} detect-newline@3.1.0: {} @@ -9879,19 +10196,19 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-next@15.2.2(eslint@9.22.0)(typescript@5.8.2): + eslint-config-next@15.2.2(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2): dependencies: '@next/eslint-plugin-next': 15.2.2 '@rushstack/eslint-patch': 1.11.0 - '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint@9.22.0)(typescript@5.8.2) - '@typescript-eslint/parser': 8.26.0(eslint@9.22.0)(typescript@5.8.2) - eslint: 9.22.0 + '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + eslint: 9.22.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0) - eslint-plugin-react: 7.37.4(eslint@9.22.0) - eslint-plugin-react-hooks: 5.2.0(eslint@9.22.0) + eslint-import-resolver-typescript: 3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-react: 7.37.4(eslint@9.22.0(jiti@2.4.2)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.22.0(jiti@2.4.2)) optionalDependencies: typescript: 5.8.2 transitivePeerDependencies: @@ -9899,9 +10216,9 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-prettier@10.1.1(eslint@9.22.0): + eslint-config-prettier@10.1.1(eslint@9.22.0(jiti@2.4.2)): dependencies: - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) eslint-import-resolver-node@0.3.9: dependencies: @@ -9911,33 +10228,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0): + eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 enhanced-resolve: 5.18.1 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) get-tsconfig: 4.10.0 is-bun-module: 1.3.0 stable-hash: 0.0.4 tinyglobby: 0.2.12 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0))(eslint@9.22.0): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.26.0(eslint@9.22.0)(typescript@5.8.2) - eslint: 9.22.0 + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + eslint: 9.22.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0) + eslint-import-resolver-typescript: 3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-typescript@3.8.5)(eslint@9.22.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -9946,9 +10263,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0))(eslint@9.22.0) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.8.5(eslint-plugin-import@2.31.0)(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -9960,13 +10277,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.26.0(eslint@9.22.0)(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.22.0): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.22.0(jiti@2.4.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -9976,7 +10293,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -9987,11 +10304,11 @@ snapshots: eslint-plugin-only-warn@1.1.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.22.0): + eslint-plugin-react-hooks@5.2.0(eslint@9.22.0(jiti@2.4.2)): dependencies: - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) - eslint-plugin-react@7.37.4(eslint@9.22.0): + eslint-plugin-react@7.37.4(eslint@9.22.0(jiti@2.4.2)): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -9999,7 +10316,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -10013,10 +10330,10 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-turbo@2.4.4(eslint@9.22.0)(turbo@2.4.4): + eslint-plugin-turbo@2.4.4(eslint@9.22.0(jiti@2.4.2))(turbo@2.4.4): dependencies: dotenv: 16.0.3 - eslint: 9.22.0 + eslint: 9.22.0(jiti@2.4.2) turbo: 2.4.4 eslint-scope@5.1.1: @@ -10033,9 +10350,9 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.22.0: + eslint@9.22.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.19.2 '@eslint/config-helpers': 0.1.0 @@ -10070,6 +10387,8 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 transitivePeerDependencies: - supports-color @@ -10458,6 +10777,8 @@ snapshots: es-set-tostringtag: 2.1.0 mime-types: 2.1.35 + fraction.js@4.3.7: {} + freeport-async@2.0.0: {} fresh@0.5.2: {} @@ -11447,6 +11768,8 @@ snapshots: jimp-compact@0.16.1: {} + jiti@2.4.2: {} + join-component@1.1.0: {} joycon@3.1.1: {} @@ -11596,33 +11919,63 @@ snapshots: lightningcss-darwin-arm64@1.27.0: optional: true + lightningcss-darwin-arm64@1.29.2: + optional: true + lightningcss-darwin-x64@1.27.0: optional: true + lightningcss-darwin-x64@1.29.2: + optional: true + lightningcss-freebsd-x64@1.27.0: optional: true + lightningcss-freebsd-x64@1.29.2: + optional: true + lightningcss-linux-arm-gnueabihf@1.27.0: optional: true + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + lightningcss-linux-arm64-gnu@1.27.0: optional: true + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + lightningcss-linux-arm64-musl@1.27.0: optional: true + lightningcss-linux-arm64-musl@1.29.2: + optional: true + lightningcss-linux-x64-gnu@1.27.0: optional: true + lightningcss-linux-x64-gnu@1.29.2: + optional: true + lightningcss-linux-x64-musl@1.27.0: optional: true + lightningcss-linux-x64-musl@1.29.2: + optional: true + lightningcss-win32-arm64-msvc@1.27.0: optional: true + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + lightningcss-win32-x64-msvc@1.27.0: optional: true + lightningcss-win32-x64-msvc@1.29.2: + optional: true + lightningcss@1.27.0: dependencies: detect-libc: 1.0.3 @@ -11638,6 +11991,21 @@ snapshots: lightningcss-win32-arm64-msvc: 1.27.0 lightningcss-win32-x64-msvc: 1.27.0 + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -12058,6 +12426,8 @@ snapshots: normalize-path@3.0.0: {} + normalize-range@0.1.2: {} + npm-package-arg@11.0.3: dependencies: hosted-git-info: 7.0.2 @@ -12351,11 +12721,12 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-load-config@6.0.1(postcss@8.4.49): + postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.3): dependencies: lilconfig: 3.1.3 optionalDependencies: - postcss: 8.4.49 + jiti: 2.4.2 + postcss: 8.5.3 postcss-value-parser@4.2.0: {} @@ -12371,8 +12742,18 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.3: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + prettier-plugin-tailwindcss@0.6.11(prettier@3.5.3): + dependencies: + prettier: 3.5.3 + prettier@3.5.3: {} pretty-bytes@5.6.0: {} @@ -13323,6 +13704,8 @@ snapshots: symbol-tree@3.2.4: {} + tailwindcss@4.0.15: {} + tapable@2.2.1: {} tar@6.2.1: @@ -13490,7 +13873,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.4.0(postcss@8.4.49)(typescript@5.8.2): + tsup@8.4.0(jiti@2.4.2)(postcss@8.5.3)(typescript@5.8.2): dependencies: bundle-require: 5.1.0(esbuild@0.25.1) cac: 6.7.14 @@ -13500,7 +13883,7 @@ snapshots: esbuild: 0.25.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(postcss@8.4.49) + postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.3) resolve-from: 5.0.0 rollup: 4.35.0 source-map: 0.8.0-beta.0 @@ -13509,7 +13892,7 @@ snapshots: tinyglobby: 0.2.12 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.4.49 + postcss: 8.5.3 typescript: 5.8.2 transitivePeerDependencies: - jiti @@ -13589,12 +13972,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.26.0(eslint@9.22.0)(typescript@5.8.2): + typescript-eslint@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0)(typescript@5.8.2))(eslint@9.22.0)(typescript@5.8.2) - '@typescript-eslint/parser': 8.26.0(eslint@9.22.0)(typescript@5.8.2) - '@typescript-eslint/utils': 8.26.0(eslint@9.22.0)(typescript@5.8.2) - eslint: 9.22.0 + '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2))(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/parser': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + '@typescript-eslint/utils': 8.26.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2) + eslint: 9.22.0(jiti@2.4.2) typescript: 5.8.2 transitivePeerDependencies: - supports-color diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3ff5faa..e9b0dad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,3 +1,3 @@ packages: - - "apps/*" - - "packages/*" + - 'apps/*' + - 'packages/*' diff --git a/turbo.json b/turbo.json index 4e06ab9..2107653 100644 --- a/turbo.json +++ b/turbo.json @@ -10,6 +10,9 @@ "lint": { "dependsOn": ["^lint"] }, + "format": { + "dependsOn": ["^format"] + }, "check-types": { "dependsOn": ["^check-types"] }, From a201b6e594c893e0b58d44c5378b022dfb782387 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Sun, 23 Mar 2025 23:03:19 +0900 Subject: [PATCH 10/54] =?UTF-8?q?feat=20:=20shadcn=20=EC=84=A4=EC=B9=98.?= =?UTF-8?q?=20#gpt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/gpt-review.yml | 6 +- apps/web/.prettierrc.cjs | 2 +- apps/web/app/components/ui/button.tsx | 59 +++++++++ apps/web/app/footer.tsx | 38 ++++++ apps/web/app/globals.css | 125 ++++++++++++++++++- apps/web/app/layout.tsx | 14 ++- apps/web/app/lib/utils.ts | 6 + apps/web/app/library/page.tsx | 8 ++ apps/web/app/page.module.css | 168 -------------------------- apps/web/app/page.tsx | 21 ++-- apps/web/app/popular/page.tsx | 8 ++ apps/web/app/search/page.tsx | 8 ++ apps/web/components.json | 21 ++++ apps/web/package.json | 8 +- pnpm-lock.yaml | 81 +++++++++++++ 15 files changed, 385 insertions(+), 188 deletions(-) create mode 100644 apps/web/app/components/ui/button.tsx create mode 100644 apps/web/app/footer.tsx create mode 100644 apps/web/app/lib/utils.ts create mode 100644 apps/web/app/library/page.tsx delete mode 100644 apps/web/app/page.module.css create mode 100644 apps/web/app/popular/page.tsx create mode 100644 apps/web/app/search/page.tsx create mode 100644 apps/web/components.json diff --git a/.github/workflows/gpt-review.yml b/.github/workflows/gpt-review.yml index 74c0228..87bc975 100644 --- a/.github/workflows/gpt-review.yml +++ b/.github/workflows/gpt-review.yml @@ -10,8 +10,8 @@ on: jobs: test: - - if: ${{ contains(github.event.head_commit.message, '#gpt') || contains(github.event.head_commit.message, '#gpt-review') }} runs-on: ubuntu-latest + runs-on: ubuntu-latest + if: ${{ contains(github.event.head_commit.message, '#gpt') || contains(github.event.head_commit.message, '#gpt-review') }} steps: - uses: anc95/ChatGPT-CodeReview@main env: @@ -23,4 +23,4 @@ jobs: MAX_PATCH_LENGTH: 10000 IGNORE_PATTERNS: /dist, /node_modules,*.md # Regex pattern to ignore files, separated by comma - INCLUDE_PATTERNS: "*.js, *.ts, *.jsx, *.tsx" # glob pattern or regex pattern to include files, separated by comma \ No newline at end of file + INCLUDE_PATTERNS: '*.js, *.ts, *.jsx, *.tsx' # glob pattern or regex pattern to include files, separated by comma diff --git a/apps/web/.prettierrc.cjs b/apps/web/.prettierrc.cjs index df3725d..edece5c 100644 --- a/apps/web/.prettierrc.cjs +++ b/apps/web/.prettierrc.cjs @@ -5,7 +5,7 @@ module.exports = { singleQuote: true, // 작은따옴표 사용 trailingComma: 'all', // 여러 줄일 때 항상 쉼표 사용 arrowParens: 'avoid', // 화살표 함수 괄호 생략 (ex: x => x) - semi: false, // 세미콜론 사용 안 함 + semi: true, // 세미콜론 사용 안 함 bracketSpacing: true, // 중괄호 간격 유지 (ex: { foo: bar }) jsxSingleQuote: false, // JSX에서 작은따옴표 사용 안 함 endOfLine: 'lf', // 줄바꿈 형식 (LF 고정) diff --git a/apps/web/app/components/ui/button.tsx b/apps/web/app/components/ui/button.tsx new file mode 100644 index 0000000..a2df8dc --- /dev/null +++ b/apps/web/app/components/ui/button.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/apps/web/app/footer.tsx b/apps/web/app/footer.tsx new file mode 100644 index 0000000..28e32d8 --- /dev/null +++ b/apps/web/app/footer.tsx @@ -0,0 +1,38 @@ +'use client' + +import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import Link from 'next/link' +import { usePathname } from 'next/navigation' + +const navigation = [ + { name: '부를 곡', href: '/' }, + { name: '검색', href: '/search' }, + { name: '인기곡', href: '/popular' }, + { name: '라이브러리', href: '/library' }, +] + +export default function Footer() { + const pathname = usePathname() + + return ( +
+ {navigation.map(item => { + const isActive = pathname === item.href + return ( + + ) + })} +
+ ) +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 336d113..4459a7c 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -1,12 +1,62 @@ @import 'tailwindcss'; +@import 'tw-animate-css'; -@theme { +@custom-variant dark (&:is(.dark *)); + +/* @theme { --color-background: #121212; --color-primary: #1db954; --color-secondary: #191414; --color-text-primary: #ffffff; --color-text-secondary: #b3b3b3; --color-text-accent: #1ed760; +} */ + +@theme inline { + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } } html, @@ -144,3 +194,76 @@ body { max-width: 100vw; overflow-x: hidden; } + +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + /* --primary: oklch(0.726 0.202 145.92); + --primary-foreground: oklch(0.985 0 0); */ + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + /* --primary: oklch(0.726 0.202 145.92); + --primary-foreground: oklch(0.985 0 0); */ + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 2ee4990..bc5f5ab 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,5 +1,8 @@ import type { Metadata } from 'next' import QueryProvider from './query' +import ErrorWrapper from '@/errorWrapper' +import Footer from './footer' + import './globals.css' export const metadata: Metadata = { @@ -14,8 +17,15 @@ export default function RootLayout({ }>) { return ( - - {children} + + + +
+
{children}
+
+
+
+
) diff --git a/apps/web/app/lib/utils.ts b/apps/web/app/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/apps/web/app/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/apps/web/app/library/page.tsx b/apps/web/app/library/page.tsx new file mode 100644 index 0000000..8615cac --- /dev/null +++ b/apps/web/app/library/page.tsx @@ -0,0 +1,8 @@ +export default function LibraryPage() { + return ( +
+

라이브러리 페이지

+

준비 중입니다...

+
+ ) +} diff --git a/apps/web/app/page.module.css b/apps/web/app/page.module.css deleted file mode 100644 index a11c8f3..0000000 --- a/apps/web/app/page.module.css +++ /dev/null @@ -1,168 +0,0 @@ -.page { - --gray-rgb: 0, 0, 0; - --gray-alpha-200: rgba(var(--gray-rgb), 0.08); - --gray-alpha-100: rgba(var(--gray-rgb), 0.05); - - --button-primary-hover: #383838; - --button-secondary-hover: #f2f2f2; - - display: grid; - grid-template-rows: 20px 1fr 20px; - align-items: center; - justify-items: center; - min-height: 100svh; - padding: 80px; - gap: 64px; - font-family: var(--font-geist-sans); -} - -@media (prefers-color-scheme: dark) { - .page { - --gray-rgb: 255, 255, 255; - --gray-alpha-200: rgba(var(--gray-rgb), 0.145); - --gray-alpha-100: rgba(var(--gray-rgb), 0.06); - - --button-primary-hover: #ccc; - --button-secondary-hover: #1a1a1a; - } -} - -.main { - display: flex; - flex-direction: column; - gap: 32px; - grid-row-start: 2; -} - -.main ol { - font-family: var(--font-geist-mono); - padding-left: 0; - margin: 0; - font-size: 14px; - line-height: 24px; - letter-spacing: -0.01em; - list-style-position: inside; -} - -.main li:not(:last-of-type) { - margin-bottom: 8px; -} - -.main code { - font-family: inherit; - background: var(--gray-alpha-100); - padding: 2px 4px; - border-radius: 4px; - font-weight: 600; -} - -.ctas { - display: flex; - gap: 16px; -} - -.ctas a { - appearance: none; - border-radius: 128px; - height: 48px; - padding: 0 20px; - border: none; - border: 1px solid transparent; - transition: - background 0.2s, - color 0.2s, - border-color 0.2s; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - font-size: 16px; - line-height: 20px; - font-weight: 500; -} - -a.primary { - background: var(--foreground); - color: var(--background); - gap: 8px; -} - -a.secondary { - border-color: var(--gray-alpha-200); - min-width: 158px; -} - -.footer { - grid-row-start: 3; - display: flex; - gap: 24px; -} - -.footer a { - display: flex; - align-items: center; - gap: 8px; -} - -.footer img { - flex-shrink: 0; -} - -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - a.primary:hover { - background: var(--button-primary-hover); - border-color: transparent; - } - - a.secondary:hover { - background: var(--button-secondary-hover); - border-color: transparent; - } - - .footer a:hover { - text-decoration: underline; - text-underline-offset: 4px; - } -} - -@media (max-width: 600px) { - .page { - padding: 32px; - padding-bottom: 80px; - } - - .main { - align-items: center; - } - - .main ol { - text-align: center; - } - - .ctas { - flex-direction: column; - } - - .ctas a { - font-size: 14px; - height: 40px; - padding: 0 16px; - } - - a.secondary { - min-width: auto; - } - - .footer { - flex-wrap: wrap; - align-items: center; - justify-content: center; - } -} - -@media (prefers-color-scheme: dark) { - .logo { - filter: invert(); - } -} diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 8fa97d3..14264fe 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,24 +1,21 @@ 'use client' import { useSong } from '@repo/query' -import ErrorWrapper from '@/errorWrapper' export default function Home() { const { data } = useSong({ title: '불나방' }) console.log('data : ', data) return ( - -
-
-

Hello World

-
-

Hello world!

+
+
+

Hello World

+
+

Hello world!

-
-

fotter

-
-
- +
+

fotter

+
+
) } diff --git a/apps/web/app/popular/page.tsx b/apps/web/app/popular/page.tsx new file mode 100644 index 0000000..8a42570 --- /dev/null +++ b/apps/web/app/popular/page.tsx @@ -0,0 +1,8 @@ +export default function PopularPage() { + return ( +
+

인기 페이지

+

준비 중입니다...

+
+ ) +} diff --git a/apps/web/app/search/page.tsx b/apps/web/app/search/page.tsx new file mode 100644 index 0000000..1605558 --- /dev/null +++ b/apps/web/app/search/page.tsx @@ -0,0 +1,8 @@ +export default function SearchPage() { + return ( +
+

검색 페이지

+

준비 중입니다...

+
+ ) +} diff --git a/apps/web/components.json b/apps/web/components.json new file mode 100644 index 0000000..335484f --- /dev/null +++ b/apps/web/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index 70cb758..b582d35 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,6 +11,7 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "dependencies": { + "@radix-ui/react-slot": "^1.1.2", "@repo/api": "workspace:*", "@repo/query": "workspace:*", "@supabase/ssr": "^0.6.1", @@ -18,9 +19,14 @@ "@tanstack/react-query": "^5.68.0", "@tanstack/react-query-devtools": "^5.68.0", "axios": "^1.5.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.483.0", "next": "15.2.2", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "tailwind-merge": "^3.0.2", + "tw-animate-css": "^1.2.4" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d377f5d..deb1041 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,9 @@ importers: apps/web: dependencies: + '@radix-ui/react-slot': + specifier: ^1.1.2 + version: 1.1.2(@types/react@19.0.10)(react@19.0.0) '@repo/api': specifier: workspace:* version: link:../../packages/api @@ -148,6 +151,15 @@ importers: axios: specifier: ^1.5.0 version: 1.8.3 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^0.483.0 + version: 0.483.0(react@19.0.0) next: specifier: 15.2.2 version: 15.2.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -157,6 +169,12 @@ importers: react-dom: specifier: ^19.0.0 version: 19.0.0(react@19.0.0) + tailwind-merge: + specifier: ^3.0.2 + version: 3.0.2 + tw-animate-css: + specifier: ^1.2.4 + version: 1.2.4 devDependencies: '@eslint/eslintrc': specifier: ^3 @@ -1636,11 +1654,29 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-slot@1.0.1': resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 + '@radix-ui/react-slot@1.1.2': + resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@react-native/assets-registry@0.76.7': resolution: {integrity: sha512-o79whsqL5fbPTUQO9w1FptRd4cw1TaeOrXtQSLQeDrMVAenw/wmsjyPK10VKtvqxa1KNMtWEyfgxcM8CVZVFmg==} engines: {node: '>=18'} @@ -2718,6 +2754,9 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -2753,6 +2792,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -4638,6 +4681,11 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lucide-react@0.483.0: + resolution: {integrity: sha512-WldsY17Qb/T3VZdMnVQ9C3DDIP7h1ViDTHVdVGnLZcvHNg30zH/MTQ04RTORjexoGmpsXroiQXZ4QyR0kBy0FA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} @@ -6047,6 +6095,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwind-merge@3.0.2: + resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==} + tailwindcss@4.0.15: resolution: {integrity: sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg==} @@ -6245,6 +6296,9 @@ packages: turbo@2.4.4: resolution: {integrity: sha512-N9FDOVaY3yz0YCOhYIgOGYad7+m2ptvinXygw27WPLQvcZDl3+0Sa77KGVlLSiuPDChOUEnTKE9VJwLSi9BPGQ==} + tw-animate-css@1.2.4: + resolution: {integrity: sha512-yt+HkJB41NAvOffe4NweJU6fLqAlVx/mBX6XmHRp15kq0JxTtOKaIw8pVSWM1Z+n2nXtyi7cW6C9f0WG/F/QAQ==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -8353,12 +8407,25 @@ snapshots: '@babel/runtime': 7.26.10 react: 18.3.1 + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)': + dependencies: + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@radix-ui/react-slot@1.0.1(react@18.3.1)': dependencies: '@babel/runtime': 7.26.10 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 + '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) + react: 19.0.0 + optionalDependencies: + '@types/react': 19.0.10 + '@react-native/assets-registry@0.76.7': {} '@react-native/babel-plugin-codegen@0.76.7(@babel/preset-env@7.26.9(@babel/core@7.26.10))': @@ -9680,6 +9747,10 @@ snapshots: cjs-module-lexer@1.4.3: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + clean-stack@2.2.0: {} cli-cursor@2.1.0: @@ -9710,6 +9781,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + co@4.6.0: {} collect-v8-coverage@1.0.2: {} @@ -12070,6 +12143,10 @@ snapshots: lru-cache@7.18.3: {} + lucide-react@0.483.0(react@19.0.0): + dependencies: + react: 19.0.0 + make-dir@2.1.0: dependencies: pify: 4.0.1 @@ -13704,6 +13781,8 @@ snapshots: symbol-tree@3.2.4: {} + tailwind-merge@3.0.2: {} + tailwindcss@4.0.15: {} tapable@2.2.1: {} @@ -13927,6 +14006,8 @@ snapshots: turbo-windows-64: 2.4.4 turbo-windows-arm64: 2.4.4 + tw-animate-css@1.2.4: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 From ddbdac8d355b277166f71913201661c39f1f43b7 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 24 Mar 2025 17:58:54 +0900 Subject: [PATCH 11/54] =?UTF-8?q?feat=20:=20cursor=20ui=20=EB=BC=88?= =?UTF-8?q?=EB=8C=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/home/page.tsx | 97 ++++++++++++++++++++++++++++++++++++++ apps/web/app/page.tsx | 20 ++------ 2 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 apps/web/app/home/page.tsx diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx new file mode 100644 index 0000000..d355609 --- /dev/null +++ b/apps/web/app/home/page.tsx @@ -0,0 +1,97 @@ +'use client'; + +import { useState } from 'react'; + +interface Block { + id: number; + content: string; +} + +export default function HomePage() { + const [blocks, setBlocks] = useState([ + { id: 1, content: '블록 1' }, + { id: 2, content: '블록 2' }, + { id: 3, content: '블록 3' }, + { id: 4, content: '블록 4' }, + ]); + + const moveBlock = (index: number, direction: 'up' | 'down') => { + const newBlocks = [...blocks]; + if (direction === 'up' && index > 0) { + [newBlocks[index], newBlocks[index - 1]] = [newBlocks[index - 1], newBlocks[index]]; + } else if (direction === 'down' && index < blocks.length - 1) { + [newBlocks[index], newBlocks[index + 1]] = [newBlocks[index + 1], newBlocks[index]]; + } + setBlocks(newBlocks); + }; + + return ( +
+
+

블록 순서 변경

+
+ {blocks.map((block, index) => ( +
+ {/* 블록 내용 */} +
{block.content}
+ + {/* 컨트롤 버튼 */} +
+ + + +
+
+ ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 14264fe..dd082d2 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,21 +1,7 @@ 'use client' -import { useSong } from '@repo/query' +import Home from '@/home/page' -export default function Home() { - const { data } = useSong({ title: '불나방' }) - console.log('data : ', data) - - return ( -
-
-

Hello World

-
-

Hello world!

- -
-

fotter

-
-
- ) +export default function HomePage() { + return } From 932855ca69cacf2c7afe318052b1999ee1744133 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Tue, 25 Mar 2025 16:58:24 +0900 Subject: [PATCH 12/54] =?UTF-8?q?feat=20:=20brand=20color=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80.=20reset=20=EC=B6=A9=EB=8F=8C=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/globals.css | 141 ++------------------------------------- 1 file changed, 5 insertions(+), 136 deletions(-) diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 4459a7c..d223c2a 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -48,6 +48,8 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + --color-brand-tj: var(--brand-tj); + --color-brand-ky: var(--brand-ky); } @layer base { @@ -59,142 +61,6 @@ } } -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, -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; - font-size: 100%; - font: inherit; - 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; -} - -a { - color: inherit; - text-decoration: none; -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - :root { --radius: 0.625rem; --background: oklch(1 0 0); @@ -230,6 +96,9 @@ body { --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); + /* oklch로는 표현 ff4a00 */ + --brand-tj: oklch(66.48% 0.226 36.37); + --brand-ky: oklch(61.23% 0.159 288.46); } .dark { From bc921c645d515eea89b8af8147eba8306985962c Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Tue, 25 Mar 2025 16:58:49 +0900 Subject: [PATCH 13/54] =?UTF-8?q?feat=20:=20DND=20=EC=84=A4=EC=B9=98.=20V0?= =?UTF-8?q?=20=ED=99=9C=EC=9A=A9=20=ED=8D=BC=EB=B8=94=EB=A6=AC=EC=8B=B1.?= =?UTF-8?q?=20=EC=9D=B4=ED=95=B4=20=ED=95=84=EC=9A=94.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/components/ui/card.tsx | 92 +++++++++++++++++++++ apps/web/app/home/SongCard.tsx | 120 +++++++++++++++++++++++++++ apps/web/app/home/SongList.tsx | 124 ++++++++++++++++++++++++++++ apps/web/app/home/TestDND.tsx | 59 +++++++++++++ apps/web/app/home/TestDNDHandle.tsx | 61 ++++++++++++++ apps/web/app/home/page.tsx | 103 +++-------------------- apps/web/app/layout.tsx | 16 ++-- apps/web/package.json | 4 + pnpm-lock.yaml | 72 ++++++++++++++++ 9 files changed, 551 insertions(+), 100 deletions(-) create mode 100644 apps/web/app/components/ui/card.tsx create mode 100644 apps/web/app/home/SongCard.tsx create mode 100644 apps/web/app/home/SongList.tsx create mode 100644 apps/web/app/home/TestDND.tsx create mode 100644 apps/web/app/home/TestDNDHandle.tsx diff --git a/apps/web/app/components/ui/card.tsx b/apps/web/app/components/ui/card.tsx new file mode 100644 index 0000000..d05bbc6 --- /dev/null +++ b/apps/web/app/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/apps/web/app/home/SongCard.tsx b/apps/web/app/home/SongCard.tsx new file mode 100644 index 0000000..3b5f197 --- /dev/null +++ b/apps/web/app/home/SongCard.tsx @@ -0,0 +1,120 @@ +'use client'; + +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { Mic, Trash, ChevronsUp, ChevronsDown, GripVertical } from 'lucide-react'; +import { Card } from '@/components/ui/card'; +import { cn } from '@/lib/utils'; + +interface Song { + id: string; + title: string; + artist: string; + kumyoungNumber: string; + tjNumber: string; + sung?: boolean; +} + +interface SongCardProps { + song: Song; + onDelete: () => void; + onMoveToTop: () => void; + onMoveToBottom: () => void; + onToggleSung: () => void; +} + +export default function SongCard({ + song, + onDelete, + onMoveToTop, + onMoveToBottom, + onToggleSung, +}: SongCardProps) { + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: song.id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( + + {/* 메인 콘텐츠 영역 */} +
+ {/* 노래 정보 */} +
+ {/* 제목 및 가수 */} +
+

+ {song.title} +

+

{song.artist}

+
+ + {/* 노래방 번호 */} +
+
+ TJ + {song.tjNumber} +
+
+ 금영 + {song.kumyoungNumber} +
+
+
+ {/* 버튼 영역 - 우측 하단에 고정 */} +
+ + + + + + + +
+
+ {/* 드래그 핸들 */} +
+ +
+
+ ); +} diff --git a/apps/web/app/home/SongList.tsx b/apps/web/app/home/SongList.tsx new file mode 100644 index 0000000..cc0e548 --- /dev/null +++ b/apps/web/app/home/SongList.tsx @@ -0,0 +1,124 @@ +'use client'; + +import { useState } from 'react'; +import { + DndContext, + closestCenter, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + verticalListSortingStrategy, +} from '@dnd-kit/sortable'; +import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; +import SongCard from './SongCard'; + +// 초기 노래 데이터 +const initialSongs = [ + { id: '1', title: '눈의 꽃', artist: '박효신', kumyoungNumber: '47251', tjNumber: '62867' }, + { id: '2', title: '거리에서', artist: '성시경', kumyoungNumber: '84173', tjNumber: '48506' }, + { + id: '3', + title: '벚꽃 엔딩', + artist: '버스커 버스커', + kumyoungNumber: '46079', + tjNumber: '30184', + }, + { id: '4', title: '사랑했나봐', artist: '윤도현', kumyoungNumber: '41906', tjNumber: '35184' }, + { + id: '5', + title: '너를 사랑하고 있어', + artist: '백지영', + kumyoungNumber: '38115', + tjNumber: '46009', + }, +]; + +export default function SongList() { + const [songs, setSongs] = useState(initialSongs); + + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }), + ); + + function handleDragEnd(event: any) { + const { active, over } = event; + + if (active.id !== over.id) { + setSongs(items => { + const oldIndex = items.findIndex(item => item.id === active.id); + const newIndex = items.findIndex(item => item.id === over.id); + + return arrayMove(items, oldIndex, newIndex); + }); + } + } + + function handleDelete(id: string) { + setSongs(songs.filter(song => song.id !== id)); + } + + function handleMoveToTop(id: string) { + setSongs(prev => { + const songIndex = prev.findIndex(song => song.id === id); + if (songIndex <= 0) return prev; + + const newSongs = [...prev]; + const [movedSong] = newSongs.splice(songIndex, 1); + newSongs.unshift(movedSong); + + return newSongs; + }); + } + + function handleMoveToBottom(id: string) { + setSongs(prev => { + const songIndex = prev.findIndex(song => song.id === id); + if (songIndex === -1 || songIndex === prev.length - 1) return prev; + + const newSongs = [...prev]; + const [movedSong] = newSongs.splice(songIndex, 1); + newSongs.push(movedSong); + + return newSongs; + }); + } + + function handleToggleSung(id: string) { + setSongs(prev => + prev.map(song => (song.id === id ? { ...song, sung: song.sung ? !song.sung : true } : song)), + ); + } + + return ( + + song.id)} strategy={verticalListSortingStrategy}> +
+ {songs.map(song => ( + handleDelete(song.id)} + onMoveToTop={() => handleMoveToTop(song.id)} + onMoveToBottom={() => handleMoveToBottom(song.id)} + onToggleSung={() => handleToggleSung(song.id)} + /> + ))} +
+
+
+ ); +} diff --git a/apps/web/app/home/TestDND.tsx b/apps/web/app/home/TestDND.tsx new file mode 100644 index 0000000..c356be1 --- /dev/null +++ b/apps/web/app/home/TestDND.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { useState } from 'react'; +import { DndContext, closestCenter } from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + verticalListSortingStrategy, + useSortable, +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; + +const defaultItems = ['Apple', 'Banana', 'Cherry', 'Date']; + +export default function DragDropList() { + const [items, setItems] = useState(defaultItems); + + const handleDragEnd = (event: any) => { + const { active, over } = event; + if (active.id !== over.id) { + const oldIndex = items.indexOf(active.id); + const newIndex = items.indexOf(over.id); + setItems(arrayMove(items, oldIndex, newIndex)); + } + }; + + return ( + + +
    + {items.map(item => ( + + ))} +
+
+
+ ); +} + +function SortableItem({ id }: { id: string }) { + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( +
  • + {id} +
  • + ); +} diff --git a/apps/web/app/home/TestDNDHandle.tsx b/apps/web/app/home/TestDNDHandle.tsx new file mode 100644 index 0000000..6b09b13 --- /dev/null +++ b/apps/web/app/home/TestDNDHandle.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { useState } from 'react'; +import { DndContext, closestCenter } from '@dnd-kit/core'; +import { + arrayMove, + SortableContext, + verticalListSortingStrategy, + useSortable, +} from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { GripVertical } from 'lucide-react'; + +const defaultItems = ['Apple', 'Banana', 'Cherry', 'Date']; + +export default function DragDropList() { + const [items, setItems] = useState(defaultItems); + + const handleDragEnd = (event: any) => { + const { active, over } = event; + if (active.id !== over.id) { + const oldIndex = items.indexOf(active.id); + const newIndex = items.indexOf(over.id); + setItems(arrayMove(items, oldIndex, newIndex)); + } + }; + + return ( + + +
      + {items.map(item => ( + + ))} +
    +
    +
    + ); +} + +function SortableItem({ id }: { id: string }) { + const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( +
  • + + + + {id} +
  • + ); +} diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx index d355609..5613aec 100644 --- a/apps/web/app/home/page.tsx +++ b/apps/web/app/home/page.tsx @@ -1,97 +1,16 @@ -'use client'; +import SongList from './SongList'; -import { useState } from 'react'; - -interface Block { - id: number; - content: string; -} - -export default function HomePage() { - const [blocks, setBlocks] = useState([ - { id: 1, content: '블록 1' }, - { id: 2, content: '블록 2' }, - { id: 3, content: '블록 3' }, - { id: 4, content: '블록 4' }, - ]); - - const moveBlock = (index: number, direction: 'up' | 'down') => { - const newBlocks = [...blocks]; - if (direction === 'up' && index > 0) { - [newBlocks[index], newBlocks[index - 1]] = [newBlocks[index - 1], newBlocks[index]]; - } else if (direction === 'down' && index < blocks.length - 1) { - [newBlocks[index], newBlocks[index + 1]] = [newBlocks[index + 1], newBlocks[index]]; - } - setBlocks(newBlocks); - }; +import TestDND from './TestDND'; +import TestDNDHandle from './TestDNDHandle'; +export default function Home() { return ( -
    -
    -

    블록 순서 변경

    -
    - {blocks.map((block, index) => ( -
    - {/* 블록 내용 */} -
    {block.content}
    - - {/* 컨트롤 버튼 */} -
    - - - -
    -
    - ))} -
    -
    +
    +

    노래방 플레이리스트

    + + + +
    ); -} \ No newline at end of file +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index bc5f5ab..78af0d3 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,19 +1,19 @@ -import type { Metadata } from 'next' -import QueryProvider from './query' -import ErrorWrapper from '@/errorWrapper' -import Footer from './footer' +import type { Metadata } from 'next'; +import QueryProvider from './query'; +import ErrorWrapper from './ErrorWrapper'; +import Footer from './footer'; -import './globals.css' +import './globals.css'; export const metadata: Metadata = { title: 'Singcode', description: 'Singcode', -} +}; export default function RootLayout({ children, }: Readonly<{ - children: React.ReactNode + children: React.ReactNode; }>) { return ( @@ -28,5 +28,5 @@ export default function RootLayout({ - ) + ); } diff --git a/apps/web/package.json b/apps/web/package.json index b582d35..2d63843 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,6 +11,10 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@radix-ui/react-slot": "^1.1.2", "@repo/api": "workspace:*", "@repo/query": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deb1041..84f096c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,18 @@ importers: apps/web: dependencies: + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@dnd-kit/modifiers': + specifier: ^9.0.0 + version: 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) + '@dnd-kit/utilities': + specifier: ^3.2.2 + version: 3.2.2(react@19.0.0) '@radix-ui/react-slot': specifier: ^1.1.2 version: 1.1.2(@types/react@19.0.10)(react@19.0.0) @@ -1057,6 +1069,34 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/modifiers@9.0.0': + resolution: {integrity: sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + '@egjs/hammerjs@2.0.17': resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} engines: {node: '>=0.8.0'} @@ -7618,6 +7658,38 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@dnd-kit/accessibility@3.1.1(react@19.0.0)': + dependencies: + react: 19.0.0 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@19.0.0) + '@dnd-kit/utilities': 3.2.2(react@19.0.0) + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + tslib: 2.8.1 + + '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@dnd-kit/utilities': 3.2.2(react@19.0.0) + react: 19.0.0 + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@dnd-kit/utilities': 3.2.2(react@19.0.0) + react: 19.0.0 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@19.0.0)': + dependencies: + react: 19.0.0 + tslib: 2.8.1 + '@egjs/hammerjs@2.0.17': dependencies: '@types/hammerjs': 2.0.46 From 74319d8a7bf1ea4cb2072f5bda4df2caaad389b1 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Tue, 25 Mar 2025 17:02:25 +0900 Subject: [PATCH 14/54] chore : prettier format --- apps/web/.prettierrc.cjs | 3 +- apps/web/app/api/auth/callback/route.ts | 26 +++---- apps/web/app/api/auth/confirm.ts | 36 +++++----- .../web/app/api/songs/[type]/[param]/route.ts | 54 +++++++-------- apps/web/app/components/ui/button.tsx | 49 +++++++------- apps/web/app/components/ui/card.tsx | 67 +++++++------------ apps/web/app/error.tsx | 22 +++--- apps/web/app/error/page.tsx | 10 +-- apps/web/app/errorWrapper.tsx | 22 +++--- apps/web/app/find/page.tsx | 14 ++-- apps/web/app/footer.tsx | 20 +++--- apps/web/app/lib/utils.ts | 6 +- apps/web/app/library/page.tsx | 2 +- apps/web/app/login/KakaoLoginButton.tsx | 16 ++--- apps/web/app/login/actions.ts | 34 +++++----- apps/web/app/login/page.tsx | 16 ++--- apps/web/app/page.tsx | 6 +- apps/web/app/popular/page.tsx | 2 +- apps/web/app/query.tsx | 14 ++-- apps/web/app/search/page.tsx | 2 +- apps/web/app/supabase/api.ts | 12 ++-- apps/web/app/supabase/client.ts | 4 +- apps/web/app/supabase/middleware.ts | 26 +++---- apps/web/app/supabase/server.ts | 12 ++-- apps/web/app/test/SearchForm.tsx | 22 +++--- apps/web/app/test/page.tsx | 26 +++---- apps/web/app/testing-table/button.tsx | 20 +++--- apps/web/app/testing-table/page.tsx | 18 ++--- apps/web/next.config.ts | 6 +- 29 files changed, 273 insertions(+), 294 deletions(-) diff --git a/apps/web/.prettierrc.cjs b/apps/web/.prettierrc.cjs index edece5c..d825bd6 100644 --- a/apps/web/.prettierrc.cjs +++ b/apps/web/.prettierrc.cjs @@ -5,8 +5,7 @@ module.exports = { singleQuote: true, // 작은따옴표 사용 trailingComma: 'all', // 여러 줄일 때 항상 쉼표 사용 arrowParens: 'avoid', // 화살표 함수 괄호 생략 (ex: x => x) - semi: true, // 세미콜론 사용 안 함 bracketSpacing: true, // 중괄호 간격 유지 (ex: { foo: bar }) jsxSingleQuote: false, // JSX에서 작은따옴표 사용 안 함 endOfLine: 'lf', // 줄바꿈 형식 (LF 고정) -} +}; diff --git a/apps/web/app/api/auth/callback/route.ts b/apps/web/app/api/auth/callback/route.ts index b8b9ac3..e4123b7 100644 --- a/apps/web/app/api/auth/callback/route.ts +++ b/apps/web/app/api/auth/callback/route.ts @@ -1,30 +1,30 @@ -import { NextResponse } from 'next/server' +import { NextResponse } from 'next/server'; // The client you created from the Server-Side Auth instructions -import { createClient } from '@/supabase/server' +import { createClient } from '@/supabase/server'; export async function GET(request: Request) { - const { searchParams, origin } = new URL(request.url) - const code = searchParams.get('code') + const { searchParams, origin } = new URL(request.url); + const code = searchParams.get('code'); // if "next" is in param, use it as the redirect URL - const next = searchParams.get('next') ?? '/' + const next = searchParams.get('next') ?? '/'; if (code) { - const supabase = await createClient() - const { error } = await supabase.auth.exchangeCodeForSession(code) + const supabase = await createClient(); + const { error } = await supabase.auth.exchangeCodeForSession(code); if (!error) { - const forwardedHost = request.headers.get('x-forwarded-host') // original origin before load balancer - const isLocalEnv = process.env.NODE_ENV === 'development' + const forwardedHost = request.headers.get('x-forwarded-host'); // original origin before load balancer + const isLocalEnv = process.env.NODE_ENV === 'development'; if (isLocalEnv) { // we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host - return NextResponse.redirect(`${origin}${next}`) + return NextResponse.redirect(`${origin}${next}`); } else if (forwardedHost) { - return NextResponse.redirect(`https://${forwardedHost}${next}`) + return NextResponse.redirect(`https://${forwardedHost}${next}`); } else { - return NextResponse.redirect(`${origin}${next}`) + return NextResponse.redirect(`${origin}${next}`); } } } // return the user to an error page with instructions - return NextResponse.redirect(`${origin}/auth/auth-code-error`) + return NextResponse.redirect(`${origin}/auth/auth-code-error`); } diff --git a/apps/web/app/api/auth/confirm.ts b/apps/web/app/api/auth/confirm.ts index c018367..41dcdda 100644 --- a/apps/web/app/api/auth/confirm.ts +++ b/apps/web/app/api/auth/confirm.ts @@ -1,45 +1,45 @@ -import { type EmailOtpType } from '@supabase/supabase-js' -import type { NextApiRequest, NextApiResponse } from 'next' +import { type EmailOtpType } from '@supabase/supabase-js'; +import type { NextApiRequest, NextApiResponse } from 'next'; -import createClient from '@/supabase/api' +import createClient from '@/supabase/api'; function stringOrFirstString(item: string | string[] | undefined) { - return Array.isArray(item) ? item[0] : item + return Array.isArray(item) ? item[0] : item; } // API Route에서는 throw 대신 적절한 HTTP 응답 필요. 리다이렉팅의 이유? export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== 'GET') { - res.status(405).appendHeader('Allow', 'GET').end() - return + res.status(405).appendHeader('Allow', 'GET').end(); + return; } - const queryParams = req.query - const token_hash = stringOrFirstString(queryParams.token_hash) - const type = stringOrFirstString(queryParams.type) - const nextUrl = stringOrFirstString(queryParams.next) || '/' + const queryParams = req.query; + const token_hash = stringOrFirstString(queryParams.token_hash); + const type = stringOrFirstString(queryParams.type); + const nextUrl = stringOrFirstString(queryParams.next) || '/'; try { if (!token_hash || !type) { - return res.redirect('/error?message=missing-parameters') + return res.redirect('/error?message=missing-parameters'); } - const supabase = createClient(req, res) + const supabase = createClient(req, res); const { error } = await supabase.auth.verifyOtp({ type: type as EmailOtpType, token_hash, - }) + }); if (error) { - console.error('인증 에러:', error) - return res.redirect(`/error?message=${encodeURIComponent(error.message)}`) + console.error('인증 에러:', error); + return res.redirect(`/error?message=${encodeURIComponent(error.message)}`); } // 성공 시 nextUrl로 리다이렉트 - return res.redirect(nextUrl) + return res.redirect(nextUrl); } catch (error) { - console.error('예상치 못한 에러:', error) - return res.redirect('/error?message=unexpected-error') + console.error('예상치 못한 에러:', error); + return res.redirect('/error?message=unexpected-error'); } } diff --git a/apps/web/app/api/songs/[type]/[param]/route.ts b/apps/web/app/api/songs/[type]/[param]/route.ts index 9ea1a3c..f956134 100644 --- a/apps/web/app/api/songs/[type]/[param]/route.ts +++ b/apps/web/app/api/songs/[type]/[param]/route.ts @@ -1,5 +1,5 @@ // app/api/songs/[type]/[param]/route.ts -import { NextRequest, NextResponse } from 'next/server' +import { NextRequest, NextResponse } from 'next/server'; import { getSong, getSinger, @@ -10,68 +10,68 @@ import { getPopular, Brand, Period, -} from '@repo/api' +} from '@repo/api'; export async function GET( request: NextRequest, { params }: { params: { type: string; param: string } }, ) { try { - const { type, param } = params - const searchParams = request.nextUrl.searchParams - const brand = searchParams.get('brand') as Brand | undefined + const { type, param } = params; + const searchParams = request.nextUrl.searchParams; + const brand = searchParams.get('brand') as Brand | undefined; - let result = null + let result = null; switch (type) { case 'title': - result = await getSong({ title: param, brand }) - break + result = await getSong({ title: param, brand }); + break; case 'singer': - result = await getSinger({ singer: param, brand }) - break + result = await getSinger({ singer: param, brand }); + break; case 'composer': - result = await getComposer({ composer: param, brand }) - break + result = await getComposer({ composer: param, brand }); + break; case 'lyricist': - result = await getLyricist({ lyricist: param, brand }) - break + result = await getLyricist({ lyricist: param, brand }); + break; case 'no': - result = await getNo({ no: param, brand }) - break + result = await getNo({ no: param, brand }); + break; case 'release': - result = await getRelease({ release: param, brand }) - break + result = await getRelease({ release: param, brand }); + break; case 'popular': // popular의 경우는 좀 특별하게 처리 // param은 brand 값이 되고, period는 쿼리 파라미터로 받음 - const period = searchParams.get('period') as Period + const period = searchParams.get('period') as Period; if (!period) { return NextResponse.json( { error: '기간(period)은 필수 파라미터입니다.' }, { status: 400 }, - ) + ); } - result = await getPopular({ brand: param as Brand, period }) - break + result = await getPopular({ brand: param as Brand, period }); + break; default: - return NextResponse.json({ error: '지원하지 않는 검색 유형입니다' }, { status: 400 }) + return NextResponse.json({ error: '지원하지 않는 검색 유형입니다' }, { status: 400 }); } if (!result) { - return NextResponse.json({ error: '검색 결과가 없습니다' }, { status: 404 }) + return NextResponse.json({ error: '검색 결과가 없습니다' }, { status: 404 }); } - return NextResponse.json({ data: result }) + return NextResponse.json({ data: result }); } catch (error) { - console.error('API 요청 오류:', error) - return NextResponse.json({ error: '서버 오류가 발생했습니다' }, { status: 500 }) + console.error('API 요청 오류:', error); + return NextResponse.json({ error: '서버 오류가 발생했습니다' }, { status: 500 }); } } diff --git a/apps/web/app/components/ui/button.tsx b/apps/web/app/components/ui/button.tsx index a2df8dc..b579275 100644 --- a/apps/web/app/components/ui/button.tsx +++ b/apps/web/app/components/ui/button.tsx @@ -1,39 +1,36 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", { variants: { variant: { - default: - "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", - ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", - link: "text-primary underline-offset-4 hover:underline", + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', }, size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', }, }, defaultVariants: { - variant: "default", - size: "default", + variant: 'default', + size: 'default', }, - } -) + }, +); function Button({ className, @@ -41,11 +38,11 @@ function Button({ size, asChild = false, ...props -}: React.ComponentProps<"button"> & +}: React.ComponentProps<'button'> & VariantProps & { - asChild?: boolean + asChild?: boolean; }) { - const Comp = asChild ? Slot : "button" + const Comp = asChild ? Slot : 'button'; return ( - ) + ); } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/apps/web/app/components/ui/card.tsx b/apps/web/app/components/ui/card.tsx index d05bbc6..8dd598f 100644 --- a/apps/web/app/components/ui/card.tsx +++ b/apps/web/app/components/ui/card.tsx @@ -1,92 +1,75 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -function Card({ className, ...props }: React.ComponentProps<"div">) { +function Card({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -function CardHeader({ className, ...props }: React.ComponentProps<"div">) { +function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -function CardTitle({ className, ...props }: React.ComponentProps<"div">) { +function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { +function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -function CardAction({ className, ...props }: React.ComponentProps<"div">) { +function CardAction({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -function CardContent({ className, ...props }: React.ComponentProps<"div">) { - return ( -
    - ) +function CardContent({ className, ...props }: React.ComponentProps<'div'>) { + return
    ; } -function CardFooter({ className, ...props }: React.ComponentProps<"div">) { +function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { return (
    - ) + ); } -export { - Card, - CardHeader, - CardFooter, - CardTitle, - CardAction, - CardDescription, - CardContent, -} +export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }; diff --git a/apps/web/app/error.tsx b/apps/web/app/error.tsx index 169dcca..7740aab 100644 --- a/apps/web/app/error.tsx +++ b/apps/web/app/error.tsx @@ -1,23 +1,23 @@ -'use client' +'use client'; type ErrorPageProps = { - error: Error - reset: () => void -} + error: Error; + reset: () => void; +}; interface AuthError { - code: string - message: string - type: string + code: string; + message: string; + type: string; } export default function Error({ error, reset }: ErrorPageProps) { - const errorMessage = error.message - let errorDetails: AuthError | null = null + const errorMessage = error.message; + let errorDetails: AuthError | null = null; // 에러 메시지 파싱 시도 try { - errorDetails = JSON.parse(error.message) as AuthError + errorDetails = JSON.parse(error.message) as AuthError; } catch { // 파싱 실패 시 기본 메시지 사용 } @@ -43,5 +43,5 @@ export default function Error({ error, reset }: ErrorPageProps) {
    - ) + ); } diff --git a/apps/web/app/error/page.tsx b/apps/web/app/error/page.tsx index f4fa6df..33dc9c7 100644 --- a/apps/web/app/error/page.tsx +++ b/apps/web/app/error/page.tsx @@ -1,10 +1,10 @@ -'use client' +'use client'; -import { useSearchParams } from 'next/navigation' +import { useSearchParams } from 'next/navigation'; export default function ErrorPage() { - const searchParams = useSearchParams() - const errorMessage = searchParams.get('message') + const searchParams = useSearchParams(); + const errorMessage = searchParams.get('message'); return (
    @@ -23,5 +23,5 @@ export default function ErrorPage() {
    - ) + ); } diff --git a/apps/web/app/errorWrapper.tsx b/apps/web/app/errorWrapper.tsx index 58c3a2a..1507d08 100644 --- a/apps/web/app/errorWrapper.tsx +++ b/apps/web/app/errorWrapper.tsx @@ -1,16 +1,16 @@ -'use client' +'use client'; -import { useSearchParams } from 'next/navigation' -import { useEffect } from 'react' +import { useSearchParams } from 'next/navigation'; +import { useEffect } from 'react'; export default function ErrorWrapper({ children }: { children: React.ReactNode }) { - const searchParams = useSearchParams() + const searchParams = useSearchParams(); useEffect(() => { // 에러 파라미터 확인 - const error = searchParams.get('error') - const errorCode = searchParams.get('error_code') - const errorDescription = searchParams.get('error_description') + const error = searchParams.get('error'); + const errorCode = searchParams.get('error_code'); + const errorDescription = searchParams.get('error_description'); if (error) { // 에러 정보를 구조화 @@ -18,12 +18,12 @@ export default function ErrorWrapper({ children }: { children: React.ReactNode } code: errorCode || 'unknown', message: errorDescription?.replace(/\+/g, ' ') || '인증 오류가 발생했습니다.', type: error, - } + }; // 에러를 throw하여 Error Boundary 트리거 - throw new Error(JSON.stringify(errorMessage)) + throw new Error(JSON.stringify(errorMessage)); } - }, [searchParams]) + }, [searchParams]); - return children + return children; } diff --git a/apps/web/app/find/page.tsx b/apps/web/app/find/page.tsx index e67b130..1ce90b9 100644 --- a/apps/web/app/find/page.tsx +++ b/apps/web/app/find/page.tsx @@ -1,14 +1,14 @@ -'use client' +'use client'; -import { getComposer } from '@repo/api' -import { useState } from 'react' +import { getComposer } from '@repo/api'; +import { useState } from 'react'; export default function Home() { - const [search, setSearch] = useState('') + const [search, setSearch] = useState(''); const handleSearch = (e: React.ChangeEvent) => { - setSearch(e.target.value) - } + setSearch(e.target.value); + }; return (
    @@ -17,5 +17,5 @@ export default function Home() {

    fotter

    - ) + ); } diff --git a/apps/web/app/footer.tsx b/apps/web/app/footer.tsx index 28e32d8..bf64f20 100644 --- a/apps/web/app/footer.tsx +++ b/apps/web/app/footer.tsx @@ -1,24 +1,24 @@ -'use client' +'use client'; -import { Button } from '@/components/ui/button' -import { cn } from '@/lib/utils' -import Link from 'next/link' -import { usePathname } from 'next/navigation' +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; const navigation = [ { name: '부를 곡', href: '/' }, { name: '검색', href: '/search' }, { name: '인기곡', href: '/popular' }, { name: '라이브러리', href: '/library' }, -] +]; export default function Footer() { - const pathname = usePathname() + const pathname = usePathname(); return (
    {navigation.map(item => { - const isActive = pathname === item.href + const isActive = pathname === item.href; return ( - ) + ); })}
    - ) + ); } diff --git a/apps/web/app/lib/utils.ts b/apps/web/app/lib/utils.ts index bd0c391..2819a83 100644 --- a/apps/web/app/lib/utils.ts +++ b/apps/web/app/lib/utils.ts @@ -1,6 +1,6 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/apps/web/app/library/page.tsx b/apps/web/app/library/page.tsx index 8615cac..fccabb7 100644 --- a/apps/web/app/library/page.tsx +++ b/apps/web/app/library/page.tsx @@ -4,5 +4,5 @@ export default function LibraryPage() {

    라이브러리 페이지

    준비 중입니다...

    - ) + ); } diff --git a/apps/web/app/login/KakaoLoginButton.tsx b/apps/web/app/login/KakaoLoginButton.tsx index 4fec03f..a5825f7 100644 --- a/apps/web/app/login/KakaoLoginButton.tsx +++ b/apps/web/app/login/KakaoLoginButton.tsx @@ -1,21 +1,21 @@ -'use client' +'use client'; -import { createClient } from '@/supabase/client' // 클라이언트용 Supabase 클라이언트 +import { createClient } from '@/supabase/client'; // 클라이언트용 Supabase 클라이언트 export default function KakaoLoginButton() { const handleKakaoLogin = async () => { - const supabase = createClient() + const supabase = createClient(); const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'kakao', - }) + }); - console.log('data : ', data) - console.log('error : ', error) - } + console.log('data : ', data); + console.log('error : ', error); + }; return ( - ) + ); } diff --git a/apps/web/app/login/actions.ts b/apps/web/app/login/actions.ts index 912c928..155ff92 100644 --- a/apps/web/app/login/actions.ts +++ b/apps/web/app/login/actions.ts @@ -1,42 +1,42 @@ -'use server' +'use server'; -import { revalidatePath } from 'next/cache' -import { redirect } from 'next/navigation' +import { revalidatePath } from 'next/cache'; +import { redirect } from 'next/navigation'; -import { createClient } from '@/supabase/server' +import { createClient } from '@/supabase/server'; export async function login(formData: FormData) { - const supabase = await createClient() + const supabase = await createClient(); const data = { email: formData.get('email') as string, password: formData.get('password') as string, - } + }; - const response = await supabase.auth.signInWithPassword(data) + const response = await supabase.auth.signInWithPassword(data); if (response.error) { throw new Error( JSON.stringify({ code: response.error.status || 500, message: response.error.message, }), - ) + ); } - revalidatePath('/', 'layout') - redirect('/') + revalidatePath('/', 'layout'); + redirect('/'); } export async function register(formData: FormData) { - const supabase = await createClient() + const supabase = await createClient(); const data = { email: formData.get('email') as string, password: formData.get('password') as string, - } + }; - const response = await supabase.auth.signUp(data) - console.log('response : ', response) + const response = await supabase.auth.signUp(data); + console.log('response : ', response); if (response.error) { // 에러를 클라이언트에 전달 @@ -45,9 +45,9 @@ export async function register(formData: FormData) { code: response.error.status || 500, message: response.error.message, }), - ) + ); } - revalidatePath('/', 'layout') - redirect('/') + revalidatePath('/', 'layout'); + redirect('/'); } diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index 9ee21a5..46ff90e 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -1,15 +1,15 @@ -import { redirect } from 'next/navigation' -import { createClient } from '@/supabase/server' -import { login, register } from './actions' -import KakaoLoginButton from './KakaoLoginButton' // 새로운 클라이언트 컴포넌트 +import { redirect } from 'next/navigation'; +import { createClient } from '@/supabase/server'; +import { login, register } from './actions'; +import KakaoLoginButton from './KakaoLoginButton'; // 새로운 클라이언트 컴포넌트 export default async function LoginPage() { - const supabase = await createClient() + const supabase = await createClient(); - const { data } = await supabase.auth.getUser() + const { data } = await supabase.auth.getUser(); if (data) { - console.log('data : ', data) + console.log('data : ', data); // redirect('/'); } @@ -23,5 +23,5 @@ export default async function LoginPage() { - ) + ); } diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index dd082d2..8235ac9 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -1,7 +1,7 @@ -'use client' +'use client'; -import Home from '@/home/page' +import Home from '@/home/page'; export default function HomePage() { - return + return ; } diff --git a/apps/web/app/popular/page.tsx b/apps/web/app/popular/page.tsx index 8a42570..c0bfe26 100644 --- a/apps/web/app/popular/page.tsx +++ b/apps/web/app/popular/page.tsx @@ -4,5 +4,5 @@ export default function PopularPage() {

    인기 페이지

    준비 중입니다...

    - ) + ); } diff --git a/apps/web/app/query.tsx b/apps/web/app/query.tsx index 4cc5c73..f38e2f9 100644 --- a/apps/web/app/query.tsx +++ b/apps/web/app/query.tsx @@ -1,11 +1,11 @@ -'use client' -import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { useState } from 'react' +'use client'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useState } from 'react'; const QueryProvider = ({ children }: { children: React.ReactNode }) => { - const [queryClient] = useState(() => new QueryClient()) + const [queryClient] = useState(() => new QueryClient()); - return {children} -} + return {children}; +}; -export default QueryProvider +export default QueryProvider; diff --git a/apps/web/app/search/page.tsx b/apps/web/app/search/page.tsx index 1605558..7d9d8f6 100644 --- a/apps/web/app/search/page.tsx +++ b/apps/web/app/search/page.tsx @@ -4,5 +4,5 @@ export default function SearchPage() {

    검색 페이지

    준비 중입니다...

    - ) + ); } diff --git a/apps/web/app/supabase/api.ts b/apps/web/app/supabase/api.ts index 8e33bc1..10bd9b0 100644 --- a/apps/web/app/supabase/api.ts +++ b/apps/web/app/supabase/api.ts @@ -1,5 +1,5 @@ -import { createServerClient, serializeCookieHeader } from '@supabase/ssr' -import { type NextApiRequest, type NextApiResponse } from 'next' +import { createServerClient, serializeCookieHeader } from '@supabase/ssr'; +import { type NextApiRequest, type NextApiResponse } from 'next'; // API client @@ -13,7 +13,7 @@ export default function createClient(req: NextApiRequest, res: NextApiResponse) return Object.keys(req.cookies).map(name => ({ name, value: req.cookies[name] || '', - })) + })); }, setAll(cookiesToSet) { res.setHeader( @@ -21,11 +21,11 @@ export default function createClient(req: NextApiRequest, res: NextApiResponse) cookiesToSet.map(({ name, value, options }) => serializeCookieHeader(name, value, options), ), - ) + ); }, }, }, - ) + ); - return supabase + return supabase; } diff --git a/apps/web/app/supabase/client.ts b/apps/web/app/supabase/client.ts index cd34411..6d0e669 100644 --- a/apps/web/app/supabase/client.ts +++ b/apps/web/app/supabase/client.ts @@ -1,4 +1,4 @@ -import { createBrowserClient } from '@supabase/ssr' +import { createBrowserClient } from '@supabase/ssr'; // Component client @@ -8,5 +8,5 @@ export function createClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - ) + ); } diff --git a/apps/web/app/supabase/middleware.ts b/apps/web/app/supabase/middleware.ts index 0ba7ed3..2641178 100644 --- a/apps/web/app/supabase/middleware.ts +++ b/apps/web/app/supabase/middleware.ts @@ -1,27 +1,27 @@ -import { createServerClient } from '@supabase/ssr' -import { NextResponse, type NextRequest } from 'next/server' +import { createServerClient } from '@supabase/ssr'; +import { NextResponse, type NextRequest } from 'next/server'; export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ request, - }) + }); const supabase = createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { - return request.cookies.getAll() + return request.cookies.getAll(); }, setAll(cookiesToSet) { - cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) + cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)); supabaseResponse = NextResponse.next({ request, - }) + }); cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options), - ) + ); }, }, - }) + }); // Do not run code between createServerClient and // supabase.auth.getUser(). A simple mistake could make it very hard to debug @@ -31,7 +31,7 @@ export async function updateSession(request: NextRequest) { const { data: { user }, - } = await supabase.auth.getUser() + } = await supabase.auth.getUser(); if ( !user && @@ -39,9 +39,9 @@ export async function updateSession(request: NextRequest) { !request.nextUrl.pathname.startsWith('/auth') ) { // no user, potentially respond by redirecting the user to the login page - const url = request.nextUrl.clone() - url.pathname = '/login' - return NextResponse.redirect(url) + const url = request.nextUrl.clone(); + url.pathname = '/login'; + return NextResponse.redirect(url); } // IMPORTANT: You *must* return the supabaseResponse object as it is. @@ -57,5 +57,5 @@ export async function updateSession(request: NextRequest) { // If this is not done, you may be causing the browser and server to go out // of sync and terminate the user's session prematurely! - return supabaseResponse + return supabaseResponse; } diff --git a/apps/web/app/supabase/server.ts b/apps/web/app/supabase/server.ts index 2709b36..09736eb 100644 --- a/apps/web/app/supabase/server.ts +++ b/apps/web/app/supabase/server.ts @@ -1,18 +1,18 @@ -import { createServerClient } from '@supabase/ssr' -import { cookies } from 'next/headers' +import { createServerClient } from '@supabase/ssr'; +import { cookies } from 'next/headers'; // Server client export async function createClient() { - const cookieStore = await cookies() + const cookieStore = await cookies(); return createServerClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!, { cookies: { getAll() { - return cookieStore.getAll() + return cookieStore.getAll(); }, setAll(cookiesToSet) { try { - cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)) + cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); } catch { // The `setAll` method was called from a Server Component. // This can be ignored if you have middleware refreshing @@ -20,5 +20,5 @@ export async function createClient() { } }, }, - }) + }); } diff --git a/apps/web/app/test/SearchForm.tsx b/apps/web/app/test/SearchForm.tsx index a29a17e..05b1e71 100644 --- a/apps/web/app/test/SearchForm.tsx +++ b/apps/web/app/test/SearchForm.tsx @@ -1,26 +1,26 @@ -'use client' +'use client'; -import { useState, FormEvent } from 'react' -import { useRouter } from 'next/navigation' +import { useState, FormEvent } from 'react'; +import { useRouter } from 'next/navigation'; interface SearchFormProps { - initialSinger?: string + initialSinger?: string; } export function SearchForm({ initialSinger = '' }: SearchFormProps) { - const router = useRouter() - const [singer, setSinger] = useState(initialSinger) + const router = useRouter(); + const [singer, setSinger] = useState(initialSinger); const handleSubmit = (e: FormEvent) => { - e.preventDefault() + e.preventDefault(); // URL 쿼리 파라미터 업데이트 if (singer) { - router.push(`/test?search=${encodeURIComponent(singer)}`) + router.push(`/test?search=${encodeURIComponent(singer)}`); } else { - router.push('/test') + router.push('/test'); } - } + }; return (
    @@ -40,5 +40,5 @@ export function SearchForm({ initialSinger = '' }: SearchFormProps) {
    - ) + ); } diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx index 8c2ca6b..1a33bbe 100644 --- a/apps/web/app/test/page.tsx +++ b/apps/web/app/test/page.tsx @@ -1,32 +1,32 @@ -import { getSinger, getNo, getPopular } from '@repo/api' -import { SearchForm } from './SearchForm' +import { getSinger, getNo, getPopular } from '@repo/api'; +import { SearchForm } from './SearchForm'; // 서버 컴포넌트 (기본적으로 서버에서 실행됨) export default async function TestPage({ searchParams }: { searchParams: { search?: string } }) { // URL 쿼리 파라미터에서 singer 값을 가져옴 - const search = searchParams.search + const search = searchParams.search; // 검색어가 있을 때만 API 호출 - let data = null - let error = null - const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000' + let data = null; + let error = null; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; if (search) { try { // TJ, 금영의 경우 해외(일본)곡 검색 못 함 // 크롤링 돌려서 DB에다가 집어넣어야 할 듯 - data = await getSinger({ singer: search, brand: 'tj' }) - const other = await getSinger({ singer: search, brand: 'kumyoung' }) + data = await getSinger({ singer: search, brand: 'tj' }); + const other = await getSinger({ singer: search, brand: 'kumyoung' }); // data = await getPopular({ brand: 'tj', period: 'weekly' }); // const other = await fetch(`http://localhost:3000/api/songs/singer?singer=아이유`); // const other = await fetch(`${baseUrl}/api/songs/singer/IU?brand=tj`); // const otherData = await other.json(); - console.log('other : ', other) - console.log('datajson : ', JSON.stringify(data, null, 2)) + console.log('other : ', other); + console.log('datajson : ', JSON.stringify(data, null, 2)); } catch (err) { - error = err instanceof Error ? err.message : '알 수 없는 오류' - console.error('API 호출 오류:', err) + error = err instanceof Error ? err.message : '알 수 없는 오류'; + console.error('API 호출 오류:', err); } } @@ -48,5 +48,5 @@ export default async function TestPage({ searchParams }: { searchParams: { searc
    )}
    - ) + ); } diff --git a/apps/web/app/testing-table/button.tsx b/apps/web/app/testing-table/button.tsx index f1c8ecd..6e62a34 100644 --- a/apps/web/app/testing-table/button.tsx +++ b/apps/web/app/testing-table/button.tsx @@ -1,21 +1,21 @@ -'use client' +'use client'; -import { createClient } from '@/supabase/client' +import { createClient } from '@/supabase/client'; const Button = () => { - const supabase = createClient() + const supabase = createClient(); const handleInsertData = async () => { - const { data, error } = await supabase.from('test').insert({ name: 'testing' }) - console.log('data : ', data) - console.log('error : ', error) - } + const { data, error } = await supabase.from('test').insert({ name: 'testing' }); + console.log('data : ', data); + console.log('error : ', error); + }; return ( - ) -} + ); +}; -export default Button +export default Button; diff --git a/apps/web/app/testing-table/page.tsx b/apps/web/app/testing-table/page.tsx index 01bff9e..eda9224 100644 --- a/apps/web/app/testing-table/page.tsx +++ b/apps/web/app/testing-table/page.tsx @@ -1,20 +1,20 @@ -import { createClient } from '@/supabase/server' -import Button from './button' +import { createClient } from '@/supabase/server'; +import Button from './button'; export default async function Instruments() { - const supabase = await createClient() - console.log('supabase : ', supabase) - const data = await supabase.from('test').select() + const supabase = await createClient(); + console.log('supabase : ', supabase); + const data = await supabase.from('test').select(); - const newData = await supabase.from('test').select('*') + const newData = await supabase.from('test').select('*'); - console.log('data : ', data) - console.log('newData : ', newData) + console.log('data : ', data); + console.log('newData : ', newData); return (
           {JSON.stringify(newData, null, 2)}
           
    - ) + ); } diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 7329063..5e891cf 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -1,7 +1,7 @@ -import type { NextConfig } from 'next' +import type { NextConfig } from 'next'; const nextConfig: NextConfig = { /* config options here */ -} +}; -export default nextConfig +export default nextConfig; From c99536a30e3ddcbbba69370db95a56524f339e91 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Wed, 26 Mar 2025 18:33:05 +0900 Subject: [PATCH 15/54] =?UTF-8?q?feat=20:=20cheerio=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=ED=81=AC=EB=A1=A4=EB=A7=81=ED=95=B4=EC=84=9C=20sup?= =?UTF-8?q?abase=20DB=20=EC=B6=94=EA=B0=80=20=EC=84=B1=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/crawling/package.json | 15 +++++++++++ packages/crawling/src/crawlWiki.js | 40 ++++++++++++++++++++++++++++++ packages/crawling/src/index.js | 11 ++++++++ packages/crawling/src/postDB.js | 28 +++++++++++++++++++++ packages/crawling/src/utils.js | 4 +++ 5 files changed, 98 insertions(+) create mode 100644 packages/crawling/package.json create mode 100644 packages/crawling/src/crawlWiki.js create mode 100644 packages/crawling/src/index.js create mode 100644 packages/crawling/src/postDB.js create mode 100644 packages/crawling/src/utils.js diff --git a/packages/crawling/package.json b/packages/crawling/package.json new file mode 100644 index 0000000..0812f60 --- /dev/null +++ b/packages/crawling/package.json @@ -0,0 +1,15 @@ +{ + "name": "@repo/crawling", + "version": "0.0.0", + "type": "module", + "main": "./src/index.js", + "exports": { + ".": "./src/index.js" + }, + "dependencies": { + "@supabase/supabase-js": "^2.49.1", + "axios": "^1.5.0", + "cheerio": "^1.0.0", + "dotenv": "^16.4.7" + } +} diff --git a/packages/crawling/src/crawlWiki.js b/packages/crawling/src/crawlWiki.js new file mode 100644 index 0000000..414e08d --- /dev/null +++ b/packages/crawling/src/crawlWiki.js @@ -0,0 +1,40 @@ +import axios from 'axios'; +import * as cheerio from 'cheerio'; +import dotenv from 'dotenv'; + +import { isNumber } from './utils.js'; +dotenv.config(); + +// ✅ 나무위키에서 데이터 크롤링 +export async function scrapeSongs(dst) { + const url = process.env.NAMU_KARAOKE_URL; + const endURL = process.env.NAMU_KARAOKE_END_URL; + const fullURL = url + dst + endURL; + try { + const { data } = await axios.get(fullURL); + // const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); + const $ = cheerio.load(data); + + let songs = []; + + $('table tbody tr').each((index, element) => { + const cols = $(element).find('td'); + if (cols.length == 3) { + const title = $(cols[0]).text().trim(); + const num_tj = $(cols[1]).text().trim().slice(0, 5); + const num_ky = $(cols[2]).text().trim().slice(0, 5); + if (isNumber(num_tj) || isNumber(num_ky)) { + songs.push({ title, artist: '아이묭 (あいみょん)', num_tj, num_ky }); + } + } + }); + + console.log(songs); // 크롤링한 데이터 확인 + console.log(songs.length); // 크롤링한 데이터 확인 + + return songs; + } catch (error) { + console.error('크롤링 실패:', error); + return []; + } +} diff --git a/packages/crawling/src/index.js b/packages/crawling/src/index.js new file mode 100644 index 0000000..60db1e3 --- /dev/null +++ b/packages/crawling/src/index.js @@ -0,0 +1,11 @@ +import { scrapeSongs } from './CrawlWiki.js'; +import { postDB } from './postDB.js'; + +const main = async () => { + const songs = await scrapeSongs('아이묭'); + await postDB(songs); +}; + +main(); + +// 크롤링 후 데이터 확인 diff --git a/packages/crawling/src/postDB.js b/packages/crawling/src/postDB.js new file mode 100644 index 0000000..01dffa7 --- /dev/null +++ b/packages/crawling/src/postDB.js @@ -0,0 +1,28 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { createClient } from '@supabase/supabase-js'; + +export async function postDB(songs) { + const supabaseUrl = process.env.SUPABASE_URL; + const supabaseKey = process.env.SUPABASE_KEY; + + const supabase = createClient(supabaseUrl, supabaseKey); + + let { data, error } = await supabase + .from('songs') + .upsert(songs, { + onConflict: ['title', 'artist'], + }) + .select(); + + console.log('res : ', data); + + if (!error) { + console.log('✅ Supabase에 데이터 저장 완료!'); + } else { + console.error('❌ Supabase 저장 실패:', error); + } +} + +// postDB({ title: '아이묭dd', artist: '아이묭 (あいみょん)', num_tj: 1, num_ky: 1 }); diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js new file mode 100644 index 0000000..9389517 --- /dev/null +++ b/packages/crawling/src/utils.js @@ -0,0 +1,4 @@ +// 정규식으로 숫자 확인 +export const isNumber = (str) => { + return /^\d+$/.test(str); +}; From 957cad35a313a651c8b64a1c01cba2260c47928a Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Wed, 26 Mar 2025 18:33:52 +0900 Subject: [PATCH 16/54] =?UTF-8?q?refactor=20:=20trivago/prettier-plugin-so?= =?UTF-8?q?rt-imports=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/.prettierrc.cjs | 5 +- apps/web/app/api/auth/callback/route.ts | 1 + .../web/app/api/songs/[type]/[param]/route.ts | 11 +- apps/web/app/components/ui/button.tsx | 4 +- apps/web/app/find/page.tsx | 3 +- apps/web/app/footer.tsx | 5 +- apps/web/app/home/SongCard.tsx | 3 +- apps/web/app/home/SongList.tsx | 9 +- apps/web/app/home/TestDND.tsx | 6 +- apps/web/app/home/TestDNDHandle.tsx | 6 +- apps/web/app/home/page.tsx | 1 - apps/web/app/layout.tsx | 4 +- apps/web/app/lib/utils.ts | 2 +- apps/web/app/login/KakaoLoginButton.tsx | 4 +- apps/web/app/login/page.tsx | 6 +- apps/web/app/query.tsx | 1 + apps/web/app/supabase/middleware.ts | 2 +- apps/web/app/test/SearchForm.tsx | 2 +- apps/web/app/test/page.tsx | 3 +- apps/web/app/testing-table/page.tsx | 1 + apps/web/package.json | 1 + pnpm-lock.yaml | 194 +++++++++++++++++- 22 files changed, 241 insertions(+), 33 deletions(-) diff --git a/apps/web/.prettierrc.cjs b/apps/web/.prettierrc.cjs index d825bd6..7f9f8ab 100644 --- a/apps/web/.prettierrc.cjs +++ b/apps/web/.prettierrc.cjs @@ -1,5 +1,4 @@ module.exports = { - plugins: ['prettier-plugin-tailwindcss'], // Tailwind 클래스 정렬 (문자열로 변경) printWidth: 100, // 한 줄 최대 길이 tabWidth: 2, // 탭 크기 (스페이스 2칸) singleQuote: true, // 작은따옴표 사용 @@ -8,4 +7,8 @@ module.exports = { bracketSpacing: true, // 중괄호 간격 유지 (ex: { foo: bar }) jsxSingleQuote: false, // JSX에서 작은따옴표 사용 안 함 endOfLine: 'lf', // 줄바꿈 형식 (LF 고정) + importOrder: ['', '^@repo/(.*)$', '^@/(.*)$', '^../(.*)$', '^./(.*)$'], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + plugins: ['prettier-plugin-tailwindcss', '@trivago/prettier-plugin-sort-imports'], // Tailwind 클래스 정렬 (문자열로 변경) }; diff --git a/apps/web/app/api/auth/callback/route.ts b/apps/web/app/api/auth/callback/route.ts index e4123b7..4361aea 100644 --- a/apps/web/app/api/auth/callback/route.ts +++ b/apps/web/app/api/auth/callback/route.ts @@ -1,4 +1,5 @@ import { NextResponse } from 'next/server'; + // The client you created from the Server-Side Auth instructions import { createClient } from '@/supabase/server'; diff --git a/apps/web/app/api/songs/[type]/[param]/route.ts b/apps/web/app/api/songs/[type]/[param]/route.ts index f956134..7ed8d01 100644 --- a/apps/web/app/api/songs/[type]/[param]/route.ts +++ b/apps/web/app/api/songs/[type]/[param]/route.ts @@ -1,15 +1,16 @@ // app/api/songs/[type]/[param]/route.ts import { NextRequest, NextResponse } from 'next/server'; + import { - getSong, - getSinger, + Brand, + Period, getComposer, getLyricist, getNo, - getRelease, getPopular, - Brand, - Period, + getRelease, + getSinger, + getSong, } from '@repo/api'; export async function GET( diff --git a/apps/web/app/components/ui/button.tsx b/apps/web/app/components/ui/button.tsx index b579275..19bc011 100644 --- a/apps/web/app/components/ui/button.tsx +++ b/apps/web/app/components/ui/button.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; -import { cva, type VariantProps } from 'class-variance-authority'; +import { type VariantProps, cva } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/apps/web/app/find/page.tsx b/apps/web/app/find/page.tsx index 1ce90b9..e7b8eea 100644 --- a/apps/web/app/find/page.tsx +++ b/apps/web/app/find/page.tsx @@ -1,8 +1,9 @@ 'use client'; -import { getComposer } from '@repo/api'; import { useState } from 'react'; +import { getComposer } from '@repo/api'; + export default function Home() { const [search, setSearch] = useState(''); diff --git a/apps/web/app/footer.tsx b/apps/web/app/footer.tsx index bf64f20..24c415e 100644 --- a/apps/web/app/footer.tsx +++ b/apps/web/app/footer.tsx @@ -1,10 +1,11 @@ 'use client'; -import { Button } from '@/components/ui/button'; -import { cn } from '@/lib/utils'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; +import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + const navigation = [ { name: '부를 곡', href: '/' }, { name: '검색', href: '/search' }, diff --git a/apps/web/app/home/SongCard.tsx b/apps/web/app/home/SongCard.tsx index 3b5f197..aeac80f 100644 --- a/apps/web/app/home/SongCard.tsx +++ b/apps/web/app/home/SongCard.tsx @@ -2,7 +2,8 @@ import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; -import { Mic, Trash, ChevronsUp, ChevronsDown, GripVertical } from 'lucide-react'; +import { ChevronsDown, ChevronsUp, GripVertical, Mic, Trash } from 'lucide-react'; + import { Card } from '@/components/ui/card'; import { cn } from '@/lib/utils'; diff --git a/apps/web/app/home/SongList.tsx b/apps/web/app/home/SongList.tsx index cc0e548..d55a866 100644 --- a/apps/web/app/home/SongList.tsx +++ b/apps/web/app/home/SongList.tsx @@ -1,21 +1,22 @@ 'use client'; -import { useState } from 'react'; import { DndContext, - closestCenter, KeyboardSensor, PointerSensor, + closestCenter, useSensor, useSensors, } from '@dnd-kit/core'; +import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import { - arrayMove, SortableContext, + arrayMove, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; -import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; +import { useState } from 'react'; + import SongCard from './SongCard'; // 초기 노래 데이터 diff --git a/apps/web/app/home/TestDND.tsx b/apps/web/app/home/TestDND.tsx index c356be1..06d610d 100644 --- a/apps/web/app/home/TestDND.tsx +++ b/apps/web/app/home/TestDND.tsx @@ -1,14 +1,14 @@ 'use client'; -import { useState } from 'react'; import { DndContext, closestCenter } from '@dnd-kit/core'; import { - arrayMove, SortableContext, - verticalListSortingStrategy, + arrayMove, useSortable, + verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; +import { useState } from 'react'; const defaultItems = ['Apple', 'Banana', 'Cherry', 'Date']; diff --git a/apps/web/app/home/TestDNDHandle.tsx b/apps/web/app/home/TestDNDHandle.tsx index 6b09b13..10d09e1 100644 --- a/apps/web/app/home/TestDNDHandle.tsx +++ b/apps/web/app/home/TestDNDHandle.tsx @@ -1,15 +1,15 @@ 'use client'; -import { useState } from 'react'; import { DndContext, closestCenter } from '@dnd-kit/core'; import { - arrayMove, SortableContext, - verticalListSortingStrategy, + arrayMove, useSortable, + verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { GripVertical } from 'lucide-react'; +import { useState } from 'react'; const defaultItems = ['Apple', 'Banana', 'Cherry', 'Date']; diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx index 5613aec..747931f 100644 --- a/apps/web/app/home/page.tsx +++ b/apps/web/app/home/page.tsx @@ -1,5 +1,4 @@ import SongList from './SongList'; - import TestDND from './TestDND'; import TestDNDHandle from './TestDNDHandle'; diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 78af0d3..992cd45 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,9 +1,9 @@ import type { Metadata } from 'next'; -import QueryProvider from './query'; + import ErrorWrapper from './ErrorWrapper'; import Footer from './footer'; - import './globals.css'; +import QueryProvider from './query'; export const metadata: Metadata = { title: 'Singcode', diff --git a/apps/web/app/lib/utils.ts b/apps/web/app/lib/utils.ts index 2819a83..9ad0df4 100644 --- a/apps/web/app/lib/utils.ts +++ b/apps/web/app/lib/utils.ts @@ -1,4 +1,4 @@ -import { clsx, type ClassValue } from 'clsx'; +import { type ClassValue, clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { diff --git a/apps/web/app/login/KakaoLoginButton.tsx b/apps/web/app/login/KakaoLoginButton.tsx index a5825f7..78fcd08 100644 --- a/apps/web/app/login/KakaoLoginButton.tsx +++ b/apps/web/app/login/KakaoLoginButton.tsx @@ -1,6 +1,8 @@ 'use client'; -import { createClient } from '@/supabase/client'; // 클라이언트용 Supabase 클라이언트 +import { createClient } from '@/supabase/client'; + +// 클라이언트용 Supabase 클라이언트 export default function KakaoLoginButton() { const handleKakaoLogin = async () => { diff --git a/apps/web/app/login/page.tsx b/apps/web/app/login/page.tsx index 46ff90e..d6a733b 100644 --- a/apps/web/app/login/page.tsx +++ b/apps/web/app/login/page.tsx @@ -1,7 +1,11 @@ import { redirect } from 'next/navigation'; + import { createClient } from '@/supabase/server'; + +import KakaoLoginButton from './KakaoLoginButton'; import { login, register } from './actions'; -import KakaoLoginButton from './KakaoLoginButton'; // 새로운 클라이언트 컴포넌트 + +// 새로운 클라이언트 컴포넌트 export default async function LoginPage() { const supabase = await createClient(); diff --git a/apps/web/app/query.tsx b/apps/web/app/query.tsx index f38e2f9..89663fc 100644 --- a/apps/web/app/query.tsx +++ b/apps/web/app/query.tsx @@ -1,4 +1,5 @@ 'use client'; + import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { useState } from 'react'; diff --git a/apps/web/app/supabase/middleware.ts b/apps/web/app/supabase/middleware.ts index 2641178..7b4dee2 100644 --- a/apps/web/app/supabase/middleware.ts +++ b/apps/web/app/supabase/middleware.ts @@ -1,5 +1,5 @@ import { createServerClient } from '@supabase/ssr'; -import { NextResponse, type NextRequest } from 'next/server'; +import { type NextRequest, NextResponse } from 'next/server'; export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ diff --git a/apps/web/app/test/SearchForm.tsx b/apps/web/app/test/SearchForm.tsx index 05b1e71..6f0b94e 100644 --- a/apps/web/app/test/SearchForm.tsx +++ b/apps/web/app/test/SearchForm.tsx @@ -1,7 +1,7 @@ 'use client'; -import { useState, FormEvent } from 'react'; import { useRouter } from 'next/navigation'; +import { FormEvent, useState } from 'react'; interface SearchFormProps { initialSinger?: string; diff --git a/apps/web/app/test/page.tsx b/apps/web/app/test/page.tsx index 1a33bbe..4730438 100644 --- a/apps/web/app/test/page.tsx +++ b/apps/web/app/test/page.tsx @@ -1,4 +1,5 @@ -import { getSinger, getNo, getPopular } from '@repo/api'; +import { getNo, getPopular, getSinger } from '@repo/api'; + import { SearchForm } from './SearchForm'; // 서버 컴포넌트 (기본적으로 서버에서 실행됨) diff --git a/apps/web/app/testing-table/page.tsx b/apps/web/app/testing-table/page.tsx index eda9224..1715267 100644 --- a/apps/web/app/testing-table/page.tsx +++ b/apps/web/app/testing-table/page.tsx @@ -1,4 +1,5 @@ import { createClient } from '@/supabase/server'; + import Button from './button'; export default async function Instruments() { diff --git a/apps/web/package.json b/apps/web/package.json index 2d63843..dedc73a 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -35,6 +35,7 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4.0.15", + "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84f096c..e79fcea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,7 +17,7 @@ importers: version: 3.5.3 prettier-plugin-tailwindcss: specifier: ^0.6.11 - version: 0.6.11(prettier@3.5.3) + version: 0.6.11(@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3))(prettier@3.5.3) turbo: specifier: ^2.4.4 version: 2.4.4 @@ -194,6 +194,9 @@ importers: '@tailwindcss/postcss': specifier: ^4.0.15 version: 4.0.15 + '@trivago/prettier-plugin-sort-imports': + specifier: ^5.2.2 + version: 5.2.2(prettier@3.5.3) '@types/node': specifier: ^20 version: 20.17.24 @@ -241,6 +244,21 @@ importers: specifier: ^5.8.2 version: 5.8.2 + packages/crawling: + dependencies: + '@supabase/supabase-js': + specifier: ^2.49.1 + version: 2.49.1 + axios: + specifier: ^1.5.0 + version: 1.8.3 + cheerio: + specifier: ^1.0.0 + version: 1.0.0 + dotenv: + specifier: ^16.4.7 + version: 16.4.7 + packages/eslint-config: devDependencies: '@eslint/js': @@ -2075,6 +2093,22 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@trivago/prettier-plugin-sort-imports@5.2.2': + resolution: {integrity: sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==} + engines: {node: '>18.12'} + peerDependencies: + '@vue/compiler-sfc': 3.x + prettier: 2.x - 3.x + prettier-plugin-svelte: 3.x + svelte: 4.x || 5.x + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + prettier-plugin-svelte: + optional: true + svelte: + optional: true + '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -2621,6 +2655,9 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bplist-creator@0.0.7: resolution: {integrity: sha512-xp/tcaV3T5PCiaY04mXga7o/TE+t95gqeLmADeBI1CvZtdWTbgBt3uLpvh4UWtenKeBhCV6oVxGk38yZr2uYEA==} @@ -2764,6 +2801,13 @@ packages: charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0: + resolution: {integrity: sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==} + engines: {node: '>=18.17'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -2963,6 +3007,13 @@ packages: css-in-js-utils@3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + cssom@0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} @@ -3122,11 +3173,24 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domexception@4.0.0: resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} engines: {node: '>=12'} deprecated: Use your platform's native DOMException instead + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + dot-case@2.1.1: resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==} @@ -3173,6 +3237,9 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + encoding-sniffer@0.2.0: + resolution: {integrity: sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -3913,6 +3980,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -4235,6 +4305,9 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + javascript-natural-sort@0.7.1: + resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==} + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5013,6 +5086,9 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} @@ -5176,6 +5252,12 @@ packages: resolution: {integrity: sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==} engines: {node: '>=10'} + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + parse5@7.2.1: resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} @@ -6577,6 +6659,10 @@ packages: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} @@ -6584,6 +6670,10 @@ packages: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + whatwg-url-without-unicode@8.0.0-3: resolution: {integrity: sha512-HoKuzZrUlgpz35YO27XgD28uh/WJH4B0+3ttFqRo//lmq+9T/mIOJ6kqmINI9HpUpz1imRC/nR/lxKpJiv0uig==} engines: {node: '>=10'} @@ -8908,6 +8998,18 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} + '@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3)': + dependencies: + '@babel/generator': 7.26.10 + '@babel/parser': 7.26.10 + '@babel/traverse': 7.26.10 + '@babel/types': 7.26.10 + javascript-natural-sort: 0.7.1 + lodash: 4.17.21 + prettier: 3.5.3 + transitivePeerDependencies: + - supports-color + '@tsconfig/node10@1.0.11': {} '@tsconfig/node12@1.0.11': {} @@ -9614,6 +9716,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + boolbase@1.0.0: {} + bplist-creator@0.0.7: dependencies: stream-buffers: 2.2.0 @@ -9785,6 +9889,29 @@ snapshots: charenc@0.0.2: {} + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.1.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.0 + htmlparser2: 9.1.0 + parse5: 7.2.1 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 6.21.1 + whatwg-mimetype: 4.0.0 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -9997,6 +10124,16 @@ snapshots: dependencies: hyphenate-style-name: 1.1.0 + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-what@6.1.0: {} + cssom@0.3.8: {} cssom@0.5.0: {} @@ -10134,10 +10271,28 @@ snapshots: dependencies: esutils: 2.0.3 + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + domelementtype@2.3.0: {} + domexception@4.0.0: dependencies: webidl-conversions: 7.0.0 + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + dot-case@2.1.1: dependencies: no-case: 2.3.2 @@ -10172,6 +10327,11 @@ snapshots: encodeurl@2.0.0: {} + encoding-sniffer@0.2.0: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -11166,6 +11326,13 @@ snapshots: html-escaper@2.0.2: {} + htmlparser2@9.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -11531,6 +11698,8 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + javascript-natural-sort@0.7.1: {} + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -12592,6 +12761,10 @@ snapshots: dependencies: path-key: 3.1.1 + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + nullthrows@1.1.1: {} nwsapi@2.2.18: {} @@ -12799,6 +12972,15 @@ snapshots: dependencies: pngjs: 3.4.0 + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.2.1 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.2.1 + parse5@7.2.1: dependencies: entities: 4.5.0 @@ -12899,9 +13081,11 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-tailwindcss@0.6.11(prettier@3.5.3): + prettier-plugin-tailwindcss@0.6.11(@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3))(prettier@3.5.3): dependencies: prettier: 3.5.3 + optionalDependencies: + '@trivago/prettier-plugin-sort-imports': 5.2.2(prettier@3.5.3) prettier@3.5.3: {} @@ -14311,10 +14495,16 @@ snapshots: dependencies: iconv-lite: 0.6.3 + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-fetch@3.6.20: {} whatwg-mimetype@3.0.0: {} + whatwg-mimetype@4.0.0: {} + whatwg-url-without-unicode@8.0.0-3: dependencies: buffer: 5.7.1 From 805f7f1c11cd29ff8775bb2c8980aac883a52c6e Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Thu, 27 Mar 2025 18:20:04 +0900 Subject: [PATCH 17/54] =?UTF-8?q?feat=20:=20=ED=81=AC=EB=A1=A4=EB=A7=81.?= =?UTF-8?q?=20=EC=A0=95=EA=B7=9C=ED=99=94=20=ED=86=B5=ED=95=B4=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=ED=8C=8C=EC=8B=B1.=20=EB=AA=A8=EB=93=A0?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=ED=86=B5=EA=B3=BC=20=EB=AA=BB?= =?UTF-8?q?=ED=96=88=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/crawling/src/argList.js | 17 +++++++++++++++++ packages/crawling/src/crawlWiki.js | 30 ++++++++++++++++-------------- packages/crawling/src/index.js | 12 ++++++++++-- packages/crawling/src/utils.js | 29 +++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 16 deletions(-) create mode 100644 packages/crawling/src/argList.js diff --git a/packages/crawling/src/argList.js b/packages/crawling/src/argList.js new file mode 100644 index 0000000..8827522 --- /dev/null +++ b/packages/crawling/src/argList.js @@ -0,0 +1,17 @@ +const getObject = (url, artist, titleIndex, tjIndex, kyIndex) => { + return { url, artist, titleIndex, tjIndex, kyIndex }; +}; + +// 테이블 구조 상 불가능했던 문서 +// aiko, AKB48, +// 너무 적은 데이터 +// &TEAM + +export const argList = [ + // url, artist, titleIndex, tjIndex, kyIndex + // getObject('AAA(혼성그룹)', 'AAA', 0, 3, 2), + // getObject('Aimer', 'Aimer', 2, 0, 1), + // getObject('아이묭', '아이묭 (あいみょん)', 0, 1, 2), + getObject('amazarashi', 'amazarashi', 0, 1, 2), + getObject('BUMP OF CHICKEN', 'BUMP OF CHICKEN', 0, 1, 2), +]; diff --git a/packages/crawling/src/crawlWiki.js b/packages/crawling/src/crawlWiki.js index 414e08d..a5d216d 100644 --- a/packages/crawling/src/crawlWiki.js +++ b/packages/crawling/src/crawlWiki.js @@ -2,15 +2,21 @@ import axios from 'axios'; import * as cheerio from 'cheerio'; import dotenv from 'dotenv'; -import { isNumber } from './utils.js'; +import { parseNumber, parseJapaneseText } from './utils.js'; dotenv.config(); // ✅ 나무위키에서 데이터 크롤링 export async function scrapeSongs(dst) { - const url = process.env.NAMU_KARAOKE_URL; - const endURL = process.env.NAMU_KARAOKE_END_URL; - const fullURL = url + dst + endURL; try { + const { url, artist, titleIndex, tjIndex, kyIndex } = dst; + if (!url || !artist) { + throw new Error('url 또는 artist가 없습니다.'); + } + + const baseUrl = process.env.NAMU_KARAOKE_URL; + const endURL = process.env.NAMU_KARAOKE_END_URL; + const fullURL = baseUrl + url + endURL; + console.log(fullURL); const { data } = await axios.get(fullURL); // const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); const $ = cheerio.load(data); @@ -19,19 +25,15 @@ export async function scrapeSongs(dst) { $('table tbody tr').each((index, element) => { const cols = $(element).find('td'); - if (cols.length == 3) { - const title = $(cols[0]).text().trim(); - const num_tj = $(cols[1]).text().trim().slice(0, 5); - const num_ky = $(cols[2]).text().trim().slice(0, 5); - if (isNumber(num_tj) || isNumber(num_ky)) { - songs.push({ title, artist: '아이묭 (あいみょん)', num_tj, num_ky }); - } + + const title = parseJapaneseText($(cols[titleIndex]).text()); + const num_tj = parseNumber($(cols[tjIndex]).text().trim().slice(0, 5)); + const num_ky = parseNumber($(cols[kyIndex]).text().trim().slice(0, 5)); + if (num_tj || num_ky) { + songs.push({ title, artist, num_tj, num_ky }); } }); - console.log(songs); // 크롤링한 데이터 확인 - console.log(songs.length); // 크롤링한 데이터 확인 - return songs; } catch (error) { console.error('크롤링 실패:', error); diff --git a/packages/crawling/src/index.js b/packages/crawling/src/index.js index 60db1e3..3cc6d4b 100644 --- a/packages/crawling/src/index.js +++ b/packages/crawling/src/index.js @@ -1,9 +1,17 @@ import { scrapeSongs } from './CrawlWiki.js'; import { postDB } from './postDB.js'; +import { argList } from './argList.js'; const main = async () => { - const songs = await scrapeSongs('아이묭'); - await postDB(songs); + const postPromises = argList.map(async (arg) => { + const songs = await scrapeSongs(arg); + + // console.log(songs); // 크롤링한 데이터 확인 + // console.log(songs.length); // 크롤링한 데이터 확인 + + await postDB(songs); + }); + await Promise.all(postPromises); }; main(); diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js index 9389517..c72f984 100644 --- a/packages/crawling/src/utils.js +++ b/packages/crawling/src/utils.js @@ -1,4 +1,33 @@ +// + +export const parseNumber = (str) => { + if (str.length < 5 || !isNumber(str)) { + return null; + } + return str; +}; + // 정규식으로 숫자 확인 export const isNumber = (str) => { return /^\d+$/.test(str); }; + +export const parseJapaneseText = (text) => { + const koreanParts = text.match(/[가-힣\s,]+/g); + const koreanText = koreanParts ? koreanParts.join('').trim() : ''; + let result = text + // 일본어 독음 제거 + .replace(/\([^)]*\)/g, ''); + + // 한글 제거 + result = result.replace(/[가-힣,]+/g, '').trim(); + + // 특수 기호 제거 + result = result.replace(/[ⓢⓗⓕⓛ]/g, ''); + + if (koreanText.length > 0) { + result += ` (${koreanText})`; + } + + return result; +}; From 937daa7f1f659989e8a26338feb85268dc810763 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Fri, 28 Mar 2025 13:01:09 +0900 Subject: [PATCH 18/54] =?UTF-8?q?feat=20:=20=EC=B6=94=EA=B0=80=20=EC=9E=91?= =?UTF-8?q?=EC=97=85.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/crawling/src/argList.js | 35 +++++++++++++++++++++++------ packages/crawling/src/crawlWiki.js | 36 ++++++++++++++++++++++++++++++ packages/crawling/src/index.js | 14 +++++++++--- packages/crawling/src/utils.js | 4 ++++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/packages/crawling/src/argList.js b/packages/crawling/src/argList.js index 8827522..7ae0799 100644 --- a/packages/crawling/src/argList.js +++ b/packages/crawling/src/argList.js @@ -3,15 +3,36 @@ const getObject = (url, artist, titleIndex, tjIndex, kyIndex) => { }; // 테이블 구조 상 불가능했던 문서 -// aiko, AKB48, +// aiko, AKB48, 노기자카46, 니시노 카나, 모닝구 무스메, Sound Horizon, 오오츠카 아이, 케야키자카46 // 너무 적은 데이터 -// &TEAM +// &TEAM, NiziU export const argList = [ // url, artist, titleIndex, tjIndex, kyIndex - // getObject('AAA(혼성그룹)', 'AAA', 0, 3, 2), - // getObject('Aimer', 'Aimer', 2, 0, 1), - // getObject('아이묭', '아이묭 (あいみょん)', 0, 1, 2), - getObject('amazarashi', 'amazarashi', 0, 1, 2), - getObject('BUMP OF CHICKEN', 'BUMP OF CHICKEN', 0, 1, 2), + // getObject('AAA(혼성그룹)', 'AAA', 0, 3, 2), + // getObject('Aimer', 'Aimer', 2, 0, 1), + // getObject('amazarashi', '아마자라시 (amazarashi)', 0, 1, 2), + // getObject('BUMP OF CHICKEN', 'BUMP OF CHICKEN', 0, 1, 2), + // getObject('DREAMS COME TRUE(밴드)', 'DREAMS COME TRUE', 0, 1, 2), + // getObject('ELLEGARDEN', '엘르가든 (ELLEGARDEN)', 0, 1, 2), + // getObject('King Gnu', '킹누 (King Gnu)', 0, 1, 2), + // getObject('LiSA', '리사 (LiSA)', 2, 0, 1), + // getObject('Mrs. GREEN APPLE', '미세스그린애플 (Mrs. GREEN APPLE)', 0, 1, 2), + // getObject('Official髭男dism', '오피셜히게단디즘 (Official髭男dism)', 2, 0, 1), + // getObject('Perfume', '퍼퓸 (Perfume)', 0, 1, 2), + // getObject('RADWIMPS', '래드윔프스 (RADWIMPS)', 2, 0, 1), + // getObject('SEKAI NO OWARI', '세카이노오와리 (SEKAI NO OWARI)', 0, 1, 2), + // getObject('SPYAIR', '스파이에어 (SPYAIR)', 2, 0, 1), + // getObject('Vaundy', '바운디 (Vaundy)', 2, 0, 1), + // getObject('w-inds.', 'w-inds.', 0, 1, 2), + // getObject('YOASOBI', '요아소비 (YOASOBI)', 0, 1, 2), + // getObject('계속 한밤중이면 좋을 텐데.', '계속 한밤중이면 좋을 텐데. (즛토마요나카데이이노니)', 2, 0, 1), + // getObject('베리즈코보', '베리즈코보', 0, 1, 2), + // getObject('아라시(아이돌)', '아라시', 0, 1, 2), + // getObject('아이묭', '아이묭 (あいみょん)', 0, 1, 2), + // getObject('요네즈 켄시', '요네즈 켄시', 2, 0, 1), + // getObject('요루시카', '요루시카 (ヨルシカ)', 0, 1, 2), + // getObject('유이카', '유이카', 0, 1, 2), + // getObject('호시노 겐', '호시노 겐', 0, 1, 2), + // getObject('Creepy Nuts', '크리피 넛츠 (Creepy Nuts)', 2, 0, 1), ]; diff --git a/packages/crawling/src/crawlWiki.js b/packages/crawling/src/crawlWiki.js index a5d216d..858401c 100644 --- a/packages/crawling/src/crawlWiki.js +++ b/packages/crawling/src/crawlWiki.js @@ -40,3 +40,39 @@ export async function scrapeSongs(dst) { return []; } } + +export async function scrapeAllSongs(dst) { + try { + const titleIndex = 2; + const artistIndex = 3; + const tjIndex = 0; + const kyIndex = 1; + + const baseUrl = process.env.NAMU_KARAOKE_URL; + const url = '애니메이션%20음악/노래방%20수록%20목록/전체곡%20일람'; + const fullURL = baseUrl + url; + console.log(fullURL); + const { data } = await axios.get(fullURL); + // const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); + const $ = cheerio.load(data); + + let songs = []; + + $('table tbody tr').each((index, element) => { + const cols = $(element).find('td'); + + const title = $(cols[titleIndex]).text(); + const artist = $(cols[artistIndex]).text(); + const num_tj = parseNumber($(cols[tjIndex]).text().trim().slice(0, 5)); + const num_ky = parseNumber($(cols[kyIndex]).text().trim().slice(0, 5)); + if (num_tj || num_ky) { + songs.push({ title, artist, num_tj, num_ky }); + } + }); + + return songs; + } catch (error) { + console.error('크롤링 실패:', error); + return []; + } +} diff --git a/packages/crawling/src/index.js b/packages/crawling/src/index.js index 3cc6d4b..df9baeb 100644 --- a/packages/crawling/src/index.js +++ b/packages/crawling/src/index.js @@ -1,8 +1,8 @@ -import { scrapeSongs } from './CrawlWiki.js'; +import { scrapeAllSongs, scrapeSongs } from './CrawlWiki.js'; import { postDB } from './postDB.js'; import { argList } from './argList.js'; -const main = async () => { +const postSongs = async () => { const postPromises = argList.map(async (arg) => { const songs = await scrapeSongs(arg); @@ -14,6 +14,14 @@ const main = async () => { await Promise.all(postPromises); }; -main(); +const postAllSongs = async () => { + const allSongs = await scrapeAllSongs(); + const postPromises = allSongs.map(async (song) => { + await postDB(song); + }); + await Promise.all(postPromises); +}; +// postSongs(); +postAllSongs(); // 크롤링 후 데이터 확인 diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js index c72f984..cc2ea28 100644 --- a/packages/crawling/src/utils.js +++ b/packages/crawling/src/utils.js @@ -25,6 +25,10 @@ export const parseJapaneseText = (text) => { // 특수 기호 제거 result = result.replace(/[ⓢⓗⓕⓛ]/g, ''); + if (result.length === 0 && koreanText.length > 0) { + return koreanText; + } + if (koreanText.length > 0) { result += ` (${koreanText})`; } From e885091d74a5f505b1b9de53d6c317735bfd6289 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Fri, 28 Mar 2025 21:41:17 +0900 Subject: [PATCH 19/54] =?UTF-8?q?feat=20:=20=EB=82=98=EB=AC=B4=EC=9C=84?= =?UTF-8?q?=ED=82=A4=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A3=BC=EC=9E=85=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .prettierrc.json | 1 - packages/crawling/src/crawlWiki.js | 44 ++++++++++++++++++++++++++---- packages/crawling/src/index.js | 14 ++++++++-- packages/crawling/src/utils.js | 7 +++++ 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/.prettierrc.json b/.prettierrc.json index 41acafa..d31762a 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,6 +1,5 @@ { "$schema": "https://json.schemastore.org/prettierrc", - "semi": true, "tabWidth": 2, "singleQuote": true, "printWidth": 120, diff --git a/packages/crawling/src/crawlWiki.js b/packages/crawling/src/crawlWiki.js index 858401c..56142db 100644 --- a/packages/crawling/src/crawlWiki.js +++ b/packages/crawling/src/crawlWiki.js @@ -2,7 +2,7 @@ import axios from 'axios'; import * as cheerio from 'cheerio'; import dotenv from 'dotenv'; -import { parseNumber, parseJapaneseText } from './utils.js'; +import { parseNumber, parseJapaneseText, parseText } from './utils.js'; dotenv.config(); // ✅ 나무위키에서 데이터 크롤링 @@ -52,8 +52,42 @@ export async function scrapeAllSongs(dst) { const url = '애니메이션%20음악/노래방%20수록%20목록/전체곡%20일람'; const fullURL = baseUrl + url; console.log(fullURL); - const { data } = await axios.get(fullURL); - // const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }); + const { data } = await axios.get(fullURL, { headers: { 'User-Agent': 'Mozilla/5.0' } }); + const $ = cheerio.load(data); + + let songs = []; + + $('table tbody tr').each((index, element) => { + const cols = $(element).find('td'); + + const title = parseText($(cols[titleIndex]).text()); + const artist = parseText($(cols[artistIndex]).text()); + const num_tj = parseNumber($(cols[tjIndex]).text().trim().slice(0, 5)); + const num_ky = parseNumber($(cols[kyIndex]).text().trim().slice(0, 5)); + if (num_tj || num_ky) { + songs.push({ title, artist, num_tj, num_ky }); + } + }); + + return songs; + } catch (error) { + console.error('크롤링 실패:', error); + return []; + } +} + +export async function scrapeUtaiteSongs() { + try { + const titleIndex = 2; + const artistIndex = 3; + const tjIndex = 0; + const kyIndex = 1; + + const baseUrl = process.env.NAMU_KARAOKE_URL; + const url = '우타이테 오리지널 곡/노래방 수록 목록'; + const fullURL = baseUrl + url; + console.log(fullURL); + const { data } = await axios.get(fullURL, { headers: { 'User-Agent': 'Mozilla/5.0' } }); const $ = cheerio.load(data); let songs = []; @@ -61,8 +95,8 @@ export async function scrapeAllSongs(dst) { $('table tbody tr').each((index, element) => { const cols = $(element).find('td'); - const title = $(cols[titleIndex]).text(); - const artist = $(cols[artistIndex]).text(); + const title = parseText($(cols[titleIndex]).text()); + const artist = parseText($(cols[artistIndex]).text()); const num_tj = parseNumber($(cols[tjIndex]).text().trim().slice(0, 5)); const num_ky = parseNumber($(cols[kyIndex]).text().trim().slice(0, 5)); if (num_tj || num_ky) { diff --git a/packages/crawling/src/index.js b/packages/crawling/src/index.js index df9baeb..45e3cc1 100644 --- a/packages/crawling/src/index.js +++ b/packages/crawling/src/index.js @@ -1,4 +1,4 @@ -import { scrapeAllSongs, scrapeSongs } from './CrawlWiki.js'; +import { scrapeAllSongs, scrapeSongs, scrapeUtaiteSongs } from './CrawlWiki.js'; import { postDB } from './postDB.js'; import { argList } from './argList.js'; @@ -22,6 +22,16 @@ const postAllSongs = async () => { await Promise.all(postPromises); }; +const postUtaiteSongs = async () => { + const utaiteSongs = await scrapeUtaiteSongs(); + const postPromises = utaiteSongs.map(async (song) => { + await postDB(song); + }); + await Promise.all(postPromises); +}; + // postSongs(); -postAllSongs(); +// postAllSongs(); +// postUtaiteSongs(); + // 크롤링 후 데이터 확인 diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js index cc2ea28..6dd1e03 100644 --- a/packages/crawling/src/utils.js +++ b/packages/crawling/src/utils.js @@ -35,3 +35,10 @@ export const parseJapaneseText = (text) => { return result; }; + +export const parseText = (text) => { + return text + .replace(/\[[^\]]*\]/g, '') + .replace(/[※★☆○●◎◇◆□■△▲▽▼→←↑↓↔]/g, '') + .trim(); +}; From 7954741034abf3c307cbb5d58ac3081a4b605b43 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Fri, 28 Mar 2025 21:50:15 +0900 Subject: [PATCH 20/54] =?UTF-8?q?feat=20:=20footer=20=EC=83=89=EA=B9=94=20?= =?UTF-8?q?=EC=9E=84=EC=8B=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/footer.tsx | 2 +- apps/web/app/globals.css | 6 ++++-- apps/web/app/home/page.tsx | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/app/footer.tsx b/apps/web/app/footer.tsx index 24c415e..257944b 100644 --- a/apps/web/app/footer.tsx +++ b/apps/web/app/footer.tsx @@ -26,7 +26,7 @@ export default function Footer() { key={item.name} className={cn( 'w-[90px] flex-auto', - isActive ? 'bg-secondary text-primary' : 'text-text-secondary hover:text-primary', + isActive ? 'bg-accent text-primary' : 'text-text-secondary hover:text-primary', )} variant="ghost" > diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index d223c2a..a118dcc 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -77,8 +77,10 @@ --secondary-foreground: oklch(0.205 0 0); --muted: oklch(0.97 0 0); --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); + --accent: oklch(0.65 0.15 250); /* 부드러운 파란색 */ + --accent-foreground: oklch(0.985 0 0); /* 흰색 */ + /* --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); */ --destructive: oklch(0.577 0.245 27.325); --border: oklch(0.922 0 0); --input: oklch(0.922 0 0); diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx index 747931f..3acc883 100644 --- a/apps/web/app/home/page.tsx +++ b/apps/web/app/home/page.tsx @@ -8,8 +8,8 @@ export default function Home() {

    노래방 플레이리스트

    - - + {/* + */}
    ); } From 0394d5d335ae9d4b17b44be7274523c83cf5cc9c Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Sun, 30 Mar 2025 22:12:53 +0900 Subject: [PATCH 21/54] =?UTF-8?q?feat=20:=20=EA=B2=80=EC=83=89=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9E=91=EC=97=85.=20Supabase=20DB=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/api/search/route.ts | 27 +++++ apps/web/app/components/ui/input.tsx | 21 ++++ apps/web/app/globals.css | 4 + apps/web/app/home/SongCard.tsx | 28 ++--- apps/web/app/home/SongList.tsx | 29 +++-- apps/web/app/home/page.tsx | 5 +- apps/web/app/search/page.tsx | 175 ++++++++++++++++++++++++++- apps/web/tsconfig.json | 3 +- 8 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 apps/web/app/api/search/route.ts create mode 100644 apps/web/app/components/ui/input.tsx diff --git a/apps/web/app/api/search/route.ts b/apps/web/app/api/search/route.ts new file mode 100644 index 0000000..ecc6490 --- /dev/null +++ b/apps/web/app/api/search/route.ts @@ -0,0 +1,27 @@ +import { NextResponse } from 'next/server'; + +import { createClient } from '@/supabase/server'; + +export async function GET(request: Request) { + // API KEY 노출을 막기 위해 미들웨어 역할을 할 API ROUTE 활용 + + const { searchParams } = new URL(request.url); + const query = searchParams.get('q'); + + if (!query) { + return NextResponse.json({ error: 'No query provided' }, { status: 400 }); + } + + const supabase = await createClient(); + + const { data: songs, error } = await supabase + .from('songs') + .select('*') + .textSearch('title', query); + + if (error) { + return NextResponse.json({ error: error.message }, { status: 500 }); + } + + return NextResponse.json(songs); +} diff --git a/apps/web/app/components/ui/input.tsx b/apps/web/app/components/ui/input.tsx new file mode 100644 index 0000000..03295ca --- /dev/null +++ b/apps/web/app/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index a118dcc..8d74ca2 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -138,3 +138,7 @@ --sidebar-border: oklch(1 0 0 / 10%); --sidebar-ring: oklch(0.556 0 0); } + +button { + @apply cursor-pointer; +} diff --git a/apps/web/app/home/SongCard.tsx b/apps/web/app/home/SongCard.tsx index aeac80f..eab1198 100644 --- a/apps/web/app/home/SongCard.tsx +++ b/apps/web/app/home/SongCard.tsx @@ -5,7 +5,6 @@ import { CSS } from '@dnd-kit/utilities'; import { ChevronsDown, ChevronsUp, GripVertical, Mic, Trash } from 'lucide-react'; import { Card } from '@/components/ui/card'; -import { cn } from '@/lib/utils'; interface Song { id: string; @@ -13,23 +12,22 @@ interface Song { artist: string; kumyoungNumber: string; tjNumber: string; - sung?: boolean; } interface SongCardProps { song: Song; + onSung: () => void; onDelete: () => void; onMoveToTop: () => void; onMoveToBottom: () => void; - onToggleSung: () => void; } export default function SongCard({ song, + onSung, onDelete, onMoveToTop, onMoveToBottom, - onToggleSung, }: SongCardProps) { const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: song.id }); @@ -39,21 +37,14 @@ export default function SongCard({ }; return ( - + {/* 메인 콘텐츠 영역 */}
    {/* 노래 정보 */}
    {/* 제목 및 가수 */}
    -

    - {song.title} -

    +

    {song.title}

    {song.artist}

    @@ -72,17 +63,16 @@ export default function SongCard({ {/* 버튼 영역 - 우측 하단에 고정 */}
    diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx index 3acc883..133e316 100644 --- a/apps/web/app/home/page.tsx +++ b/apps/web/app/home/page.tsx @@ -1,6 +1,7 @@ import SongList from './SongList'; -import TestDND from './TestDND'; -import TestDNDHandle from './TestDNDHandle'; + +// import TestDND from './TestDND'; +// import TestDNDHandle from './TestDNDHandle'; export default function Home() { return ( diff --git a/apps/web/app/search/page.tsx b/apps/web/app/search/page.tsx index 7d9d8f6..de44d2a 100644 --- a/apps/web/app/search/page.tsx +++ b/apps/web/app/search/page.tsx @@ -1,8 +1,177 @@ +'use client'; + +import { Mic, Search } from 'lucide-react'; +import type React from 'react'; +import { useEffect, useState } from 'react'; + +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; + +// 검색 결과 타입 정의 +interface SearchResult { + id: string; + title: string; + artist: string; + kumyoungNumber: string; + tjNumber: string; +} + +// 샘플 데이터 +const sampleData: SearchResult[] = [ + { id: '1', title: '눈의 꽃', artist: '박효신', kumyoungNumber: '47251', tjNumber: '62867' }, + { id: '2', title: '거리에서', artist: '성시경', kumyoungNumber: '84173', tjNumber: '48506' }, + { + id: '3', + title: '벚꽃 엔딩', + artist: '버스커 버스커', + kumyoungNumber: '46079', + tjNumber: '30184', + }, + { id: '4', title: '사랑했나봐', artist: '윤도현', kumyoungNumber: '41906', tjNumber: '35184' }, + { + id: '5', + title: '너를 사랑하고 있어', + artist: '백지영', + kumyoungNumber: '38115', + tjNumber: '46009', + }, + { id: '6', title: '보고싶다', artist: '김범수', kumyoungNumber: '30444', tjNumber: '34147' }, + { + id: '7', + title: '사랑이 멀어지면', + artist: '씨엔블루', + kumyoungNumber: '46513', + tjNumber: '47278', + }, + { + id: '8', + title: '너에게 난 나에게 넌', + artist: '자전거 탄 풍경', + kumyoungNumber: '16293', + tjNumber: '44871', + }, + { id: '9', title: '사랑스러워', artist: '김종국', kumyoungNumber: '35174', tjNumber: '46522' }, + { id: '10', title: '내 사람', artist: 'SG워너비', kumyoungNumber: '45872', tjNumber: '62427' }, +]; + +const baseUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; + export default function SearchPage() { + const [query, setQuery] = useState(''); + const [results, setResults] = useState([]); + const [isSearching, setIsSearching] = useState(false); + + // 검색 기능 + const handleSearch = async () => { + setIsSearching(true); + + console.log(query); + const response = await fetch(`api/search?q=${query}`); + const data = await response.json(); + + const other = await fetch(`/api/songs/title/${query}`); + const otherData = await other.json(); + + console.log(data); + console.log(otherData); + + // 실제로는 API 호출을 할 것이지만, 여기서는 샘플 데이터를 필터링 + const filtered = sampleData.filter( + item => + item.title.toLowerCase().includes(query.toLowerCase()) || + item.artist.toLowerCase().includes(query.toLowerCase()), + ); + + // 약간의 지연을 주어 검색 중인 느낌을 줌 (실제 구현에서는 제거) + setTimeout(() => { + setResults(filtered); + setIsSearching(false); + }, 300); + }; + + // 엔터 키 처리 + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearch(); + } + }; + + // 초기 로드 시 빈 검색 결과 + useEffect(() => { + setResults([]); + }, []); + return ( -
    -

    검색 페이지

    -

    준비 중입니다...

    +
    +
    +

    노래 검색

    + +
    +
    + + setQuery(e.target.value)} + onKeyDown={handleKeyDown} + /> +
    + +
    +
    + +
    + {isSearching ? ( +
    + 검색 중... +
    + ) : results.length > 0 ? ( +
    + {results.map(result => ( + + ))} +
    + ) : query ? ( +
    + 검색 결과가 없습니다 +
    + ) : ( +
    +

    노래 제목이나 가수를 검색해보세요

    + +
    + )} +
    ); } + +// 검색 결과 카드 컴포넌트 +function SearchResultCard({ result }: { result: SearchResult }) { + return ( + + +
    +

    {result.title}

    +

    {result.artist}

    + +
    +
    + 금영 + {result.kumyoungNumber} +
    +
    + TJ + {result.tjNumber} +
    +
    +
    +
    +
    + ); +} diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index ba741d9..45c5f4a 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -20,7 +20,8 @@ } ], "paths": { - "@/*": ["./app/*"] + "@/*": ["./app/*"], + "react": ["./node_modules/@types/react"] // lucide-react : cannot be used as a JSX component. 이슈 해결 } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], From 296cecfb34faacb370e31d7bcb23f18a17d19754 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Sun, 30 Mar 2025 23:24:30 +0900 Subject: [PATCH 22/54] =?UTF-8?q?chore=20:=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=20=EC=A0=95=EC=A0=9C=20=EA=B3=A0=EB=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/package.json | 2 +- packages/api/src/getSong.ts | 13 +++++++++++++ packages/api/src/test.ts | 38 ++++++++++++++++++------------------- packages/api/tsconfig.json | 7 ++++++- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index b79f0e7..1c0dd4c 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -8,7 +8,7 @@ }, "type": "module", "scripts": { - "test": "node --loader ts-node/esm", + "test": "node --experimental-specifier-resolution=node --loader ts-node/esm src/test.ts", "build": "tsup src/index.ts --format esm,cjs --dts" }, "dependencies": { diff --git a/packages/api/src/getSong.ts b/packages/api/src/getSong.ts index 2b7c152..22eac9c 100644 --- a/packages/api/src/getSong.ts +++ b/packages/api/src/getSong.ts @@ -17,4 +17,17 @@ const getSong = async ({ title, brand }: GetSongProps): Promise => { +// const responseArray = await Promise.all(brand.map((brand) => apiRequest('/song', title, brand))); + +// if (!responseArray.every((response) => response.success)) { +// return null; +// } +// const response = responseArray.map((response) => response.data); +// console.log('response', response); + +// return null; +// // return response; +// }; + export default getSong; diff --git a/packages/api/src/test.ts b/packages/api/src/test.ts index 14436cd..39d0c86 100644 --- a/packages/api/src/test.ts +++ b/packages/api/src/test.ts @@ -1,33 +1,33 @@ -import { getSong, getSinger, getComposer, getLyricist } from './index'; +import { getSong, getSinger, getComposer, getLyricist } from './index.js'; console.log('get song test'); -const response = await getSong({ title: '아이유', brand: 'kumyoung' }); +const response = await getSong({ title: '안좋을때' }); console.log('response', response); -const response2 = await getSong({ title: '아이유' }); -console.log('response2', response2); +// const response2 = await getSong({ title: '아이유' }); +// console.log('response2', response2); -console.log('get singer test'); +// console.log('get singer test'); -const response3 = await getSinger({ singer: '아이유', brand: 'kumyoung' }); -console.log('response3', response3); +// const response3 = await getSinger({ singer: '아이유', brand: 'kumyoung' }); +// console.log('response3', response3); -const response4 = await getSinger({ singer: '아이유' }); -console.log('response4', response4); +// const response4 = await getSinger({ singer: '아이유' }); +// console.log('response4', response4); -console.log('get composer test'); +// console.log('get composer test'); -const response5 = await getComposer({ composer: '아이유', brand: 'kumyoung' }); -console.log('response5', response5); +// const response5 = await getComposer({ composer: '아이유', brand: 'kumyoung' }); +// console.log('response5', response5); -const response6 = await getComposer({ composer: '아이유' }); -console.log('response6', response6); +// const response6 = await getComposer({ composer: '아이유' }); +// console.log('response6', response6); -console.log('get lyricist test'); +// console.log('get lyricist test'); -const response7 = await getLyricist({ lyricist: '아이유', brand: 'kumyoung' }); -console.log('response7', response7); +// const response7 = await getLyricist({ lyricist: '아이유', brand: 'kumyoung' }); +// console.log('response7', response7); -const response8 = await getLyricist({ lyricist: '아이유' }); -console.log('response8', response8); +// const response8 = await getLyricist({ lyricist: '아이유' }); +// console.log('response8', response8); diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index a73bba3..6984ac0 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -15,5 +15,10 @@ "declarationMap": true // 디버깅 편의를 위해 추가 }, "include": ["**/*.ts"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist"], + + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node" + } } From 06c15c7014bb042cbd0c0428efca92644e603857 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 31 Mar 2025 12:30:25 +0900 Subject: [PATCH 23/54] =?UTF-8?q?fix=20:=20ResponseType=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B0=B0=EC=97=B4=20=ED=98=95=ED=83=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=BC=EA=B4=84=20=EA=B5=90=EC=B2=B4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/src/getComposer.ts | 2 +- packages/api/src/getLyricist.ts | 2 +- packages/api/src/getNo.ts | 2 +- packages/api/src/getPopular.ts | 2 +- packages/api/src/getRelease.ts | 2 +- packages/api/src/getSinger.ts | 2 +- packages/api/src/getSong.ts | 2 +- packages/api/src/instance.ts | 2 +- packages/api/src/test.ts | 30 ++++++++++++++++++++++---- packages/api/src/types.ts | 2 +- packages/crawling/package.json | 1 + packages/crawling/src/postByRelease.js | 1 + packages/crawling/src/postDB.js | 2 -- packages/crawling/src/utils.js | 2 -- packages/query/src/types.ts | 2 +- pnpm-lock.yaml | 3 +++ 16 files changed, 41 insertions(+), 18 deletions(-) create mode 100644 packages/crawling/src/postByRelease.js diff --git a/packages/api/src/getComposer.ts b/packages/api/src/getComposer.ts index 68b2800..2fbdb50 100644 --- a/packages/api/src/getComposer.ts +++ b/packages/api/src/getComposer.ts @@ -7,7 +7,7 @@ interface GetComposerProps { brand?: Brand; } -const getComposer = async ({ composer, brand }: GetComposerProps): Promise => { +const getComposer = async ({ composer, brand }: GetComposerProps): Promise => { const response = await apiRequest('/composer', composer, brand); if (!response.success) { diff --git a/packages/api/src/getLyricist.ts b/packages/api/src/getLyricist.ts index 0bf6676..c68d4b1 100644 --- a/packages/api/src/getLyricist.ts +++ b/packages/api/src/getLyricist.ts @@ -7,7 +7,7 @@ interface GetLyricistProps { brand?: Brand; } -const getLyricist = async ({ lyricist, brand }: GetLyricistProps): Promise => { +const getLyricist = async ({ lyricist, brand }: GetLyricistProps): Promise => { const response = await apiRequest('/lyricist', lyricist, brand); if (!response.success) { diff --git a/packages/api/src/getNo.ts b/packages/api/src/getNo.ts index 6df7f5b..e1f4554 100644 --- a/packages/api/src/getNo.ts +++ b/packages/api/src/getNo.ts @@ -7,7 +7,7 @@ interface GetNoProps { brand?: Brand; } -const GetNo = async ({ no, brand }: GetNoProps): Promise => { +const GetNo = async ({ no, brand }: GetNoProps): Promise => { const response = await apiRequest('/no', no, brand); if (!response.success) { diff --git a/packages/api/src/getPopular.ts b/packages/api/src/getPopular.ts index aedc843..7953c9c 100644 --- a/packages/api/src/getPopular.ts +++ b/packages/api/src/getPopular.ts @@ -7,7 +7,7 @@ interface GetPopularProps { period: Period; } -const getPopular = async ({ brand, period }: GetPopularProps): Promise => { +const getPopular = async ({ brand, period }: GetPopularProps): Promise => { if (!isVaildBrand(brand)) { throw new Error('Invalid brand type'); } diff --git a/packages/api/src/getRelease.ts b/packages/api/src/getRelease.ts index bd8e120..4e54ebe 100644 --- a/packages/api/src/getRelease.ts +++ b/packages/api/src/getRelease.ts @@ -7,7 +7,7 @@ interface GetReleaseProps { brand?: Brand; } -const getRelease = async ({ release, brand }: GetReleaseProps): Promise => { +const getRelease = async ({ release, brand }: GetReleaseProps): Promise => { const response = await apiRequest('/release', release, brand); if (!response.success) { diff --git a/packages/api/src/getSinger.ts b/packages/api/src/getSinger.ts index ca5b991..c40797b 100644 --- a/packages/api/src/getSinger.ts +++ b/packages/api/src/getSinger.ts @@ -7,7 +7,7 @@ interface GetSingerProps { brand?: Brand; } -const getSinger = async ({ singer, brand }: GetSingerProps): Promise => { +const getSinger = async ({ singer, brand }: GetSingerProps): Promise => { const response = await apiRequest('/singer', singer, brand); if (!response.success) { diff --git a/packages/api/src/getSong.ts b/packages/api/src/getSong.ts index 22eac9c..d1760ef 100644 --- a/packages/api/src/getSong.ts +++ b/packages/api/src/getSong.ts @@ -7,7 +7,7 @@ interface GetSongProps { brand?: Brand; } -const getSong = async ({ title, brand }: GetSongProps): Promise => { +const getSong = async ({ title, brand }: GetSongProps): Promise => { const response = await apiRequest('/song', title, brand); if (!response.success) { diff --git a/packages/api/src/instance.ts b/packages/api/src/instance.ts index 4b78dca..db0bf11 100644 --- a/packages/api/src/instance.ts +++ b/packages/api/src/instance.ts @@ -27,7 +27,7 @@ const createApiRequest = (instance: AxiosInstance) => { // brand가 있는 경우 path 수정 const finalPath = brand ? `${path}/${param}/${brand}.json` : `${path}/${param}.json`; - const response: AxiosResponse = await instance.get(finalPath); + const response: AxiosResponse = await instance.get(finalPath); return { data: response.data, diff --git a/packages/api/src/test.ts b/packages/api/src/test.ts index 39d0c86..572a825 100644 --- a/packages/api/src/test.ts +++ b/packages/api/src/test.ts @@ -1,9 +1,11 @@ -import { getSong, getSinger, getComposer, getLyricist } from './index.js'; +import { getSong, getSinger, getComposer, getLyricist, getRelease } from './index.js'; console.log('get song test'); -const response = await getSong({ title: '안좋을때' }); -console.log('response', response); +// 요청 시 공백 제거해서 보내져야 함 + +// const response = await getSong({ title: 'theworldsmallest' }); +// console.log('response', response); // const response2 = await getSong({ title: '아이유' }); // console.log('response2', response2); @@ -13,7 +15,7 @@ console.log('response', response); // const response3 = await getSinger({ singer: '아이유', brand: 'kumyoung' }); // console.log('response3', response3); -// const response4 = await getSinger({ singer: '아이유' }); +// const response4 = await getSinger({ singer: 'PLAVE' }); // console.log('response4', response4); // console.log('get composer test'); @@ -31,3 +33,23 @@ console.log('response', response); // const response8 = await getLyricist({ lyricist: '아이유' }); // console.log('response8', response8); + +console.log('get release test'); + +let year = 2024; +let month = 1; + +const parseMonth = (month: number) => { + return month < 10 ? `0${month}` : month; +}; + +while (year <= 2025) { + month = 1; + while (month <= 12) { + const response9 = await getRelease({ release: `${year}${parseMonth(month)}`, brand: 'kumyoung' }); + console.log('response9', response9); + console.log('response9', `${year}${parseMonth(month)}`, response9?.length); + month++; + } + year++; +} diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 66dd88a..b07c26e 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -13,7 +13,7 @@ export interface ResponseType { } export interface InstanceResponse { - data: ResponseType | null; + data: ResponseType[] | null; success: boolean; error?: string; } diff --git a/packages/crawling/package.json b/packages/crawling/package.json index 0812f60..0904ab5 100644 --- a/packages/crawling/package.json +++ b/packages/crawling/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@supabase/supabase-js": "^2.49.1", + "@repo/api": "workspace:*", "axios": "^1.5.0", "cheerio": "^1.0.0", "dotenv": "^16.4.7" diff --git a/packages/crawling/src/postByRelease.js b/packages/crawling/src/postByRelease.js new file mode 100644 index 0000000..37b3ad6 --- /dev/null +++ b/packages/crawling/src/postByRelease.js @@ -0,0 +1 @@ +import { Brand, Period, getComposer, getLyricist, getNo, getPopular, getRelease, getSinger, getSong } from '@repo/api'; diff --git a/packages/crawling/src/postDB.js b/packages/crawling/src/postDB.js index 01dffa7..cc947f2 100644 --- a/packages/crawling/src/postDB.js +++ b/packages/crawling/src/postDB.js @@ -24,5 +24,3 @@ export async function postDB(songs) { console.error('❌ Supabase 저장 실패:', error); } } - -// postDB({ title: '아이묭dd', artist: '아이묭 (あいみょん)', num_tj: 1, num_ky: 1 }); diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js index 6dd1e03..05e788d 100644 --- a/packages/crawling/src/utils.js +++ b/packages/crawling/src/utils.js @@ -1,5 +1,3 @@ -// - export const parseNumber = (str) => { if (str.length < 5 || !isNumber(str)) { return null; diff --git a/packages/query/src/types.ts b/packages/query/src/types.ts index 0de00ea..7f10117 100644 --- a/packages/query/src/types.ts +++ b/packages/query/src/types.ts @@ -1,7 +1,7 @@ import { ResponseType } from '@repo/api'; export interface UseQueryReturn { - data: ResponseType | null | undefined; + data: ResponseType[] | null | undefined; isLoading: boolean; isError: boolean; error: Error | null; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e79fcea..ed0c117 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -246,6 +246,9 @@ importers: packages/crawling: dependencies: + '@repo/api': + specifier: workspace:* + version: link:../api '@supabase/supabase-js': specifier: ^2.49.1 version: 2.49.1 From 1ad0c816792da7216193386faf8195e6a2fda66d Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 31 Mar 2025 18:51:25 +0900 Subject: [PATCH 24/54] =?UTF-8?q?chore=20:=20=EA=B3=B5=EB=B0=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/app/home/page.tsx | 13 ++++---- packages/api/src/test.ts | 40 ++++++++++++------------ packages/crawling/src/argList.js | 52 ++++++++++++++++---------------- packages/crawling/src/utils.js | 2 +- 4 files changed, 53 insertions(+), 54 deletions(-) diff --git a/apps/web/app/home/page.tsx b/apps/web/app/home/page.tsx index 133e316..715301a 100644 --- a/apps/web/app/home/page.tsx +++ b/apps/web/app/home/page.tsx @@ -1,16 +1,15 @@ import SongList from './SongList'; - -// import TestDND from './TestDND'; -// import TestDNDHandle from './TestDNDHandle'; +import TestDND from './TestDND'; +import TestDNDHandle from './TestDNDHandle'; export default function Home() { return (
    -

    노래방 플레이리스트

    - + {/*

    노래방 플레이리스트

    */} + {/* */} - {/* - */} + +
    ); } diff --git a/packages/api/src/test.ts b/packages/api/src/test.ts index 572a825..c3e12a9 100644 --- a/packages/api/src/test.ts +++ b/packages/api/src/test.ts @@ -7,7 +7,7 @@ console.log('get song test'); // const response = await getSong({ title: 'theworldsmallest' }); // console.log('response', response); -// const response2 = await getSong({ title: '아이유' }); +// const response2 = await getSong({ title: '반딧불' }); // console.log('response2', response2); // console.log('get singer test'); @@ -34,22 +34,22 @@ console.log('get song test'); // const response8 = await getLyricist({ lyricist: '아이유' }); // console.log('response8', response8); -console.log('get release test'); - -let year = 2024; -let month = 1; - -const parseMonth = (month: number) => { - return month < 10 ? `0${month}` : month; -}; - -while (year <= 2025) { - month = 1; - while (month <= 12) { - const response9 = await getRelease({ release: `${year}${parseMonth(month)}`, brand: 'kumyoung' }); - console.log('response9', response9); - console.log('response9', `${year}${parseMonth(month)}`, response9?.length); - month++; - } - year++; -} +// console.log('get release test'); + +// let year = 2025; +// let month = 1; + +// const parseMonth = (month: number) => { +// return month < 10 ? `0${month}` : month; +// }; + +// while (year <= 2025) { +// month = 1; +// while (month <= 12) { +// const response9 = await getRelease({ release: `${year}${parseMonth(month)}`, brand: 'kumyoung' }); +// console.log('response9', response9); +// console.log('response9', `${year}${parseMonth(month)}`, response9?.length); +// month++; +// } +// year++; +// } diff --git a/packages/crawling/src/argList.js b/packages/crawling/src/argList.js index 7ae0799..5f550af 100644 --- a/packages/crawling/src/argList.js +++ b/packages/crawling/src/argList.js @@ -9,30 +9,30 @@ const getObject = (url, artist, titleIndex, tjIndex, kyIndex) => { export const argList = [ // url, artist, titleIndex, tjIndex, kyIndex - // getObject('AAA(혼성그룹)', 'AAA', 0, 3, 2), - // getObject('Aimer', 'Aimer', 2, 0, 1), - // getObject('amazarashi', '아마자라시 (amazarashi)', 0, 1, 2), - // getObject('BUMP OF CHICKEN', 'BUMP OF CHICKEN', 0, 1, 2), - // getObject('DREAMS COME TRUE(밴드)', 'DREAMS COME TRUE', 0, 1, 2), - // getObject('ELLEGARDEN', '엘르가든 (ELLEGARDEN)', 0, 1, 2), - // getObject('King Gnu', '킹누 (King Gnu)', 0, 1, 2), - // getObject('LiSA', '리사 (LiSA)', 2, 0, 1), - // getObject('Mrs. GREEN APPLE', '미세스그린애플 (Mrs. GREEN APPLE)', 0, 1, 2), - // getObject('Official髭男dism', '오피셜히게단디즘 (Official髭男dism)', 2, 0, 1), - // getObject('Perfume', '퍼퓸 (Perfume)', 0, 1, 2), - // getObject('RADWIMPS', '래드윔프스 (RADWIMPS)', 2, 0, 1), - // getObject('SEKAI NO OWARI', '세카이노오와리 (SEKAI NO OWARI)', 0, 1, 2), - // getObject('SPYAIR', '스파이에어 (SPYAIR)', 2, 0, 1), - // getObject('Vaundy', '바운디 (Vaundy)', 2, 0, 1), - // getObject('w-inds.', 'w-inds.', 0, 1, 2), - // getObject('YOASOBI', '요아소비 (YOASOBI)', 0, 1, 2), - // getObject('계속 한밤중이면 좋을 텐데.', '계속 한밤중이면 좋을 텐데. (즛토마요나카데이이노니)', 2, 0, 1), - // getObject('베리즈코보', '베리즈코보', 0, 1, 2), - // getObject('아라시(아이돌)', '아라시', 0, 1, 2), - // getObject('아이묭', '아이묭 (あいみょん)', 0, 1, 2), - // getObject('요네즈 켄시', '요네즈 켄시', 2, 0, 1), - // getObject('요루시카', '요루시카 (ヨルシカ)', 0, 1, 2), - // getObject('유이카', '유이카', 0, 1, 2), - // getObject('호시노 겐', '호시노 겐', 0, 1, 2), - // getObject('Creepy Nuts', '크리피 넛츠 (Creepy Nuts)', 2, 0, 1), + getObject('AAA(혼성그룹)', 'AAA', 0, 3, 2), + getObject('Aimer', '에이머(Aimer)', 2, 0, 1), + getObject('amazarashi', '아마자라시(amazarashi)', 0, 1, 2), + getObject('BUMP OF CHICKEN', 'BUMP OF CHICKEN', 0, 1, 2), + getObject('DREAMS COME TRUE(밴드)', 'DREAMS COME TRUE', 0, 1, 2), + getObject('ELLEGARDEN', '엘르가든(ELLEGARDEN)', 0, 1, 2), + getObject('King Gnu', '킹누(King Gnu)', 0, 1, 2), + getObject('LiSA', '리사(LiSA)', 2, 0, 1), + getObject('Mrs. GREEN APPLE', '미세스그린애플(Mrs. GREEN APPLE)', 0, 1, 2), + getObject('Official髭男dism', '오피셜히게단디즘(Official髭男dism)', 2, 0, 1), + getObject('Perfume', '퍼퓸(Perfume)', 0, 1, 2), + getObject('RADWIMPS', '래드윔프스(RADWIMPS)', 2, 0, 1), + getObject('SEKAI NO OWARI', '세카이노오와리(SEKAI NO OWARI)', 0, 1, 2), + getObject('SPYAIR', '스파이에어(SPYAIR)', 2, 0, 1), + getObject('Vaundy', '바운디(Vaundy)', 2, 0, 1), + getObject('w-inds.', 'w-inds.', 0, 1, 2), + getObject('YOASOBI', '요아소비(YOASOBI)', 0, 1, 2), + getObject('계속 한밤중이면 좋을 텐데.', '계속 한밤중이면 좋을 텐데.(즛토마요나카데이이노니)', 2, 0, 1), + getObject('베리즈코보', '베리즈코보', 0, 1, 2), + getObject('아라시(아이돌)', '아라시', 0, 1, 2), + getObject('아이묭', '아이묭(あいみょん)', 0, 1, 2), + getObject('요네즈 켄시', '요네즈 켄시', 2, 0, 1), + getObject('요루시카', '요루시카(ヨルシカ)', 0, 1, 2), + getObject('유이카', '유이카', 0, 1, 2), + getObject('호시노 겐', '호시노 겐', 0, 1, 2), + getObject('Creepy Nuts', '크리피 넛츠(Creepy Nuts)', 2, 0, 1), ]; diff --git a/packages/crawling/src/utils.js b/packages/crawling/src/utils.js index 05e788d..17d8df6 100644 --- a/packages/crawling/src/utils.js +++ b/packages/crawling/src/utils.js @@ -28,7 +28,7 @@ export const parseJapaneseText = (text) => { } if (koreanText.length > 0) { - result += ` (${koreanText})`; + result += `(${koreanText})`; } return result; From dd5180bbef870c0bcf6d94d1896b3f7f36c5b832 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Wed, 2 Apr 2025 22:11:48 +0900 Subject: [PATCH 25/54] =?UTF-8?q?feat=20:=20login,=20register=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=84=B8=ED=8C=85.=20prettier=20=ED=94=8C?= =?UTF-8?q?=EB=9F=AC=EA=B7=B8=EC=9D=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/.prettierrc.cjs | 2 +- apps/web/app/Header.tsx | 17 +++ apps/web/app/components/ui/input.tsx | 18 +-- apps/web/app/components/ui/label.tsx | 21 +++ apps/web/app/components/ui/separator.tsx | 28 ++++ apps/web/app/footer.tsx | 7 +- apps/web/app/home/page.tsx | 13 +- apps/web/app/layout.tsx | 6 +- .../{KakaoLoginButton.tsx => KakaoLogin.tsx} | 10 +- apps/web/app/login/actions.ts | 31 +---- apps/web/app/login/page.tsx | 124 +++++++++++++++--- apps/web/app/search/page.tsx | 26 ++-- apps/web/app/signup/actions.ts | 31 +++++ apps/web/app/signup/page.tsx | 97 ++++++++++++++ apps/web/package.json | 4 + apps/web/public/kakao_login.png | Bin 0 -> 3307 bytes apps/web/public/logo.png | Bin 0 -> 2135867 bytes pnpm-lock.yaml | 79 +++++++++++ 18 files changed, 426 insertions(+), 88 deletions(-) create mode 100644 apps/web/app/Header.tsx create mode 100644 apps/web/app/components/ui/label.tsx create mode 100644 apps/web/app/components/ui/separator.tsx rename apps/web/app/login/{KakaoLoginButton.tsx => KakaoLogin.tsx} (60%) create mode 100644 apps/web/app/signup/actions.ts create mode 100644 apps/web/app/signup/page.tsx create mode 100644 apps/web/public/kakao_login.png create mode 100644 apps/web/public/logo.png diff --git a/apps/web/.prettierrc.cjs b/apps/web/.prettierrc.cjs index 7f9f8ab..c689c11 100644 --- a/apps/web/.prettierrc.cjs +++ b/apps/web/.prettierrc.cjs @@ -10,5 +10,5 @@ module.exports = { importOrder: ['', '^@repo/(.*)$', '^@/(.*)$', '^../(.*)$', '^./(.*)$'], importOrderSeparation: true, importOrderSortSpecifiers: true, - plugins: ['prettier-plugin-tailwindcss', '@trivago/prettier-plugin-sort-imports'], // Tailwind 클래스 정렬 (문자열로 변경) + plugins: ['@trivago/prettier-plugin-sort-imports', 'prettier-plugin-tailwindcss'], }; diff --git a/apps/web/app/Header.tsx b/apps/web/app/Header.tsx new file mode 100644 index 0000000..fc96049 --- /dev/null +++ b/apps/web/app/Header.tsx @@ -0,0 +1,17 @@ +'use client'; + +import { User } from 'lucide-react'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; + +export default function Header() { + // login + const router = useRouter(); + + return ( +
    + logo router.push('/')} /> + router.push('/login')} /> +
    + ); +} diff --git a/apps/web/app/components/ui/input.tsx b/apps/web/app/components/ui/input.tsx index 03295ca..3c1cfca 100644 --- a/apps/web/app/components/ui/input.tsx +++ b/apps/web/app/components/ui/input.tsx @@ -1,21 +1,21 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/lib/utils" +import { cn } from '@/lib/utils'; -function Input({ className, type, ...props }: React.ComponentProps<"input">) { +function Input({ className, type, ...props }: React.ComponentProps<'input'>) { return ( - ) + ); } -export { Input } +export { Input }; diff --git a/apps/web/app/components/ui/label.tsx b/apps/web/app/components/ui/label.tsx new file mode 100644 index 0000000..1ea85dc --- /dev/null +++ b/apps/web/app/components/ui/label.tsx @@ -0,0 +1,21 @@ +'use client'; + +import * as LabelPrimitive from '@radix-ui/react-label'; +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +function Label({ className, ...props }: React.ComponentProps) { + return ( + + ); +} + +export { Label }; diff --git a/apps/web/app/components/ui/separator.tsx b/apps/web/app/components/ui/separator.tsx new file mode 100644 index 0000000..d1af264 --- /dev/null +++ b/apps/web/app/components/ui/separator.tsx @@ -0,0 +1,28 @@ +'use client'; + +import * as SeparatorPrimitive from '@radix-ui/react-separator'; +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +function Separator({ + className, + orientation = 'horizontal', + decorative = true, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Separator }; diff --git a/apps/web/app/footer.tsx b/apps/web/app/footer.tsx index 257944b..fbd1c35 100644 --- a/apps/web/app/footer.tsx +++ b/apps/web/app/footer.tsx @@ -17,17 +17,14 @@ export default function Footer() { const pathname = usePathname(); return ( -