diff --git a/package.json b/package.json index 69caf29..08c103a 100644 --- a/package.json +++ b/package.json @@ -5,31 +5,25 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", - "@reduxjs/toolkit": "^2.3.0", "@tanstack/react-query": "^5.59.20", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", "@testing-library/user-event": "^13.2.1", - "antd": "^5.21.5", "axios": "^1.7.7", - "highlight.js": "^11.10.0", "jwt-decode": "^4.0.0", "lucide-react": "^0.454.0", "markdown-it": "^14.1.0", "markdown-it-anchor": "^9.2.0", - "markdown-it-mermaid": "^0.2.5", - "markdown-it-toc-done-right": "^4.2.0", "mermaid": "^11.4.1", "normalize.css": "^8.0.1", + "prismjs": "^1.29.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-markdown": "^9.0.1", "react-modal": "^3.16.1", "react-query": "^3.39.3", "react-redux": "^9.1.2", "react-router-dom": "^6.27.0", "react-scripts": "5.0.1", - "remark-html": "^16.0.1", "styled-components": "^6.1.13", "styled-reset": "^4.5.2", "web-vitals": "^2.1.0", @@ -60,6 +54,7 @@ ] }, "devDependencies": { - "@babel/plugin-proposal-private-property-in-object": "^7.21.11" + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", + "source-map-explorer": "^2.5.3" } } diff --git a/public/favicon.ico b/public/favicon.ico index a11777c..d8bc06b 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html index 6923a20..6cd29ba 100644 --- a/public/index.html +++ b/public/index.html @@ -4,8 +4,21 @@ + - + + + + + + + OAuthProvider - OAuthProvider --> OAuthClient - OAuthProvider --> OAuthUriProvider - OAuthClient --> OAuthMember -\`\`\` -`; - - - - -const DocsContent = () => { - - return ( - <> - -
- - - 왼쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 항목 {i + 1} - - ))} - - - - - - 오른쪽 패널 - - - {/* {Array.from({ length: 25 }).map((_, i) => ( - - 콘텐츠 {i + 1} - - ))} */} - - {markdownText} - - - - - - - - {/* 오른쪽 splitter */} - - - 왼쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 항목 {i + 1} - - ))} - - - - - - 오른쪽 패널 - - - {Array.from({ length: 25 }).map((_, i) => ( - - 콘텐츠 {i + 1} - - ))} - - - - -
-
- - ) -} - -export default DocsContent; diff --git a/src/components/organisms/DocsContent/DocsContent.styles.js b/src/components/organisms/DocsContent/DocsContent.styles.js deleted file mode 100644 index 3301595..0000000 --- a/src/components/organisms/DocsContent/DocsContent.styles.js +++ /dev/null @@ -1,93 +0,0 @@ -import styled from 'styled-components'; -import _ from '../../../config/index.js'; -export const ContentStyle = styled.div` - padding-top: ${_.HEADER.TOTAL_DVH}dvh; - width: 100dvw; - height: 100dvh; - display: flex; - justify-content: center; -`; - -export const PanelContent = styled.div` - max-height: 100dvh; - display: flex; - flex-direction: column; - height: 100%; -`; - -export const LeftPanelContent = styled(PanelContent)` - background-color: transparent; -`; - -export const RightPanelContent = styled(PanelContent)` - background-color: rgb(39 39 42); - border-radius: 0.75rem; -`; - -export const RightTitle = styled.div` - height: 4dvh; - display: flex; - padding: 1rem; - border-bottom: 1px solid #3f3f46; - gap: 0.5rem; - align-items: center; - justify-content: space-between; - font-size: 1.2rem; -`; - -export const RightContentWrapper = styled.div` - padding: 0 1rem; - overflow: hidden; - max-height: 100dvh; -`; - -export const RightContent = styled.div` - padding: 0.75rem 1rem; - width: 100%; - outline: none; - /* Firefox */ - scrollbar-width: thin; - scrollbar-color: rgba(255, 255, 255, 0.3) transparent; - position: relative; - overflow: hidden; - overflow-y: scroll; - height: 100%; - /* Chrome, Safari, Edge */ - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-track { - background: transparent; - } - - &::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.3); - border-radius: 3px; - - &:hover { - background-color: rgba(255, 255, 255, 0.5); - } - } -`; - -export const Title = styled.h2` - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 1rem; -`; - -export const Item = styled.div` - padding: 0.75rem; - background-color: white; - border-radius: 0.375rem; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - margin-bottom: 0.5rem; -`; - -export const Content = styled.div` - padding: 1rem; - background-color: #f9fafb38; - border-radius: 0.375rem; - margin-bottom: 1rem; -`; diff --git a/src/components/organisms/DocsContent/FlexSplitter.jsx b/src/components/organisms/DocsContent/FlexSplitter.jsx deleted file mode 100644 index 95563d4..0000000 --- a/src/components/organisms/DocsContent/FlexSplitter.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { - Container, LeftPanel, - Divider, DividerLine, - RightPanel, - NarrowPanel, WidePanel -} from './FlexSplitter.styles.js' - - -const FlexSplitter = ({ - splitterWidth = '100%', - initialLeftWidth = 250, - minWidth = 150, - maxWidth = 800, - position = 'left', - children -}) => { - const [isDragging, setIsDragging] = useState(false); - const [leftWidth, setLeftWidth] = useState(initialLeftWidth); - const [startX, setStartX] = useState(0); - const [startWidth, setStartWidth] = useState(0); - - const handleMouseDown = useCallback((e) => { - setIsDragging(true); - setStartX(e.clientX); - setStartWidth(leftWidth); - e.preventDefault(); - }, [leftWidth]); - - const handleMouseMove = useCallback((e) => { - if (!isDragging) return; - - const deltaX = e.clientX - startX; - // position에 따라 deltaX의 부호를 반대로 적용 - const adjustedDelta = position === 'left' ? deltaX : -deltaX; - const newWidth = Math.min(Math.max(startWidth + adjustedDelta, minWidth), maxWidth); - setLeftWidth(newWidth); - }, [isDragging, startX, startWidth, minWidth, maxWidth, position]); - - const handleMouseUp = useCallback(() => { - setIsDragging(false); - }, []); - - useEffect(() => { - if (isDragging) { - window.addEventListener('mousemove', handleMouseMove); - window.addEventListener('mouseup', handleMouseUp); - - return () => { - window.removeEventListener('mousemove', handleMouseMove); - window.removeEventListener('mouseup', handleMouseUp); - }; - } - }, [isDragging, handleMouseMove, handleMouseUp]); - - return ( - - { - position === 'left' ? - <> - - {children[0]} - - - - - - {children[1]} - - - : - <> - - {children[0]} - - - - - - {children[1]} - - - } - - - - ); -}; - -export default FlexSplitter; \ No newline at end of file diff --git a/src/components/organisms/DocsContent/FlexSplitter.styles.js b/src/components/organisms/DocsContent/FlexSplitter.styles.js deleted file mode 100644 index f701c4e..0000000 --- a/src/components/organisms/DocsContent/FlexSplitter.styles.js +++ /dev/null @@ -1,74 +0,0 @@ -import styled from 'styled-components'; -// Divider에서 Container 참조 - -export const Container = styled.div` - display: flex; - height: 100%; - width: ${(props) => props.splitterWidth}; - /* width: 50%; */ - background-color: rgb(39 39 42); - border-radius: 0.75rem; - transition: background-color 0.2s ease; -`; - -export const Panel = styled.div` - height: 100%; - overflow: hidden; - background-color: rgb(39 39 42); - border-radius: 0.75rem; -`; - -export const NarrowPanel = styled(Panel)` - flex: 0 0 ${(props) => props.width}px; - width: ${(props) => props.width}px; - transition: ${(props) => (props.isDragging ? 'none' : 'all 0.1s ease')}; -`; - -export const WidePanel = styled(Panel)` - flex: 1; -`; - -export const Divider = styled.div` - flex: 0 0 clamp(10px, auto, 1rem); - display: flex; - align-items: center; - justify-content: center; - cursor: col-resize; - user-select: none; - background-color: rgb(39 39 42); - transition: background-color 0.2s ease; - - border-left: 1px solid rgb(63 63 70 / 70%); - border-right: 1px solid rgb(63 63 70 / 70%); - - &:hover { - color: white; - background-color: #0e0c15; - } -`; - -export const DividerLine = styled.div` - /* width: 2px; - height: 32px; - background-color: #3f3f46; - border-radius: 9999px; */ - width: 10px; - height: 100%; - background: transparent; - display: flex; - align-items: center; - justify-content: center; - cursor: col-resize; - - &::after { - content: '︙'; - color: ${(props) => (props.isDragging ? '#fff' : '#adb5bd')}; - font-size: 12px; - font-weight: bold; - } - &:hover { - &::after { - color: #fff; - } - } -`; diff --git a/src/components/organisms/DocsContent/test.md b/src/components/organisms/DocsContent/test.md deleted file mode 100644 index ca4ed89..0000000 --- a/src/components/organisms/DocsContent/test.md +++ /dev/null @@ -1,252 +0,0 @@ -# Comprehensive Documentation for the Auth Service Code - -## 1. Overall Structure - -### High-Level Overview - -The `AuthService` codebase is part of an authentication module that integrates with various OAuth providers to manage user authentication and token generation. It interacts with member services to create or retrieve user accounts based on OAuth member information. - -### Purpose and Function - -The primary purpose of the `AuthService` is to handle authentication processes, including: - -- Generating tokens using OAuth codes. -- Creating or retrieving members based on OAuth member data. -- Generating OAuth URIs for login. -- Renewing access tokens and managing refresh tokens. - -### Interaction Between Components - -- **OAuthProvider**: Provides the necessary OAuth client and URI provider based on the provider name. -- **MemberService**: Manages member-related operations, such as saving new members and checking for existing members. -- **TokenManager**: Handles the creation and management of access and refresh tokens. - -### Mermaid Diagram - -```mermaid -classDiagram - class AuthService { - +generateTokenWithCode(code: String, providerName: String): MemberToken - +generateUri(providerName: String): String - +generateRenewalAccessToken(renewalAccessTokenRequest: RenewalAccessTokenRequest): RenewalAccessTokenResponse - +removeRefreshToken(logoutRequest: LogoutRequest): void - +extractMemberId(accessToken: String): Long - } - - class OAuthProvider { - +getOauthClient(providerName: String): OAuthClient - +getOAuthUriProvider(providerName: String): OAuthUriProvider - +getSocialType(provider: String): SocialType - } - - class MemberService { - +existsByEmail(email: String): boolean - +save(member: Member): void - +findByEmail(email: String): Member - } - - class TokenManager { - +createMemberToken(memberId: Long): MemberToken - +generateRenewalAccessToken(refreshToken: String): RenewalToken - +getMemberId(accessToken: String): Long - +removeRefreshToken(refreshToken: String): void - } - - AuthService --> OAuthProvider - AuthService --> MemberService - AuthService --> TokenManager -``` - -## 2. Strategy Pattern Implementation - -### Strategy Pattern Overview - -The strategy pattern is implemented through the use of interfaces for OAuth clients and URI providers, allowing for different implementations based on the OAuth provider. - -### Strategy Interface and Concrete Classes - -- **OAuthClient**: Interface for OAuth clients that defines methods for retrieving OAuth member information. -- **OAuthMember**: Interface representing an OAuth member with methods to get social login ID, email, and profile image URL. -- **OAuthProvider**: Interface that provides methods to get specific OAuth clients and URI providers based on the provider name. -- **OAuthUriProvider**: Interface for generating OAuth URIs. - -### Context Class - -- **AuthService**: Acts as the context that uses the strategy interfaces to perform authentication tasks. - -### Class Diagram - -```mermaid -classDiagram - class OAuthClient { - +getOAuthMember(code: String): OAuthMember - +isSame(oAuthProviderName: String): boolean - } - - class OAuthMember { - +getSocialLoginId(): String - +getEmail(): String - +getProfileImageUrl(): String - } - - class OAuthProvider { - +getOauthClient(providerName: String): OAuthClient - +getOAuthUriProvider(providerName: String): OAuthUriProvider - +getSocialType(provider: String): SocialType - } - - class OAuthUriProvider { - +generateUri(): String - +isSame(provider: String): boolean - } - - class AuthService { - +generateTokenWithCode(code: String, providerName: String): MemberToken - +generateUri(providerName: String): String - } - - AuthService --> OAuthProvider - OAuthProvider --> OAuthClient - OAuthProvider --> OAuthUriProvider - OAuthClient --> OAuthMember -``` - -## 3. Detailed Component Documentation - -### a. Classes - -#### 1. `AuthService` - -- **Purpose**: Manages authentication processes using OAuth providers. -- **Attributes**: - - `OAuthProvider oAuthProvider`: Interface for obtaining OAuth clients and URIs. - - `MemberService memberService`: Service for managing member data. - - `TokenManager tokenManager`: Service for managing tokens. -- **Role**: Acts as the main service for authentication, coordinating between OAuth providers and member services. - -#### 2. `OAuthClient` - -- **Purpose**: Interface for OAuth clients. -- **Methods**: - - `OAuthMember getOAuthMember(final String code)`: Retrieves an OAuth member based on the provided code. - - `boolean isSame(final String oAuthProviderName)`: Checks if the client is the same as the given provider name. - -#### 3. `OAuthMember` - -- **Purpose**: Represents an OAuth member. -- **Methods**: - - `String getSocialLoginId()`: Returns the social login ID. - - `String getEmail()`: Returns the email of the member. - - `String getProfileImageUrl()`: Returns the profile image URL. - -#### 4. `OAuthProvider` - -- **Purpose**: Provides OAuth clients and URI providers. -- **Methods**: - - `OAuthClient getOauthClient(final String providerName)`: Returns the OAuth client for the specified provider. - - `OAuthUriProvider getOAuthUriProvider(final String providerName)`: Returns the URI provider for the specified provider. - - `SocialType getSocialType(final String provider)`: Returns the social type for the specified provider. - -#### 5. `OAuthUriProvider` - -- **Purpose**: Generates OAuth URIs. -- **Methods**: - - `String generateUri()`: Generates the OAuth URI. - - `boolean isSame(final String provider)`: Checks if the provider matches. - -### b. Methods and Functions - -#### 1. `generateTokenWithCode` - -- **Purpose**: Generates a member token using an OAuth code. -- **Parameters**: - - `String code`: The OAuth code received from the provider. - - `String providerName`: The name of the OAuth provider. -- **Return Value**: `MemberToken`: The generated member token. -- **Code Example**: - -```java -MemberToken token = authService.generateTokenWithCode("oauth_code", "google"); -``` - -#### 2. `generateUri` - -- **Purpose**: Generates an OAuth URI for login. -- **Parameters**: - - `String providerName`: The name of the OAuth provider. -- **Return Value**: `String`: The generated OAuth URI. -- **Code Example**: - -```java -String uri = authService.generateUri("google"); -``` - -#### 3. `generateRenewalAccessToken` - -- **Purpose**: Generates a new access token using a refresh token. -- **Parameters**: - - `RenewalAccessTokenRequest renewalAccessTokenRequest`: The request containing the refresh token. -- **Return Value**: `RenewalAccessTokenResponse`: The response containing the new access token. -- **Code Example**: - -```java -RenewalAccessTokenResponse response = authService.generateRenewalAccessToken(new RenewalAccessTokenRequest("refresh_token")); -``` - -#### 4. `removeRefreshToken` - -- **Purpose**: Removes a refresh token. -- **Parameters**: - - `LogoutRequest logoutRequest`: The request containing the refresh token to be removed. -- **Return Value**: `void` -- **Code Example**: - -```java -authService.removeRefreshToken(new LogoutRequest("refresh_token")); -``` - -#### 5. `extractMemberId` - -- **Purpose**: Extracts the member ID from an access token. -- **Parameters**: - - `String accessToken`: The access token from which to extract the member ID. -- **Return Value**: `Long`: The extracted member ID. -- **Code Example**: - -```java -Long memberId = authService.extractMemberId("access_token"); -``` - -## 4. Implementation Flow - -### Sequence Diagram - -```mermaid -sequenceDiagram - participant Client - participant AuthService - participant OAuthProvider - participant MemberService - participant TokenManager - - Client->>AuthService: generateTokenWithCode(code, providerName) - AuthService->>OAuthProvider: getOauthClient(providerName) - OAuthProvider->>AuthService: OAuthClient - AuthService->>OAuthClient: getOAuthMember(code) - OAuthClient->>AuthService: OAuthMember - AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) - MemberService->>AuthService: Member - AuthService->>TokenManager: createMemberToken(memberId) - TokenManager->>AuthService: MemberToken - AuthService->>Client: MemberToken -``` - -### Explanation of Flow - -1. The client requests a token by calling `generateTokenWithCode` on the `AuthService`. -2. The `AuthService` retrieves the appropriate `OAuthClient` from the `OAuthProvider`. -3. The `AuthService` uses the `OAuthClient` to get the `OAuthMember` using the provided code. -4. The `AuthService` checks if the member exists or creates a new member using the `MemberService`. -5. Finally, the `AuthService` generates a member token using the `TokenManager` and returns it to the client. - -This documentation provides a comprehensive overview of the `AuthService` codebase, detailing its structure, strategy pattern implementation, component documentation, and implementation flow. It serves as a guide for both new and experienced developers to understand and work with the code effectively. diff --git a/src/components/organisms/HomeContent/HomeContent.jsx b/src/components/organisms/HomeContent/HomeContent.jsx index 4bf5f71..3162b32 100644 --- a/src/components/organisms/HomeContent/HomeContent.jsx +++ b/src/components/organisms/HomeContent/HomeContent.jsx @@ -1,5 +1,5 @@ // src/components/organisms/HomeContent/HomeContent.jsx -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; import { Image, RotateTypo, Button, Typo } from "../../index.js" import { useNavigate } from 'react-router-dom'; @@ -8,6 +8,9 @@ import styled from 'styled-components'; import mainBannerImg from "../../../assets/images/landing-hero-min.png"; import bgShapeFive from "../../../assets/images/bg-shape-five.png"; import bgShapeFour from "../../../assets/images/bg-shape-four.png"; +import chattingLanding from "../../../assets/images/chatting-landing.png"; +import docuLanding from "../../../assets/images/docu-landing.png"; +import readmeLanding from "../../../assets/images/readme-landing.png"; import { Check } from "lucide-react" import { HomeWrapper, MainSectionWrapper, HomeLayout, @@ -22,6 +25,13 @@ import useAuthStore from '../../../store/authStore.js'; const DocsContent = () => { const navigate = useNavigate(); + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }, []); + //Zustand store에서 현재 사용자 정보를 가져옵니다. const { isAuthenticated @@ -98,20 +108,22 @@ const DocsContent = () => { - - + @@ -121,20 +133,23 @@ const DocsContent = () => { - - + @@ -145,20 +160,22 @@ const DocsContent = () => { - - + diff --git a/src/components/organisms/HomeContent/HomeContent.styles.js b/src/components/organisms/HomeContent/HomeContent.styles.js index 9c52814..d37e04b 100644 --- a/src/components/organisms/HomeContent/HomeContent.styles.js +++ b/src/components/organisms/HomeContent/HomeContent.styles.js @@ -48,6 +48,7 @@ export const HomeLayout = styled.div` &::before { opacity: 0.1; background-color: #000; + pointer-events: none; content: ''; position: absolute; top: 0; @@ -176,12 +177,14 @@ export const FeatureContentText = styled.div` } ul { + padding-left: 0; display: flex; flex-direction: column; gap: 1rem; } li { + color: rgb(233, 213, 255); display: flex; align-items: center; gap: 1rem; diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx new file mode 100644 index 0000000..dc2bc10 --- /dev/null +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.jsx @@ -0,0 +1,405 @@ +import React, { useEffect } from 'react'; +import styled, { css, keyframes } from 'styled-components'; +import { + Bot, Code, Zap, ArrowRight, Github, MessagesSquare, Sparkles, + FileText, Settings, Upload + +} from 'lucide-react'; +import { Image, Typo } from '../../../index.js'; +import mainBannerImg from "../../../../assets/images/chatting-landing.png" +import { useNavigate } from 'react-router-dom'; + +const fadeIn = keyframes` + from { + opacity: 0.5; + transform: translateY(10px); /* 아래에서 올라오는 효과 추가 */ + } + to { + opacity: 1; + transform: translateY(0); + } +`; +const Container = styled.div` + min-height: 100vh; + background: #18181b; + color: #e4e4e7; + + animation: ${fadeIn} 0.5s ease-out; /* 애니메이션 추가 */ + + +`; +const HeroSection = styled.div` + padding-top : 11.5dvh; + position : relative; + + + &::before { + content: ''; + position: absolute; + inset: 0; + height: 100%; + background: linear-gradient(to right, rgba(147, 51, 234, 0.1), rgba(236, 72, 153, 0.1)); + pointer-events : none; + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%); + } +` +const HeroGrid = styled.div` + display: grid; + gap: 5rem; + align-items: center; + + + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (min-width: 1300px) { + grid-template-columns: repeat(2, 1fr); + gap : 3rem; + } + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +const HeroTitle = styled.div` +display : flex; +flex-direction: column; +justify-content: flex-start; +` + +const HeroImage = styled.div` +display : flex; +justify-content: center; +` + +const Section = styled.section` + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } + + ${(props) => + props.feature && + ` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + `} + +`; + +const Header = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +`; + +const Title = styled.h1` + font-size: 3rem; + font-weight: bold; + margin-bottom: 24px; + span { + background: linear-gradient(to right, #9333ea, #ec4899); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + @media (max-width: 1300px) { + br { + display: none; // br 태그 숨김 + } + } +`; + +const Description = styled.p` + font-size: 1.25rem; + color: #a1a1aa; + margin-bottom: 32px; +`; + +const Button = styled.button` + padding: 12px 24px; + border-radius: 8px; + border : rgb(63 63 70) 1px solid; + font-size: 1rem; + display: flex; + align-items: center; + gap: 8px; + transition: 0.2s; + ${(props) => (props.primary ? `background: #9333ea; color: white;` : `border: rgb(63 63 70) 1px solid;`)} + + &:hover { + ${(props) => (props.primary ? `background: #7e22ce;` : `background: #27272a;`)} + } +`; + +const FeatureWrapper = styled.div` + display: grid; + grid-template-rows: repeat(auto-fit, minmax(0, 1fr)); /* 자동 행 크기 설정 */ + gap: 24px; + align-items: stretch; /* 카드 높이를 균일하게 맞춤 */ +`; + +const FeatureCard = styled.div` +padding: 2rem; + border-radius: 12px; + background-color: rgba(39,39,42,.5); + border: 1px solid rgb(63 63 70); + display: flex; + align-items: flex-start; /* 아이콘과 내용 상단 정렬 */ + gap: 16px; + flex-grow: 1; /* 카드가 동일한 높이로 늘어남 */ +`; + +const FeatureIcon = styled.div` + width: 48px; + height: 48px; + background: #9333ea33; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + color: #9333ea; + flex-shrink: 0; /* 아이콘 크기가 줄어들지 않도록 고정 */ +`; + +const FeatureContent = styled.div` + h3 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 8px; + } + + p { + color: #a1a1aa; + /* color : rgb(233 213 255); */ + } +`; + +const Badge = styled.span` +background: ${props => (props.bg) || `rgba(168, 85, 247, .2)`} ; +color: ${props => (props.color) || `rgb(216, 180, 254)`} ; +border-radius: 0.5rem; + +display: inline-block; + margin-bottom: 1rem; + padding: 0.25rem 1rem; + background: rgba(147, 51, 234, 0.2); + color: #c084fc; + font-size: 0.875rem; +`; + + +const WelcomeMessage = styled.div` + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + background: rgba(105, 14, 124, 0.05); + border: 1px solid rgba(217, 35, 255, 0.1); + padding: 1.5rem; + border-radius: 1rem; + bottom: 0; + left: 50%; + backdrop-filter: blur(10px); + max-width: calc(100% - 36px - 36px - 24px); + width: 60%; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +const WelcomeMessageTitle = styled.div` + display: flex; + flex-direction: row; + gap: 1rem; + align-items: center; + justify-content: center; +`; + +const BotIcon = styled.div` + width: 2.5rem; + height: 2.5rem; + background: linear-gradient(135deg, #d923ff, #a78bfa); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + box-shadow: 0 2px 10px rgba(217, 35, 255, 0.2); +`; + +const MessageContent = styled.div` + color: #e4e4e7; + font-size: 0.95rem; + line-height: 1.6; + + strong { + color: white; + font-size: 1.3rem; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.5rem; + /* margin-bottom: 0.5rem; */ + } + + ul { + margin-top: 0.75rem; + padding-left: 3.5rem; + li { + margin: 0.5rem 0; + padding-left: 1rem; + position: relative; + + &:before { + content: '•'; + color: #d923ff; + position: absolute; + left: 0; + } + } + } +`; + + + +const LandingPage = () => { + const navigate = useNavigate(); + + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }, []); + + return ( + + {/* Hero Section */} + + + +
+ +
+ + AI-Powered Code <br></br> <span>Chat Bot</span> + + + Dododocs의 지능형 채팅 어시스턴트와 함께 코드베이스를 즉시 이해하세요.

+ 질문하고 설명을 받고 복잡한 코드를 쉽게 탐색하세요. +
+
+ +
+
+ + + +
+
+ + {/* Features */} +
+
+ feature +
+

AI chatbot이 도와주는 코드 분석

+

+ 복잡한 코드도 쉽게 이해할 수 있습니다. + DODODOCS AI 챗봇이 코드의 모든 측면을 분석하고 명확한 설명을 제공합니다. +

+ + + + + + +

Code Analysis

+

GitHub 저장소의 코드를 자동으로 분석하여 구조를 파악하고, 복잡한 로직도 쉽게 설명해드립니다.

+
+
+ + + + + +

Real-time Interaction

+

자연스러운 대화를 통해 코드에 대한 질문을 주고받으며, 즉각적이고 정확한 답변을 받아보세요.

+
+
+ +
+
+ +
+
+ How It Works +
+

Interacting chatbot with AI

+

+ AI 코드 어시스턴트와 대화를 시작하세요. +

+ + + + + + + + DODODOCS AI + {/* AI 챗봇 */} + +

+ 안녕하세요! GitHub 레포지토리의 코드에 대해 궁금하신 점을 물어보세요. + 자세한 분석과 함께 답변해드리겠습니다. +

+
+
+ +
    +
  • 코드 구조와 아키텍처 분석
  • +
  • 함수와 클래스의 역할 설명
  • +
  • 코드 개선 방안 제안
  • +
  • 버그 해결 방안 제시
  • +
+
+
+
+ +
+
+ Get Started +
+

Get Clear Answers to Your Code Questions!

+

+ 복잡한 코드도 AI chatbot 의 도움으로 쉽게 이해할 수 있습니다. 지금 바로 시작해보세요. +

+
+ +
+
+ +
+ Dododocs +
+ +
+ ); +}; + +export default LandingPage; + +// linear-gradient(rgba(39, 39, 42, 0.5), rgb(24, 24, 27) \ No newline at end of file diff --git a/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js new file mode 100644 index 0000000..06671d7 --- /dev/null +++ b/src/components/organisms/Landing/ChattingLanding/ChattingLanding.styles.js @@ -0,0 +1,206 @@ +// ChattingLanding.styles.js +import styled, { css, keyframes } from 'styled-components'; + +const fadeIn = keyframes` + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +`; + +export const PageWrapper = styled.div` + width: 100%; + &::before { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient( + to bottom, + rgba(162, 92, 255, 0.1), + transparent, + transparent + ); + } +`; + +export const PageContainer = styled.div` + min-height: 100vh; + /* margin-top: 11.5dvh; */ + background-color: #0f0a1f; + color: white; + + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + animation: ${fadeIn} 0.5s ease-out; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroSection = styled.div` + position: relative; + overflow: hidden; +`; + +export const ContentWrapper = styled.div` + position: relative; + max-width: 80rem; + margin: 0 auto; + padding: 5rem 1rem; +`; + +export const HeroGrid = styled.div` + display: grid; + gap: 3rem; + align-items: center; + + @media (min-width: 1024px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const GradientText = styled.span` + background: linear-gradient(to right, #a78bfa, #f472b6); + -webkit-background-clip: text; + color: transparent; +`; + +export const ButtonGroup = styled.div` + display: flex; + gap: 1rem; +`; + +export const Button = styled.button` + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + border-radius: 0.5rem; + transition: all 0.2s; + + ${(props) => + props.primary && + ` + background: #9333ea; + color: white; + &:hover { + background: #7e22ce; + } + `} + + ${(props) => + props.secondary && + ` + border: 1px solid rgba(147, 51, 234, 0.2); + color: white; + &:hover { + background: rgba(147, 51, 234, 0.2); + } + `} +`; + +export const Section = styled.section` + padding: 5rem 1rem; + ${(props) => + props.darker && + ` + background: rgba(22, 17, 46, 0.5); + `} +`; + +export const FeatureWrapper = styled.div` + display: flex; + gap: 1rem; + padding: 1.5rem; + border-radius: 0.75rem; + background: #16112e; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const FeatureIcon = styled.div` + width: 3rem; + height: 3rem; + border-radius: 0.5rem; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + color: #e9d5ff; +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: white; + } + + p { + color: #e9d5ff; + } +`; + +export const FeatureGrid = styled.div` + display: grid; + gap: 1.5rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } +`; + +export const StepsGrid = styled.div` + display: grid; + gap: 2rem; + margin-top: 3rem; + + @media (min-width: 768px) { + grid-template-columns: repeat(3, 1fr); + } +`; + +export const StepCard = styled.div` + text-align: center; + padding: 1.5rem; + background: #16112e; + border-radius: 0.75rem; + border: 1px solid rgba(147, 51, 234, 0.2); +`; + +export const StepIcon = styled.div` + width: 4rem; + height: 4rem; + border-radius: 50%; + background: rgba(147, 51, 234, 0.2); + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const CTASection = styled.div` + max-width: 64rem; + margin: 0 auto; + text-align: center; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; diff --git a/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx b/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx new file mode 100644 index 0000000..daed19a --- /dev/null +++ b/src/components/organisms/Landing/DocsLanding/DocsLanding.jsx @@ -0,0 +1,227 @@ +import React, { useEffect } from 'react'; +import { + Boxes, Code, Book, ArrowRight, Github, Coffee, Sparkles, + FileText, Settings, Upload, GitBranch, + Flag, Layout, +} from 'lucide-react'; +import { Image, Typo } from '../../../index.js'; +import mainBannerImg from "../../../../assets/images/docu-landing.png" +import { + Container, HeroSection, HeroGrid, HeroTitle, HeroImage, Section, Header, Title, Description, + PreviewCard, PreviewHeader, PreviewTitle, PreviewContent, FileHeader, FileContent, + Button, FeatureWrapper, FeatureCard, FeatureIcon, FeatureContent, TimelineContainer, ConnectionLine, StepsList, StepItem, NumberBox, GradientBorder, NumberContent, StepLabel, StepNumber, IconBox, ContentBox, ContentCard, ContentTitle, ArrowIcon, ContentDescription, Badge +} from "./DocsLanding.styles.js" +import { useNavigate } from 'react-router-dom'; + + +const LandingReadme = () => { + const navigate = useNavigate(); + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }, []); + const steps = [ + { + number: "01", + icon: Code, + title: "Upload Java Repository", + description: "Java 프로젝트의 repository를 업로드하세요", + gradient: "rgba(147, 51, 234, 0.5), rgba(147, 51, 234, 0.4)" + }, + { + number: "02", + icon: Boxes, + title: "AI Analysis", + description: "AI가 코드를 분석하여 문서 구조를 자동으로 생성합니다", + gradient: "rgba(59, 130, 246, 0.5), rgba(59, 130, 246, 0.4)" + }, + { + number: "03", + icon: Book, + title: "EView Documentation", + description: "생성된 문서를 확인하고 내보내세요", + gradient: "rgba(16, 185, 129, 0.5), rgba(16, 185, 129, 0.4)" + } + ]; + + return ( + + {/* Hero Section */} + + + +
+ Java Documentation + +
+ + Create Beautiful<br></br> + + <span>Java Documentation</span> + + + AI가 Java 프로젝트를 분석하여 깔끔한 문서를 자동으로 생성합니다.
+ 더 이상 수동으로 문서를 작성하지 마세요. +
+
+ + +
+
+ + + +
+
+ + + + + + + +

Java Documentation

+
+ Java 프로젝트 전용 +
+
+
+ + + + + Controller.java + + +

- API 엔드포인트 설명

+

- 요청/응답 구조

+

- 비즈니스 로직 분석

+
+
+
+
+
+ + {/* Features */} +
+
+ feature +
+

+ AI-Powered Documentation Generation +

+

+

+ Java 코드를 자동으로 분석하고 체계적인 문서로 만듭니다. +

+

+ 수동 문서화의 번거로움을 AI가 해결해드립니다. +

+

+ + + + + + +

+ Java 전문화 +

+

+ Java 프로젝트에 특화된 분석 엔진으로 Spring Boot, JPA 등 Java 생태계의 특성을 정확히 파악합니다. +

+ +
+
+ + + + + +

+ 체계적인 문서화 +

+

+ API 문서, 클래스 다이어그램, 시퀀스 다이어그램 등 다양한 형태의 문서를 자동으로 생성합니다. +

+
+
+
+
+ +
+
+ How It Works +
+

Create documentation in minutes

+

+ Java 프로젝트의 문서화를 AI와 함께 완성하세요. +

+ + + + {steps.map((step) => ( + + + + + STEP + {step.number} + + + + + + + + + + + {step.title} + + + {step.description} + + + + ))} + + +
+ +
+
+ Get Started +
+

+ Java 문서화의 새로운 시작 +

+

+ AI 기반 자동 문서 생성, 직관적인 인터페이스, 강력한 챗봇 기능까지. Dododocs로 스마트한 문서화를 시작하세요. +

+
+ +
+
+ +
+ Dododocs +
+ +
+ ); +}; + +export default LandingReadme; + +// linear-gradient(rgba(39, 39, 42, 0.5), rgb(24, 24, 27) \ No newline at end of file diff --git a/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js b/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js new file mode 100644 index 0000000..8baeed0 --- /dev/null +++ b/src/components/organisms/Landing/DocsLanding/DocsLanding.styles.js @@ -0,0 +1,386 @@ +import styled, { keyframes } from 'styled-components'; +import { ArrowRight } from 'lucide-react'; +export const fadeIn = keyframes` + from { + opacity: 0.5; + transform: translateY(10px); /* 아래에서 올라오는 효과 추가 */ + } + to { + opacity: 1; + transform: translateY(0); + } +`; +export const Container = styled.div` + min-height: 100vh; + background: #18181b; + color: #e4e4e7; + + animation: ${fadeIn} 0.5s ease-out; /* 애니메이션 추가 */ +`; +export const HeroSection = styled.div` + padding-top: 11.5dvh; + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0; + height: 100%; + background: linear-gradient( + to right, + rgba(147, 51, 234, 0.1), + rgba(236, 72, 153, 0.1) + ); + pointer-events: none; + mask-image: linear-gradient( + to bottom, + rgba(0, 0, 0, 1) 0%, + rgba(0, 0, 0, 1) 60%, + rgba(0, 0, 0, 0) 100% + ); + } +`; +export const HeroGrid = styled.div` + display: grid; + gap: 5rem; + align-items: center; + + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (min-width: 1300px) { + grid-template-columns: repeat(2, 1fr); + gap: 3rem; + } + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroTitle = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; +`; + +export const HeroImage = styled.div` + display: flex; + justify-content: center; +`; + +export const Section = styled.section` + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } + + ${(props) => + props.feature && + ` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + `} +`; + +export const Header = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +`; + +export const Title = styled.h1` + font-size: 3rem; + font-weight: bold; + margin-bottom: 24px; + span { + background: linear-gradient(to right, #9333ea, #ec4899); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + @media (max-width: 1300px) { + br { + display: none; // br 태그 숨김 + } + } +`; + +export const Description = styled.p` + font-size: 1.25rem; + color: #a1a1aa; + margin-bottom: 32px; +`; + +export const Button = styled.button` + padding: 12px 24px; + border-radius: 8px; + border: rgb(63 63 70) 1px solid; + font-size: 1rem; + display: flex; + align-items: center; + gap: 8px; + transition: 0.2s; + ${(props) => + props.primary + ? `background: #9333ea; color: white;` + : `border: rgb(63 63 70) 1px solid;`} + + &:hover { + ${(props) => (props.primary ? `background: #7e22ce;` : `background: #27272a;`)} + } +`; + +export const PreviewCard = styled.div` + width: 50%; + padding: 1.5rem; + background: rgba(39, 39, 42, 0.5); + border-radius: 0.75rem; + border: 1px solid rgb(63 63 70); +`; + +export const PreviewHeader = styled.div` + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.5rem; +`; + +export const PreviewTitle = styled.div` + h3 { + font-size: 1.125rem; + font-weight: 600; + color: white; + margin-bottom: 0.25rem; + } + + div { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: #a1a1aa; + } +`; + +export const PreviewContent = styled.div` + padding: 1rem; + background: rgba(24, 24, 27, 0.5); + border-radius: 0.5rem; + border: 1px solid rgb(63 63 70); +`; + +export const FileHeader = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + color: #c084fc; + margin-bottom: 1rem; +`; + +export const FileContent = styled.div` + margin-left: 1.5rem; + color: #a1a1aa; + font-size: 0.875rem; + + p { + margin-bottom: 0.5rem; + &:last-child { + margin-bottom: 0; + } + } +`; + +export const FeatureWrapper = styled.div` + display: grid; + grid-template-columns: repeat(2, minmax(250px, 1fr)); /* 최소 250px, 최대 동일 비율 */ + gap: 24px; + align-items: stretch; /* 카드 높이를 균일하게 맞춤 */ +`; + +export const FeatureCard = styled.div` + padding: 2rem; + border-radius: 12px; + background-color: rgba(39, 39, 42, 0.5); + border: 1px solid rgb(63 63 70); + display: flex; + align-items: flex-start; /* 아이콘과 내용 상단 정렬 */ + gap: 16px; + flex-grow: 1; /* 카드가 동일한 높이로 늘어남 */ +`; + +export const FeatureIcon = styled.div` + width: 48px; + height: 48px; + background: #9333ea33; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + color: #9333ea; + flex-shrink: 0; /* 아이콘 크기가 줄어들지 않도록 고정 */ +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 8px; + } + + p { + color: #a1a1aa; + /* color : rgb(233 213 255); */ + } +`; + +export const Badge = styled.span` + background: ${(props) => props.bg || `rgba(168, 85, 247, .2)`}; + color: ${(props) => props.color || `rgb(216, 180, 254)`}; + border-radius: 0.5rem; + + display: inline-block; + margin-bottom: 1rem; + padding: 0.25rem 1rem; + background: rgba(147, 51, 234, 0.2); + color: #c084fc; + font-size: 0.875rem; +`; +export const TimelineContainer = styled.div` + position: relative; +`; + +export const ConnectionLine = styled.div` + position: absolute; + left: 3rem; + top: -1.5rem; + bottom: -0rem; + width: 2px; + border-radius: 1rem; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.5), + rgba(59, 130, 246, 0.5), + rgba(16, 185, 129, 0.5) + ); +`; + +export const StepsList = styled.div` + display: flex; + flex-direction: column; + gap: 5rem; +`; + +export const StepItem = styled.div` + position: relative; + display: flex; + align-items: flex-start; + gap: 2rem; + + &:hover { + ${(props) => props.hoverEffects} + } +`; + +export const NumberBox = styled.div` + position: relative; + z-index: 10; +`; + +export const GradientBorder = styled.div` + width: 6rem; + height: 6rem; + border-radius: 1rem; + background: linear-gradient(to bottom right, ${(props) => props.gradient}); + padding: 1px; +`; + +export const NumberContent = styled.div` + width: 100%; + height: 100%; + border-radius: 1rem; + background: rgb(24, 24, 27); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +`; + +export const StepLabel = styled.span` + font-size: 0.875rem; + color: rgb(113, 113, 122); + font-family: monospace; +`; + +export const StepNumber = styled.span` + font-size: 1.5rem; + font-weight: bold; + background: linear-gradient(to right, #a855f7, #9333ea); + -webkit-background-clip: text; + color: transparent; +`; + +export const IconBox = styled.div` + position: absolute; + bottom: -0.5rem; + right: -0.5rem; + width: 2rem; + height: 2rem; + border-radius: 0.5rem; + background: rgb(24, 24, 27); + display: flex; + align-items: center; + justify-content: center; + color: rgb(168, 85, 247); +`; + +export const ContentBox = styled.div` + flex: 1; + padding-top: 1rem; +`; + +export const ContentCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border-radius: 1rem; + padding: 1.5rem; + border: 1px solid rgba(147, 51, 234, 0.1); + transition: all 0.3s; + + &:hover { + border-color: rgba(147, 51, 234, 0.2); + } +`; + +export const ContentTitle = styled.h3` + font-size: 1.25rem; + font-weight: 600; + color: rgb(168, 85, 247); + margin-bottom: 0.5rem; + display: flex; + align-items: center; + transition: color 0.3s; + + &:hover { + color: rgb(168, 85, 247); + } +`; + +export const ContentDescription = styled.p` + color: rgb(161, 161, 170); +`; + +export const ArrowIcon = styled(ArrowRight)` + margin-left: 0.5rem; + opacity: 0; + transition: opacity 0.3s; + + ${ContentCard}:hover & { + opacity: 0; + } +`; diff --git a/src/components/organisms/Landing/LandingContent.jsx b/src/components/organisms/Landing/LandingContent.jsx new file mode 100644 index 0000000..1c46d59 --- /dev/null +++ b/src/components/organisms/Landing/LandingContent.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import ChattingLanding from './ChattingLanding/ChattingLanding.jsx'; +import DocsLanding from './DocsLanding/DocsLanding.jsx'; +import ReadmeLanding from './LandingReadme/LandingReadme.jsx'; + +const LandingContent = () => { + const { serviceTitle } = useParams(); + + if (serviceTitle === 'chatting') { + return ; + } else if (serviceTitle === 'readme') { + return ; + } else if (serviceTitle === 'docs') { + return ; + } + +}; + +export default LandingContent; \ No newline at end of file diff --git a/src/components/organisms/Landing/LandingReadme/LandingReadme.jsx b/src/components/organisms/Landing/LandingReadme/LandingReadme.jsx new file mode 100644 index 0000000..dd8dd61 --- /dev/null +++ b/src/components/organisms/Landing/LandingReadme/LandingReadme.jsx @@ -0,0 +1,225 @@ +import React, { useEffect } from 'react'; +import { + Bot, Code, Zap, ArrowRight, Github, MessagesSquare, Sparkles, + FileText, Settings, Upload, GitBranch, + Flag, Layout, +} from 'lucide-react'; +import { Image, Typo } from '../../../index.js'; +import mainBannerImg from "../../../../assets/images/readme-landing.png" +import { + Container, HeroSection, HeroGrid, HeroTitle, HeroImage, Section, Header, Title, Description, + Button, FeatureWrapper, FeatureCard, FeatureIcon, FeatureContent, TimelineContainer, ConnectionLine, StepsList, StepItem, NumberBox, GradientBorder, NumberContent, StepLabel, StepNumber, IconBox, ContentBox, ContentCard, ContentTitle, ArrowIcon, ContentDescription, Badge +} from "./ReadMeLanding.styles.js" +import { useNavigate } from 'react-router-dom'; + +const LandingReadme = () => { + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + }, []); + const navigate = useNavigate(); + + const steps = [ + { + number: "01", + icon: FileText, + title: " AI Generated README", + description: "프로젝트 codebase를 기반으로 즉시 AI가 README를 생성합니다", + gradient: "rgba(147, 51, 234, 0.5), rgba(147, 51, 234, 0.4)" + }, + { + number: "02", + icon: Settings, + title: "Customize Content", + description: "AI가 생성한 README를 커스터마이징합니다.", + gradient: "rgba(59, 130, 246, 0.5), rgba(59, 130, 246, 0.4)" + }, + { + number: "03", + icon: Upload, + title: "Export README", + description: "README를 MARKDOWN으로 내보내세요.", + gradient: "rgba(16, 185, 129, 0.5), rgba(16, 185, 129, 0.4)" + } + ]; + + return ( + + {/* Hero Section */} + + + +
+ +
+ + Automated + <br></br> <span>Readme</span> + + + 체계적으로 구성된 README 파일을 손쉽게 생성하세요.

코드베이스를 분석하여 필요한 정보를 자동으로 추출하고, 명확하고 이해하기 쉬운 문서로 정리해 드립니다. +
+
+ + +
+
+ + + +
+
+ + {/* Features */} +
+
+ feature +
+

+ 완벽한 README를 위한 완벽한 솔루션 +

+

+

+ 복잡하고 어려운 README작성, 어렵게 생각하지 마세요! +

+

+ 저희 dododocs의 도움을 받으면, 프로젝트의 모든 것을 담은 훌륭한 README를 누구나 쉽고 빠르게 만들 수 있습니다. +

+

+ 문서 작성에 들이는 시간과 노력을 절약하고, 더욱 중요한 업무에 집중하세요. +

+

+ + + + + + +

+ Create ReadMe +

+

+ 간단하고 편리한 AI로 멋진 문서가 만들어집니다! +

+

+ Dododocs의 직관적인 인터페이스로, 나만의 README를 만들어 보세요 +

+
+
+ + + + + +

+ AI-Powered Suggestions +

+

+ 프로젝트의 특성과 코드 분석을 통해, 콘텐츠 구성과 문서 구조에 대한 맞춤형 제안을 받아보세요. +

+
+
+ + + + + +

+ Connect Git Repositories +

+

+ Git 저장소와 문서를 원활하게 동기화하고 코드를 가져오세요 +

+
+
+ + + + + +

Custom

+

+ 드래그 앤 드롭 인터페이스로 프로젝트 요구사항에 맞게 섹션을 쉽게 추가, 제거 또는 재배열하세요. +

+
+
+
+
+ +
+
+ How It Works +
+

Create documentation in minutes

+

+ 3단계의 간단한 절차를 따라, 누구나 쉽고 빠르게 문서를 완성할 수 있습니다. +

+ + + + {steps.map((step) => ( + + + + + STEP + {step.number} + + + + + + + + + + + {step.title} + + + {step.description} + + + + ))} + + +
+ +
+
+ Get Started +
+

+ 코드 이해의 새로운 시작, Dododocs +

+

+ AI 기반 자동 문서 생성, 직관적인 인터페이스, 강력한 챗봇 기능까지. Dododocs 하나로 코드 문서화의 모든 것을 경험하세요. +

+
+ + +
+
+ +
+ Dododocs +
+ +
+ ); +}; + +export default LandingReadme; + +// linear-gradient(rgba(39, 39, 42, 0.5), rgb(24, 24, 27) \ No newline at end of file diff --git a/src/components/organisms/Landing/LandingReadme/ReadMeLanding.styles.js b/src/components/organisms/Landing/LandingReadme/ReadMeLanding.styles.js new file mode 100644 index 0000000..dc80cdf --- /dev/null +++ b/src/components/organisms/Landing/LandingReadme/ReadMeLanding.styles.js @@ -0,0 +1,326 @@ +import styled, { keyframes } from 'styled-components'; +import { ArrowRight } from 'lucide-react'; +export const fadeIn = keyframes` + from { + opacity: 0.5; + transform: translateY(10px); /* 아래에서 올라오는 효과 추가 */ + } + to { + opacity: 1; + transform: translateY(0); + } +`; +export const Container = styled.div` + min-height: 100vh; + background: #18181b; + color: #e4e4e7; + + animation: ${fadeIn} 0.5s ease-out; /* 애니메이션 추가 */ +`; +export const HeroSection = styled.div` + padding-top: 11.5dvh; + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0; + height: 100%; + background: linear-gradient( + to right, + rgba(147, 51, 234, 0.1), + rgba(236, 72, 153, 0.1) + ); + pointer-events: none; + mask-image: linear-gradient( + to bottom, + rgba(0, 0, 0, 1) 0%, + rgba(0, 0, 0, 1) 60%, + rgba(0, 0, 0, 0) 100% + ); + } +`; +export const HeroGrid = styled.div` + display: grid; + gap: 5rem; + align-items: center; + + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (min-width: 1300px) { + grid-template-columns: repeat(2, 1fr); + gap: 3rem; + } + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } +`; + +export const HeroTitle = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; +`; + +export const HeroImage = styled.div` + display: flex; + justify-content: center; +`; + +export const Section = styled.section` + padding: clamp(5vh, 6vh, 8vh) clamp(4vw, 6vw, 8vw); + + background: ${(props) => props.bg || 'transparent'}; + + @media (max-width: 768px) { + padding: clamp(3vh, 4vh, 6vh) clamp(2vw, 3vw, 4vw); + } + + ${(props) => + props.feature && + ` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + `} +`; + +export const Header = styled.div` + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 24px; +`; + +export const Title = styled.h1` + font-size: 3rem; + font-weight: bold; + margin-bottom: 24px; + span { + background: linear-gradient(to right, #9333ea, #ec4899); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + @media (max-width: 1300px) { + br { + display: none; // br 태그 숨김 + } + } +`; + +export const Description = styled.p` + font-size: 1.25rem; + color: #a1a1aa; + margin-bottom: 32px; +`; + +export const Button = styled.button` + padding: 12px 24px; + border-radius: 8px; + border: rgb(63 63 70) 1px solid; + font-size: 1rem; + display: flex; + align-items: center; + gap: 8px; + transition: 0.2s; + ${(props) => + props.primary + ? `background: #9333ea; color: white;` + : `border: rgb(63 63 70) 1px solid;`} + + &:hover { + ${(props) => (props.primary ? `background: #7e22ce;` : `background: #27272a;`)} + } +`; + +export const FeatureWrapper = styled.div` + display: grid; + grid-template-columns: repeat(2, minmax(250px, 1fr)); /* 최소 250px, 최대 동일 비율 */ + gap: 24px; + align-items: stretch; /* 카드 높이를 균일하게 맞춤 */ +`; + +export const FeatureCard = styled.div` + padding: 2rem; + border-radius: 12px; + background-color: rgba(39, 39, 42, 0.5); + border: 1px solid rgb(63 63 70); + display: flex; + align-items: flex-start; /* 아이콘과 내용 상단 정렬 */ + gap: 16px; + flex-grow: 1; /* 카드가 동일한 높이로 늘어남 */ +`; + +export const FeatureIcon = styled.div` + width: 48px; + height: 48px; + background: #9333ea33; + display: flex; + align-items: center; + justify-content: center; + border-radius: 8px; + color: #9333ea; + flex-shrink: 0; /* 아이콘 크기가 줄어들지 않도록 고정 */ +`; + +export const FeatureContent = styled.div` + h3 { + font-size: 1.25rem; + font-weight: 600; + margin-bottom: 8px; + } + + p { + color: #a1a1aa; + /* color : rgb(233 213 255); */ + } +`; + +export const Badge = styled.span` + background: ${(props) => props.bg || `rgba(168, 85, 247, .2)`}; + color: ${(props) => props.color || `rgb(216, 180, 254)`}; + border-radius: 0.5rem; + + display: inline-block; + margin-bottom: 1rem; + padding: 0.25rem 1rem; + background: rgba(147, 51, 234, 0.2); + color: #c084fc; + font-size: 0.875rem; +`; +export const TimelineContainer = styled.div` + position: relative; +`; + +export const ConnectionLine = styled.div` + position: absolute; + left: 3rem; + top: -1.5rem; + bottom: -0rem; + width: 2px; + border-radius: 1rem; + background: linear-gradient( + to bottom, + rgba(147, 51, 234, 0.5), + rgba(59, 130, 246, 0.5), + rgba(16, 185, 129, 0.5) + ); +`; + +export const StepsList = styled.div` + display: flex; + flex-direction: column; + gap: 5rem; +`; + +export const StepItem = styled.div` + position: relative; + display: flex; + align-items: flex-start; + gap: 2rem; + + &:hover { + ${(props) => props.hoverEffects} + } +`; + +export const NumberBox = styled.div` + position: relative; + z-index: 10; +`; + +export const GradientBorder = styled.div` + width: 6rem; + height: 6rem; + border-radius: 1rem; + background: linear-gradient(to bottom right, ${(props) => props.gradient}); + padding: 1px; +`; + +export const NumberContent = styled.div` + width: 100%; + height: 100%; + border-radius: 1rem; + background: rgb(24, 24, 27); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +`; + +export const StepLabel = styled.span` + font-size: 0.875rem; + color: rgb(113, 113, 122); + font-family: monospace; +`; + +export const StepNumber = styled.span` + font-size: 1.5rem; + font-weight: bold; + background: linear-gradient(to right, #a855f7, #9333ea); + -webkit-background-clip: text; + color: transparent; +`; + +export const IconBox = styled.div` + position: absolute; + bottom: -0.5rem; + right: -0.5rem; + width: 2rem; + height: 2rem; + border-radius: 0.5rem; + background: rgb(24, 24, 27); + display: flex; + align-items: center; + justify-content: center; + color: rgb(168, 85, 247); +`; + +export const ContentBox = styled.div` + flex: 1; + padding-top: 1rem; +`; + +export const ContentCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border-radius: 1rem; + padding: 1.5rem; + border: 1px solid rgba(147, 51, 234, 0.1); + transition: all 0.3s; + + &:hover { + border-color: rgba(147, 51, 234, 0.2); + } +`; + +export const ContentTitle = styled.h3` + font-size: 1.25rem; + font-weight: 600; + color: rgb(168, 85, 247); + margin-bottom: 0.5rem; + display: flex; + align-items: center; + transition: color 0.3s; + + &:hover { + color: rgb(168, 85, 247); + } +`; + +export const ContentDescription = styled.p` + color: rgb(161, 161, 170); +`; + +export const ArrowIcon = styled(ArrowRight)` + margin-left: 0.5rem; + opacity: 0; + transition: opacity 0.3s; + + ${ContentCard}:hover & { + opacity: 0; + } +`; diff --git a/src/components/organisms/MainContent/MainContent.jsx b/src/components/organisms/MainContent/MainContent.jsx deleted file mode 100644 index 720f5a3..0000000 --- a/src/components/organisms/MainContent/MainContent.jsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { useState, useEffect } from "react" -import profileImage from "../../../assets/images/man08.jpg" -import { useNavigate } from 'react-router-dom'; -import { - Wrapper, - Container, - Card, - ImageContainer, - Image, - TextContainer, - MainText, - ButtonContainer, - CategoryButton, - ActionButtonContainer, - ActionButton -} from './MainContent.styles.js'; - - -const MainContent = () => { - const navigate = useNavigate(); - - const categories = [ - { name: 'all', label: '🍽️ 전체' }, - { name: 'korean', label: '🍚 한식' }, - { name: 'chinese', label: '🍜 중식' }, - { name: 'japanese', label: '🍣 일식' }, - { name: 'western', label: '🍝 양식' }, - { name: 'others', label: '🥓 기타' } - ]; - - const [userCategories, setUserCategories] = useState([]); - - useEffect(() => { - console.log(userCategories) - }, [userCategories]) - - - const handleCategoryClick = (category) => { - if (category === "all") { - - } - setUserCategories((state) => { - if (category === "all") { - return state.length === 5 ? [] : ['korean', 'chinese', 'japanese', 'western', 'others']; - } - return state.includes(category) - ? state.filter((e) => e !== category) - : [...state, category] - }); - }; - - const handleActionButtonClick = () => { - console.log('식당 골라줘! 버튼이 클릭되었습니다.'); - navigate('/result') - }; - - return ( - <> - {/* - - - - Profile - - - 아레아레.. 못 말리는 아가씨, - 카테고리를 선택해 주세요.. - - - - {categories.map((category) => ( - handleCategoryClick(category.name)} - > - {category.label} - - ))} - - - 식당 골라줘! - - - - */} - - ) -} - -export default MainContent; \ No newline at end of file diff --git a/src/components/organisms/MainContent/MainContent.styles.js b/src/components/organisms/MainContent/MainContent.styles.js deleted file mode 100644 index 42096a9..0000000 --- a/src/components/organisms/MainContent/MainContent.styles.js +++ /dev/null @@ -1,97 +0,0 @@ -import styled from 'styled-components'; - -export const Wrapper = styled.div` - width: 100%; - height: 100%; - box-sizing: border-box; - display: flex; - align-items: center; - margin: auto auto; - justify-content: center; - flex-direction: column; - gap: 50px; -`; - -export const Container = styled.div` - display: flex; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; - flex-direction: column; - max-width: 500px; - gap: 20px; -`; - -export const Card = styled.div` - background: white; - border-radius: 10px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - overflow: hidden; - width: 100%; - text-align: center; -`; - -export const ImageContainer = styled.div` - width: 100%; -`; - -export const Image = styled.img` - width: 100%; - height: auto; -`; - -export const TextContainer = styled.div` - padding: 20px; -`; - -export const MainText = styled.p` - font-size: 24px; - color: #333; - margin: 0; - font-family: 'grandiflora'; -`; - -export const ButtonContainer = styled.div` - display: flex; - flex-wrap: wrap; - width: 100%; - justify-content: space-between; -`; - -export const CategoryButton = styled.button` - background-color: #ffffff; - border: none; - padding: 18px 10px 19px 8px; - font-size: 1rem; - font-family: 'grandiflora'; - cursor: pointer; - border-radius: 5px; - color: #000000; - - ${(props) => - props.checked && - ` - background-color: #96C9F4; - box-shadow: inset 3px 3px 3px 0px rgba(0, 0, 0, 0.25); - `} -`; - -export const ActionButtonContainer = styled.div` - width: 100%; -`; - -export const ActionButton = styled.button` - background-color: #ffeb3b; - border: none; - padding: 15px 30px; - font-size: 1.2em; - font-family: diphylleia; - cursor: pointer; - border-radius: 5px; - box-shadow: 8px 8px 6px 0px rgba(0, 0, 0, 0.25); - width: 100%; - &:hover { - background-color: #fdd835; - } -`; diff --git a/src/components/organisms/RepoContent/Board.jsx b/src/components/organisms/RepoContent/Board.jsx deleted file mode 100644 index ba5ece7..0000000 --- a/src/components/organisms/RepoContent/Board.jsx +++ /dev/null @@ -1,324 +0,0 @@ -import React, { useState } from 'react'; -import styled from 'styled-components'; -import { CalendarIcon, X, GitBranch, Plus } from 'lucide-react'; - -// Styled Components -const BoardGrid = styled.div` - display: grid; - grid-template-columns: 1fr; - gap: 1.5rem; - padding: 1.5rem; - - @media (min-width: 768px) { - grid-template-columns: repeat(2, 1fr); - } - - @media (min-width: 1024px) { - grid-template-columns: repeat(3, 1fr); - } -`; - -const ProjectCard = styled.div` - background: #18181b; - border: 1px solid ${props => props.isSelected ? '#8b5cf6' : '#27272a'}; - border-radius: 0.5rem; - overflow: hidden; - transition: all 0.2s ease; - cursor: pointer; - - &:hover { - border-color: #3f3f46; - box-shadow: 0 0 0 2px #8b5cf6; - } - - ${props => props.isSelected && ` - box-shadow: 0 0 0 2px #8b5cf6; - `} -`; - -const CardContent = styled.div` - padding: 1.5rem; -`; - -const HeaderSection = styled.div` - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 1rem; -`; - -const ProjectIcon = styled.div` - width: 3rem; - height: 3rem; - background: #27272a; - border-radius: 0.2rem; - display: flex; - align-items: center; - justify-content: center; - font-size: 1.5rem; - color: #ffffff; -`; - -const CloseButton = styled.div` - display : flex; - justify-content: center; - align-items: center; - cursor: pointer; - padding: 0.5rem; - color: #9a9cae; - border-color: #26272f; - border-radius: 0.375rem; - background: #18181b; - &:hover { - background: #26272f; - } -` - -const StatusBadge = styled.span` - padding: 0.25rem 0.75rem; - border-radius: 0.5rem; - font-size: 0.75rem; - font-weight: 500; - background: ${props => { - switch (props.status) { - case 'In Progress': - return 'rgba(59, 130, 246, 0.1)'; - case 'Completed': - return 'rgba(34, 197, 94, 0.1)'; - case 'Upcoming': - return 'rgba(161, 161, 170, 0.1)'; - default: - return 'rgba(139, 92, 246, 0.1)'; - } - }}; - color: ${props => { - switch (props.status) { - case 'In Progress': - return '#3b82f6'; - case 'Completed': - return '#22c55e'; - case 'Upcoming': - return '#a1a1aa'; - default: - return '#8b5cf6'; - } - }}; -`; - -const ProjectInfo = styled.div` - margin-bottom: 1.5rem; -`; - -const ProjectTitle = styled.h3` - color: #fafafa; - font-size: 1.125rem; - font-weight: 600; - margin-bottom: 0.5rem; -`; - - - -const Timeline = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - color: #a1a1aa; - font-size: 0.875rem; -`; - -const ProgressBarContainer = styled.div` - width: 100%; - height: 0.35rem; - background: #27272a; -`; - -const ProgressBar = styled.div` - height: 100%; - background: #8b5cf6; - border-radius: 0.5rem; - transition: width 0.3s ease; - width: ${props => props.progress}%; -`; - -const ProgressSection = styled.div` - display: flex; - flex-direction: column; - gap: 1rem; - padding: 1rem 1.5rem 1.5rem 1.5rem; - border-top: 1px solid #27272a; -`; - -const StatusContainer = styled.div` - width: 100%; - height: auto; -` - -const BranchContainer = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - margin: 0.75rem 0; -`; - -const BranchTag = styled.div` - background: #2a2a3c; - /* color: #a78bfa; */ - color : #e980ff; - font-weight: 500; - padding: 0.375rem 0.75rem; - border-radius: 0.375rem; - font-size: 0.875rem; - display: flex; - align-items: center; - gap: 0.5rem; - border: 1px solid #3f3f46; - - svg { - /* color: #8b5cf6; */ - color : #d923ff; - } -`; - -const EmptySlot = styled.div` - border: 2px dashed #27272a; - border-radius: 0.5rem; - height: 320px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1rem; - cursor: pointer; - transition: all 0.2s ease; - - &:hover { - border-color: rgba(139, 92, 246, 0.6); - box-shadow: 0 8px 12px rgba(0, 0, 0, 0.2); - - } -`; - -const EmptySlotIcon = styled.div` - width: 3rem; - height: 3rem; - border: 2px dashed #27272a; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: #71717a; - - ${EmptySlot}:hover & { - background: rgba(139, 92, 246, 0.1); - color: #8b5cf6; - border : 2px solid rgba(138, 92, 246, 0.05); - } -`; - -const EmptySlotText = styled.p` - color: #71717a; - font-size: 0.875rem; - font-weight: 500; - ${EmptySlot}:hover & { - color: #ffffff; - } -`; - -const SlotCounter = styled.p` - color: #52525b; - font-size: 0.75rem; - ${EmptySlot}:hover & { - color: #71717a; - } -`; - -const ProjectBoard = ({ - dataSource = [], - onCardClick, - handleDeleteClick, - selectedCard, - onAddClick, - MAX_PROJECTS -}) => { - - - const emptySlots = Math.max(0, MAX_PROJECTS - dataSource.length); - console.log(dataSource) - return ( - - {dataSource.map((project) => { - return ( - - - onCardClick?.(project)} - isSelected={selectedCard === project.registeredRepoId} - > - - - - {project.Repository.charAt(0)} - - handleDeleteClick(e, project)}> - - - - - - {project.Repository} - - - - {project.Branch} - - - - - - - Dec 2024 - - - - - - - - - - {project.Status} - - - - - ) - } - )} - - {/* Empty Slots */} - {emptySlots > 0 && Array(emptySlots) - .fill(null) - .map((_, index) => ( - { - if (dataSource.length < MAX_PROJECTS) { - onAddClick?.(); - } - }} - > - - - - Add New Project - - {dataSource.length + 1} of {MAX_PROJECTS} Projects - - - ))} - - ); -}; - -export default ProjectBoard; \ No newline at end of file diff --git a/src/components/organisms/RepoContent/Board.test.jsx b/src/components/organisms/RepoContent/Board.test.jsx deleted file mode 100644 index da62e74..0000000 --- a/src/components/organisms/RepoContent/Board.test.jsx +++ /dev/null @@ -1,673 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import styled, { keyframes, css } from 'styled-components'; -import { - CalendarIcon, X, GitBranch, Plus, ChevronUp, ChevronDown, - FileText, MessageCircle, Book, - Clock, Loader2, CheckCircle2 -} from 'lucide-react'; - - -// Styled Components -// 회전 애니메이션 정의 -const spin = keyframes` - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -`; - -const BoardWrapper = styled.div` - border-bottom : 1px solid rgba(63, 63, 70, 0.4); - border-top : 1px solid rgba(63, 63, 70, 0.4); - gap : 1.5rem; - justify-content : space-between; - width : 100%; - padding: 1.5rem; - - display: flex; - - display: grid; - grid-template-columns: 1fr; - - @media (min-width: 768px) { - grid-template-columns: repeat(2, 1fr); - } - - @media (min-width: 1024px) { - grid-template-columns: repeat(3, 1fr); - } - -` -const ProjectCardWrapper = styled.div` -/* width : 100%; */ -flex : 1; -overflow: hidden; - -transition: all 0.2s ease; - @media (min-width: 768px) { - - &:nth-child(3) { - grid-column: 1 / -1; - } - } - @media (min-width: 1024px) { - &:nth-child(3) { - grid-column: auto ; - } - } - -` - - -const ProjectCard = styled.div` - flex-direction : column; - display : flex; - height : 21rem; - background: #18181b; - border: 1px solid rgb(39, 39, 42); - transition: all 0.2s ease; - width : 100%; - border-radius: 0.5rem; - position : relative; - - overflow: hidden; -` - -const CloseButton = styled.div` - position: absolute; - top : 1rem; - right : 1.5rem; - display : flex; - justify-content: center; - align-items: center; - cursor: pointer; - padding: 0.5rem; - color: #9a9cae; - border-color: #26272f; - border-radius: 0.375rem; - background: #18181b; - &:hover { - background: #26272f; - } -` - - - -const CardContent = styled.div` - transition: all 0.2s ease; - flex : ${props => props.isExpanded ? '0.8' : '0.2'}; - padding : 1rem 1.5rem; - - display : flex; - gap : ${props => props.isExpanded ? '0.75rem' : '0.4rem'}; - flex-direction : column; -` -const CardHeader = styled.div` - transition: all 0.2s ease; - align-items: ${props => props.isExpanded ? 'flex-start' : 'center'} ; - display : flex; - gap : ${props => props.isExpanded ? '1rem' : '.5rem'}; - flex-direction : ${props => props.isExpanded ? 'column' : 'row'}; -` -const ProjectIcon = styled.div` - transition: all 0.2s ease; - - width : ${props => props.isExpanded ? '3rem' : '2.3rem'} ; - height : ${props => props.isExpanded ? '3rem' : '2.3rem'}; - background: #27272a; - border-radius: 0.2rem; - display: flex; - align-items: center; - justify-content: center; - font-size: ${props => props.isExpanded ? '1.5rem' : '1.2rem'}; - color: #ffffff; -`; - -const CardTitle = styled.div` - color: #fafafa; - font-size: 1.125rem; - font-weight: 600; -` - -const CardBody = styled.div` - transition: all 0.2s ease; - align-items: ${props => props.isExpanded ? 'flex-start' : 'center'} ; - display : flex; - gap : ${props => props.isExpanded ? '1.5rem' : '.5rem'}; - flex-direction : ${props => props.isExpanded ? 'column' : 'row'}; -` - -const BranchContainer = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; -`; - -const BranchTag = styled.div` - transition: all 0.2s ease; - background: #2a2a3c; - color : #e980ff; - font-weight: ${props => props.isExpanded ? '500' : 'normal'}; - padding: ${props => props.isExpanded ? '0.375rem 0.75rem' : '0.2rem 0.75rem'}; - border-radius: 0.375rem; - font-size: ${props => props.isExpanded ? '.875rem' : '0.875rem'}; - display: flex; - align-items: center; - gap: 0.5rem; - border: ${props => props.isExpanded ? '1px solid #3f3f46' : 'none'} ; - - svg { - color : #d923ff; - } -`; - -const BranchDot = styled.div` - width: 0.5rem; - height: 0.5rem; - background-color: #d923ff; - border-radius: 9999px; -`; - - -const TimeTag = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - color: #a1a1aa; - font-size: 0.875rem; -`; - - -const ExpandSection = styled.div` - position: relative; - padding: 0.5rem; - cursor: pointer; - -`; - -const Divider = styled.div` - width: 100%; - height: 1px; - background-color: rgb(39, 39, 42); - - ${ExpandSection}:hover & { - background-color: rgba(138, 92, 246, 0.243); - } -`; - -const ExpandButton = styled.div` - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - width: 1.5rem; - height: 1rem; - display: flex; - align-items: center; - justify-content: center; - border-radius: 9999px; - background-color: #18181b; - border: 1px solid rgb(39, 39, 42); - transition: border-color 0.2s; - color : #6b7280; - ${ExpandSection}:hover & { - border-color: #8b5cf6; - background-color: #1b1127; - - svg { - color: #8b5cf6; - } - } -`; - - -const CardTimelineSection = styled.div` - transition: all 0.2s ease; - width : 100%; - display : flex; - justify-content: space-between; - flex : ${props => props.isExpanded ? '0.2' : '0.8'}; - flex-direction : ${props => props.isExpanded ? 'row' : 'column'}; - /* flex : 0.3; */ - padding : 1rem 1.5rem; -` - - -const CardTimelineItem = styled.div` - transition : padding 0.2s ease; - width : ${props => props.isExpanded ? '33%' : '100%'}; - flex : ${props => props.isExpanded ? '0.32' : '0.3'}; - border-radius : 0.5rem; - gap : ${props => props.isExpanded ? 'null' : '0.5rem'} ; - word-break : keep-all; - padding : ${props => props.isExpanded ? '0.5rem 0.1rem' : '0.75rem'} ; - background-color: ${props => props.isExpanded ? 'null' : 'rgba(30, 32, 38, 0.95)'} ; - border: ${props => props.isExpanded ? '1px solid rgba(63, 63, 70, 0.5)' : '1px solid rgba(30, 32, 38, 0.95)'} ; - display : flex; - flex-direction : ${props => props.isExpanded ? 'row' : 'row'}; - align-items: center; - justify-content:${props => props.isExpanded ? 'space-evenly' : 'space-between'} ; - /* background-color : #27272a; */ -` - -const TimelineItemInfo = styled.div` - display: flex; - align-items: center; - gap: 0.75rem; -` - -const TimeLineItemTitle = styled.div` - display : ${props => props.isExpanded ? 'none' : 'flex'}; -font-weight: 500; -color: #d1d5db; -` -const TimeLineStatusBadge = styled.div` - display: flex; - align-items: center; - gap: 0.5rem; - padding: ${props => props.isExpanded ? '0.25rem 0.5rem' : '0.25rem 0.75rem'} ; - border-radius: 0.5rem; - border : 1px solid ${props => props.bdColor}; - background-color: ${props => props.bgColor}; - -`; - -const StatusIconWrapper = styled.div` - display: inline-flex; - ${props => props.isSpinning && css` - animation: ${spin} 1s linear infinite; - `} - will-change: transform; -`; - -const TimeLineStatusText = styled.span` - font-size: 0.85rem; - font-weight: 500; - color: ${props => props.color}; -`; - -const EmptySlot = styled.div` - border: 2px dashed #27272a; - border-radius: 0.5rem; - height: 21rem; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1rem; - cursor: pointer; - transition: all 0.2s ease; - - &:hover { - border-color: rgba(139, 92, 246, 0.6); - box-shadow: 0 8px 12px rgba(0, 0, 0, 0.2); - - } -`; - -const EmptySlotIcon = styled.div` - width: 3rem; - height: 3rem; - border: 2px dashed #27272a; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - color: #71717a; - - ${EmptySlot}:hover & { - background: rgba(139, 92, 246, 0.1); - color: #8b5cf6; - border : 2px solid rgba(138, 92, 246, 0.05); - } -`; - -const EmptySlotText = styled.p` - color: #71717a; - font-size: 0.875rem; - font-weight: 500; - ${EmptySlot}:hover & { - color: #ffffff; - } -`; - -const SlotCounter = styled.p` - color: #52525b; - font-size: 0.75rem; - ${EmptySlot}:hover & { - color: #71717a; - } -`; - - -const StatusCompletedContainer = styled.div` - width: 100%; - height: auto; -` - - -const StatusCompletedBadge = styled.div` - padding: 0.35rem 0.75rem; - border-radius: 0.5rem; - font-size: 0.75rem; - font-weight: 500; - background:rgba(139, 92, 246, 0.1); - color: #8b5cf6; - - display: flex; - width : 100%; - height : 100%; - align-items: center; - justify-content: center; - gap : 0.25rem -`; - - -const StatusCompletedText = styled.span` - display: flex; - align-items: center; - justify-content: center; - font-size: 1rem; - height: 100%; - margin-top: .2rem; -` - - -const ProjectBoard = ({ - // dataSource = [], - onCardClick, - handleDeleteClick, - selectedCard, - onAddClick, - MAX_PROJECTS -}) => { - - const dataSource = [ - { - Action: "delete", - Branch: "main", - Repository: "spring-boot_test", - Status: "Code Imported", - key: "0", - projectsStatus: [ - { - id: 1, - title: "readme", - name: "README 만들기", - status: "waiting", - }, - { - id: 2, - title: "chatting", - name: "Chatting 만들기", - status: "in-progress", - }, - { - id: 3, - title: "docs", - name: "Docs 만들기", - status: "completed", - } - ], - completed: false - }, - { - Action: "delete", - Branch: "main", - Repository: "moheng", - Status: "Code Imported", - key: "1", - projectsStatus: [ - { - id: 1, - title: "readme", - name: "README 만들기", - status: "completed", - }, - { - id: 2, - title: "chatting", - name: "Chatting 만들기", - status: "completed", - }, - { - id: 3, - title: "docs", - name: "Docs 만들기", - status: "completed", - } - ], - completed: true - }, - ] - - const emptySlots = Math.max(0, MAX_PROJECTS - dataSource.length); - const [expandedStates, setExpandedStates] = useState({ - 0: dataSource[0]?.completed, - 1: dataSource[1]?.completed, - 2: dataSource[2]?.completed, - }); - - useEffect(() => { - console.log(expandedStates) - }, [expandedStates]); - - // handleToggleExpand 함수 수정 - const handleToggleExpand = (e, index) => { - e.stopPropagation(); - setExpandedStates(prev => ({ - ...prev, - [index]: !prev[index] - })); - }; - - const projectsStatus = [ - { - id: 1, - title: "readme", - name: "README 만들기", - status: "waiting", - icon: FileText - }, - { - id: 2, - title: "chatting", - name: "Chatting 만들기", - status: "in-progress", - icon: MessageCircle - }, - { - id: 3, - title: "docs", - name: "Docs 만들기", - status: "completed", - icon: Book - } - ]; - - const getStatusIcon = (status) => { - switch (status) { - case 'readme': - return FileText; - case 'chatting': - return MessageCircle; - case 'docs': - return Book; - default: - return FileText; // 기본값 설정 - } - }; - - const getStatusDisplay = (status) => { - switch (status) { - case 'waiting': - return { - icon: Clock, - text: "대기", - color: "#9ca3af", - bgColor: "#1f2937", - bdColor: "rgba(156, 163, 175, 0.1)" - }; - case 'in-progress': - return { - icon: Loader2, - text: "진행중", - color: "rgb(192, 132, 252)", - bgColor: "rgba(126, 34, 206, 0.3)", - bdColor: "rgba(192, 132, 252,0.1)" - }; - case 'completed': - return { - icon: CheckCircle2, - text: "완료", - color: "rgb(74, 222, 128)", - bgColor: "rgba(21, 128, 61, 0.3)", - bdColor: "rgba(74, 222, 128,0.1)" - }; - default: - return null; - } - }; - - - - - - return ( - <> - - { - dataSource.map((project, index) => { - return ( - - onCardClick?.(project)} - isSelected={selectedCard === project.key} - > - handleDeleteClick(e, project)}> - - - - - - {project.Repository.charAt(0)} - - {project.Repository} - - - - - { - expandedStates[index] ? - - : - - } - - {project.Branch} - - - - - - Dec 2024 - - - - - handleToggleExpand(e, index)}> - - - {expandedStates[index] ? ( - - ) : ( - - )} - - - { - - } - - - - { - project.completed && expandedStates[index] ? - - - - - Dododocs complete ! - - - - : - - project.projectsStatus.map((statusLists) => { - const status = getStatusDisplay(statusLists.status); - const TimeLineIcon = getStatusIcon(statusLists.title); - const StatusIcon = status.icon; - return ( - - - - - {statusLists.name} - - - - - - {status.text} - - - ) - - }) - } - - - - ) - - }) - } - - { - emptySlots > 0 && Array(emptySlots) - .fill(null) - .map((_, index) => ( - { - if (dataSource.length < MAX_PROJECTS) { - onAddClick?.(); - } - }} - > - - - - Add New Project - - {dataSource.length + 1} of {MAX_PROJECTS} Projects - - - )) - } - - - - - - ); -}; - -export default ProjectBoard; diff --git a/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx b/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx index 10eae42..b1beda0 100644 --- a/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx +++ b/src/components/organisms/RepoContent/RepoBoard/RepoBoard.jsx @@ -30,7 +30,7 @@ const ProjectBoard = ({ const transformedData = dataSource.map(item => ({ Action: "delete", Branch: item.branchName, - Repository: item.repositoryName, + repositoryName: item.repositoryName, Status: "Code Imported", registeredRepoId: item.registeredRepoId, projectsStatus: [ @@ -151,9 +151,9 @@ const ProjectBoard = ({ - {project.Repository.charAt(0)} + {project.repositoryName.charAt(0)} - {project.Repository} + {project.repositoryName} diff --git a/src/components/organisms/RepoContent/RepoContent.jsx b/src/components/organisms/RepoContent/RepoContent.jsx index 727694a..fb8acd6 100644 --- a/src/components/organisms/RepoContent/RepoContent.jsx +++ b/src/components/organisms/RepoContent/RepoContent.jsx @@ -6,9 +6,8 @@ import bgShapeFour from "../../../assets/images/bg-shape-four.png"; import { Search, Plus } from 'lucide-react'; import { useRepoStore, useRegisteredRepoStore, useUserStore } from '../../../store/store.js' import { useRepoManagement } from '../../../hooks/useRepoManagement' - -import BoardTest from './RepoBoard/RepoBoard.jsx'; -import Board from './Board.jsx'; +import RepositoryGuide from './RepositoryGuide.jsx'; +import RepoBoard from './RepoBoard/RepoBoard.jsx'; import { BgShape, @@ -137,33 +136,14 @@ const RepoContent = () => { - - - - - setSearchValue(e.target.value)} - placeholder="Search repositories..." - plane={true} - /> - - - - - - { + + diff --git a/src/components/organisms/RepoContent/RepoContentModal/AddRepoModal.jsx b/src/components/organisms/RepoContent/RepoContentModal/AddRepoModal.jsx index 225068b..50604ad 100644 --- a/src/components/organisms/RepoContent/RepoContentModal/AddRepoModal.jsx +++ b/src/components/organisms/RepoContent/RepoContentModal/AddRepoModal.jsx @@ -207,6 +207,7 @@ const AddRepo = ({ isOpen, onClose, resetForm, }) => { // const isAddingRepoLoading = true; + console.log('🥹🥹🥹addRepoError', addRepoError, isAddingRepoLoading) const { repoListRefetch, isUserDataLoading } = useUser(); const { repositories } = useUserStore(); @@ -229,7 +230,8 @@ const AddRepo = ({ isOpen, onClose, const loadingMessages = [ '레포지토리 연결 중...', '코드를 분석하는 중입니다...', - 'AI가 코드를 이해하는 중입니다...' + 'AI가 코드를 이해하는 중입니다...', + ]; const [messageIndex, setMessageIndex] = useState(0); diff --git a/src/components/organisms/RepoContent/RepositoryGuide.jsx b/src/components/organisms/RepoContent/RepositoryGuide.jsx new file mode 100644 index 0000000..a580dc3 --- /dev/null +++ b/src/components/organisms/RepoContent/RepositoryGuide.jsx @@ -0,0 +1,381 @@ +import React, { useState } from 'react'; +import styled from 'styled-components'; +import { + CalendarIcon, GitBranch, Plus, ChevronUp, ChevronDown, + FileText, MessageCircle, Book, + Clock, Loader2, MessagesSquare, CheckCircle2, HelpCircle, X +} from 'lucide-react'; + +import { + BoardWrapper, + ProjectCardWrapper, ProjectCard, CloseButton, CardContent, CardHeader, ProjectIcon, CardTitle, CardBody, + BranchContainer, BranchTag, TimeTag, Divider, ExpandSection, ExpandButton, + CardTimelineSection, CardTimelineItem, BranchDot, StatusCompletedContainer, StatusCompletedBadge, + StatusCompletedText, TimelineItemInfo, TimeLineItemTitle, TimeLineStatusBadge, + StatusIconWrapper, TimeLineStatusText, EmptySlot, EmptySlotIcon, EmptySlotText, SlotCounter +} from "./RepoBoard/RepoBoard.styles.js" +import { useRepoStore } from '../../../store/store.js' + + +const GuideContainer = styled.div` + width: 100%; + width: 100%; + margin: 0 auto; + /* padding: 1rem; */ +`; + +const GuideWrapper = styled.div` + background-color: rgba(17, 24, 39, 0.95); + border-top:1px solid rgba(63, 63, 70, 0.4); + /* border-radius: 0.5rem; */ + overflow: hidden; + backdrop-filter: blur(8px); +`; + +const GuideHeader = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + cursor: pointer; + transition: background-color 0.2s; + + &:hover { + background-color: rgba(31, 41, 55, 0.5); + } +`; + +const HeaderTitle = styled.div` + display: flex; + align-items: center; + gap: 0.75rem; + + span { + color: white; + font-weight: 500; + } +`; + +const GuideContent = styled.div` + transition: max-height 0.3s; + max-height: ${props => props.isExpanded ? 'auto' : '0'}; + overflow: hidden; + display : grid; + grid-template-columns: 1fr 1fr; +`; + +const StepsList = styled.div` + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +`; + +const StepItem = styled.div` + padding: 1rem; + border-radius: 0.5rem; + border: 1px solid ${props => props.isActive ? 'rgba(139, 92, 246, 0.5)' : 'rgb(31, 41, 55)'}; + background-color: ${props => props.isActive ? 'rgba(31, 41, 55, 0.5)' : 'rgba(17, 24, 39, 0.5)'}; + transition: all 0.3s; + cursor: pointer; + +`; + +const StepContent = styled.div` + display: flex; + align-items: flex-start; + gap: 0.75rem; +`; + +const StepIconWrapper = styled.div` + margin-top: 0.25rem; +`; + +const StepInfo = styled.div` + flex: 1; +`; + +const StepTitle = styled.h3` + color: white; + font-weight: 500; + margin-bottom: 0.25rem; +`; + +const StepDescription = styled.p` + color: rgb(156, 163, 175); + font-size: 0.875rem; + margin-bottom: 0.5rem; +`; + +const StepHint = styled.div` + color: rgba(139, 92, 246, 0.8); + font-size: 0.75rem; + background-color: rgba(139, 92, 246, 0.1); + padding: 0.375rem 0.75rem; + border-radius: 9999px; + display: inline-block; +`; + +const RepositoryGuide = ({ onCardClick }) => { + // const { testRepo } = useRepoStore(); + const transformedData = { + Action: "delete", + Branch: 'main', + repositoryName: 'guide', + Status: "Code Imported", + registeredRepoId: 'guide', + projectsStatus: [ + { + id: 1, + title: "readme", + name: "README 만들기", + status: "completed", + }, + + { + id: 2, + title: "docs", + name: "Docs 만들기", + status: 'completed', + }, + { + id: 3, + title: "chatting", + name: "Chatting 만들기", + status: 'completed', + }, + ], + completed: true, + createdAt: '2024-12-18', + // completed: testRepo?.readmeComplete && testRepo?.chatbotComplete && testRepo?.docsComplete, + // createdAt: testRepo?.createdAt + } + + const [isExpanded, setIsExpanded] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + + const guideSteps = [ + { + title: "README 문서 작성", + status: "readme", + description: "AI가 Repository 분석 후 최적화된 README를 생성합니다", + hint: "Repository를 선택하고 'Create README' 버튼을 클릭하세요" + }, + { + title: "실시간 채팅 지원", + status: "chatting", + description: "코드에 대해 질문하고 실시간으로 답변을 받아보세요", + hint: "채팅 탭을 선택하고 질문을 입력하세요" + }, + { + title: "문서 자동화", + status: "docs", + description: "코드 문서화를 자동으로 생성하고 관리합니다", + hint: "문서 탭에서 자동 생성된 문서를 확인하세요" + } + ]; + + const getStatusGuideIcon = (status) => { + switch (status) { + case 'readme': + return ; + case 'chatting': + return ; + default: + return ; + } + }; + + + const getStatusIcon = (status) => { + switch (status) { + case 'readme': + return FileText; + case 'chatting': + return MessageCircle; + case 'docs': + return Book; + default: + return FileText; + } + }; + + const getStatusDisplay = (status) => { + switch (status) { + case 'waiting': + return { + icon: Clock, + text: "대기", + color: "#9ca3af", + bgColor: "#1f2937", + bdColor: "rgba(156, 163, 175, 0.1)" + }; + case 'in-progress': + return { + icon: Loader2, + text: "진행중", + color: "rgb(192, 132, 252)", + bgColor: "rgba(126, 34, 206, 0.3)", + bdColor: "rgba(192, 132, 252,0.1)" + }; + case 'completed': + return { + icon: CheckCircle2, + text: "완료", + color: "rgb(74, 222, 128)", + bgColor: "rgba(21, 128, 61, 0.3)", + bdColor: "rgba(74, 222, 128,0.1)" + }; + default: + return null; + } + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + return date.toLocaleString('en-US', { month: 'short', year: 'numeric' }); + }; + + const [expandedStates, setExpandedStates] = useState(true) + const handleToggleExpand = (e) => { + e.stopPropagation(); + setExpandedStates((prev) => !prev); + }; + + + return ( + + + setIsExpanded(!isExpanded)}> + + + How to Use Guide + + {isExpanded ? + : + + } + + + + + {guideSteps.map((step, index) => ( + setCurrentStep(index)} + > + + + {getStatusGuideIcon(step.status)} + + + {step.title} + {step.description} + 💡 {step.hint} + + + + ))} + +
+ onCardClick('guide')} + style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} + > + + + + + + + + {transformedData.repositoryName.charAt(0)} + + {transformedData.repositoryName} + + + + + {expandedStates ? + + : + + } + {transformedData.Branch} + + + + + {formatDate(transformedData.createdAt)} + + + + handleToggleExpand(e, transformedData.registeredRepoId)}> + + + {expandedStates ? ( + + ) : ( + + )} + + + + { + transformedData.completed && expandedStates ? ( + + + + + Dododocs complete ! + + + + ) : ( + transformedData.projectsStatus.map((statusList) => { + const status = getStatusDisplay(statusList.status); + const TimeLineIcon = getStatusIcon(statusList.title); + const StatusIcon = status.icon; + return ( + + + + + {statusList.name} + + + + + + + + {status.text} + + + + ); + }) + )} + + + + +
+
+ +
+
+ ); +}; + +export default RepositoryGuide; \ No newline at end of file diff --git a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx new file mode 100644 index 0000000..27e2281 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx @@ -0,0 +1,389 @@ +// src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.jsx +import React, { useState, useRef, useEffect } from 'react'; +import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; +import { Typo } from "../../../index.js"; +import useClickAway from '../../../../hooks/useClickAway.js'; +import { useChatbot } from '../../../../hooks/RepoDetailContent/useChatbot.js'; +import { TypingMarkdownRenderer, MarkdownRenderer } from '../../../index.js'; +import { useRegisteredRepoStore, useAuthStore } from "../../../../store/store.js" + +import { + ChatWrapper, + EllipsisMenuWrapper, EllipsisVerticalIcon, EllipsisMenu, + ChatContainer, + MessageContainer, + WelcomeTitle, WelcomeMessage, WelcomeMessageTitle, + SparklesIcon, + BotIcon, + MessageContent, + Badge, + ChatMessages, + MessageBubble, + Avatar, + Message, + InputContainer, InputWrapper, Input, + ActionButton, + LoadingDots, + ErrorBanner, LoadingMessage, LoadingText, LoadingStatus, +} from "./Chatting.styles.js" + + +const ChatbotUI = () => { + const { activeRepositoryId } = useRegisteredRepoStore(); + const token = useAuthStore(state => state.token); + + // === Chat History 관련 상태 (useChatbot) === + const { + chatHistory, + isLoading: isHistoryLoading, // 채팅 히스토리 로딩 상태 + isError: isHistoryError, // 채팅 히스토리 에러 상태 + error: historyError, // 채팅 히스토리 에러 메시지 + resetChat + } = useChatbot(activeRepositoryId); + + + // === 실시간 채팅 관련 상태 === + const [messages, setMessages] = useState([]); // 현재 화면에 표시되는 메시지들 + const [inputText, setInputText] = useState(''); // 입력창 텍스트 + const [isSending, setIsSending] = useState(false); // 메시지 전송 중 상태 + const [sendError, setSendError] = useState(null); // 메시지 전송 에러 + const [streamingResponse, setStreamingResponse] = useState(''); // 실시간 응답 데이터 + const [accumulatedResponse, setAccumulatedResponse] = useState(''); + // === UI 관련 상태 === + const messagesEndRef = useRef(null); + const [isMenuOpen, setIsMenuOpen] = useState(false); + const menuRef = useRef(null); + + + const [isTest, setIsTest] = useState(0); + + + + + + // 채팅 히스토리 동기화 + useEffect(() => { + if (chatHistory && !isHistoryLoading && !isHistoryError) { + const formattedMessages = chatHistory.flatMap(chat => [ + { id: `q-${chat.id}`, text: chat.question, isUser: true }, + { id: `a-${chat.id}`, text: chat.text, isUser: false } + ]); + setMessages(formattedMessages); + } + }, [chatHistory, isHistoryLoading, isHistoryError]); + + + + // 스트리밍 응답 처리 useEffect 수정 + useEffect(() => { + if (streamingResponse) { + try { + // 'data:' 부분 제거 및 JSON 파싱 + console.log('1 😇streamingResponse', streamingResponse) + const cleanedStr = streamingResponse.split('data:')[1] || streamingResponse; + console.log(cleanedStr) + const parsedResponse = JSON.parse(cleanedStr); + const answer = parsedResponse.answer; + + console.log('🙃 answer', answer) + // 누적된 응답 업데이트 + setAccumulatedResponse(prev => prev + answer); + + // 메시지 업데이트 + setMessages(prev => { + const lastMessage = prev[prev.length - 1]; + + if (!lastMessage || lastMessage.isUser) { + // 새로운 AI 메시지 추가 + return [...prev, { + id: `stream-${Date.now()}`, + text: accumulatedResponse + answer, + isUser: false + }]; + } + + // 기존 AI 메시지 업데이트 (누적된 응답 사용) + const newMessages = [...prev]; + newMessages[newMessages.length - 1] = { + ...newMessages[newMessages.length - 1], + text: accumulatedResponse + answer + }; + return newMessages; + }); + } catch (error) { + console.error('스트리밍 응답 파싱 에러:', error); + // setSendError('응답 처리 중 오류가 발생했습니다.'); + } + } + }, [streamingResponse]); + + // 메시지 전송 핸들러 + const handleSend = async () => { + if (!inputText.trim() || isSending || !token || !activeRepositoryId) return; + + const userMessageId = Date.now(); + // 사용자 메시지 즉시 표시 + setMessages(prev => [...prev, { + id: `q-${userMessageId}`, + text: inputText, + isUser: true + }]); + + // 상태 초기화 + setInputText(''); + setIsSending(true); + setSendError(null); + setStreamingResponse(''); + setAccumulatedResponse(''); // 누적 응답 초기화 + + try { + const response = await fetch( + `${process.env.REACT_APP_API_BASE_URL}api/chatbot/stream-and-save/${activeRepositoryId}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + question: inputText + }) + } + ); + + if (!response.ok) { + throw new Error(`응답 오류: ${response.status}`); + } + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { value, done } = await reader.read(); + if (done) break; + const chunk = decoder.decode(value, { stream: true }); + setStreamingResponse(chunk); + } + + } catch (error) { + console.error('메시지 전송 에러:', error); + // setSendError(error.message); + setSendError(`잠시만 기다려주세요. 레포지토리를 분석하고 챗봇을 생성하는데 시간이 소요됩니다.`); + + setMessages(prev => [...prev, { + id: `error-${userMessageId}`, + text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", + isUser: false, + isError: true + }]); + } finally { + setIsSending(false); + } + }; + + + // 대화 초기화 핸들러 + const handleReset = async () => { + try { + await resetChat(); // 서버 측 히스토리 초기화 + setMessages([]); // 로컬 메시지 초기화 + setStreamingResponse(''); + setSendError(null); + } catch (error) { + console.error('대화 초기화 에러:', error); + setSendError('대화 초기화 중 오류가 발생했습니다.'); + } + }; + + + + useClickAway(menuRef, () => { + if (isMenuOpen) setIsMenuOpen(false); + }); + + // 스크롤 핸들러 + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + useEffect(() => { + scrollToBottom(); + }, [messages]); + + const handleKeyPress = (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + + // 통합 로딩 상태 (히스토리 로딩 or 메시지 전송 중) + const isLoading = isHistoryLoading || isSending; + + // 에러 메시지 우선순위 + const displayError = sendError || (isHistoryError ? historyError : null); + + + + return ( + + + setIsMenuOpen(!isMenuOpen)} + > + + +
    +
  • + + reset +
  • +
+
+
+
+ + + + {/* 에러 메시지 표시 */} + {displayError && ( + + {displayError} + + )} + + {/* Welcome message */} + + + + Dododocs AI Chat + + + 연결된 GitHub 레포지토리의 코드를 기반으로 답변을 제공해드립니다. + + + { + messages.length === 0 && !isHistoryLoading ? + + + + + + + + DODODOCS AI + {/* AI 챗봇 */} + +

+ 안녕하세요! GitHub 레포지토리의 코드에 대해 궁금하신 점을 물어보세요. + 자세한 분석과 함께 답변해드리겠습니다. +

+
+
+ +
    +
  • 코드 구조와 아키텍처 분석
  • +
  • 함수와 클래스의 역할 설명
  • +
  • 코드 개선 방안 제안
  • +
  • 버그 해결 방안 제시
  • +
+
+
+ : null + } + + {/* Chat messages */} + + + {messages.map(message => ( + + + {message.isUser ? : } + + + {message.isUser ? ( + message.text + ) : ( + console.log('타이핑 완료')} + /> + )} + + + ))} + {isLoading && ( + + + + + + + + + + + + + {isHistoryLoading ? '채팅 내역을 불러오는 중...' : '답변 생성 중...'} + + + + + )} +
+ + + + + + setInputText(e.target.value)} + onKeyPress={handleKeyPress} + disabled={isLoading} + /> + + + + + + + + + + + + + + + + + + ); +}; + +export default ChatbotUI; + + diff --git a/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js new file mode 100644 index 0000000..aed8500 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/CattingDetailContent/Chatting.styles.js @@ -0,0 +1,484 @@ +import styled, { css, keyframes } from 'styled-components'; +import { Sparkles } from 'lucide-react'; + +// Animations +export const fadeIn = keyframes` + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +`; + +export const shimmer = keyframes` + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +`; + +export const WelcomeFadeIn = keyframes` + from { + opacity: 0; + transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 + } + to { + opacity: 1; + transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 + } +`; + +const bounce = keyframes` + 0%, 80%, 100% { + transform: translateY(0); + opacity: 0.3; + } + 40% { + transform: translateY(-6px); + opacity: 1; + } +`; + +// Styled Components +export const ChatWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + height: 100%; + width: 100%; + background: rgba(45, 45, 58, 0.4); + backdrop-filter: blur(10px); +`; + +export const EllipsisMenuWrapper = styled.div` + width: 100%; + background: transparent; + display: flex; + justify-content: flex-end; + align-items: center; + height: 2.5rem; +`; + +export const EllipsisVerticalIcon = styled.div` + color: #ffffff; + cursor: pointer; + padding: 0.7rem; + display: flex; + justify-content: center; + align-items: center; + &:hover { + background: rgba(13, 15, 15, 0.3); + } + position: relative; + margin: 0 1rem 0 0; +`; + +export const EllipsisMenu = styled.div` + position: absolute; + cursor: default; + inset: 0px 0px auto auto; + margin: 0px; + transform: translate(0px, 2.5rem); + border-radius: 0.75rem; + padding: 1rem; + min-width: 15rem; + background-color: #232236; + z-index: 3000; + border: 1px solid rgba(63, 63, 70, 0.3); + + /* 최적화된 그림자 효과 */ + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 8px 24px rgba(0, 0, 0, 0.15); + + /* 최적화된 트랜지션 */ + transform-origin: top right; + transition: opacity 0.2s ease-out, visibility 0.2s ease-out, + transform 0.2s cubic-bezier(0.16, 1, 0.3, 1); + + /* 상태에 따른 스타일 변화 */ + pointer-events: ${(props) => (props.isOpen ? 'auto' : 'none')}; + + ${(props) => + props.isOpen + ? css` + opacity: 1; + visibility: visible; + transform: translate(0, 2.5rem) scale(1); + ` + : css` + opacity: 0; + visibility: hidden; + transform: translate(0, 2rem) scale(0.95); + `} + ul { + margin: 0; + padding-inline-start: 0; + li { + margin: 0.5rem 0; + background-color: #232236; + padding: 0.7rem 0.5rem; + color: ${(props) => + props.$disabled + ? 'rgba(113, 119, 144, 0.4)' + : '#9492A6'}; // 비활성화 상태의 색상 변경 + transition: all 0.2s ease; // 부드러운 전환 효과 추가 + cursor: pointer; // hover 시 커서 변경 + border-radius: 4px; + display: flex; + align-items: center; + font-size: 1rem; + cursor: ${(props) => (props.$disabled === true ? 'not-allowed' : 'cursor')}; + + span { + padding: 0.3rem; + background: ${(props) => (props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#373554')}; + display: flex; + transition: all 0.2s ease; + border-radius: 4px; + margin-right: 0.5rem; + } + svg { + width: 1rem; + height: 1rem; + opacity: ${(props) => (props.$disabled ? 0.4 : 1)}; // 아이콘 투명도 + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#d923ff')}; + } + + &:hover { + background-color: ${(props) => + props.$disabled ? '#232236' : '#373554'}; // 비활성화 상태에서는 hover 효과 제거 + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff')}; + + span { + background: ${(props) => + props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#a25cff'}; + } + svg { + color: ${(props) => (props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff')}; + } + } + } + } +`; + +export const ChatContainer = styled.div` + display: flex; + flex-direction: column; + /* flex : 1; */ + height: calc(100% - 2.5rem); + width: 100%; + border-radius: 16px; + /* border: 1px solid rgba(255, 255, 255, 0.1); */ +`; + +export const MessageContainer = styled.div` + flex: 1; + overflow-y: auto; + padding: 24px; + position: relative; + scroll-behavior: smooth; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: rgba(217, 35, 255, 0.2); + border-radius: 3px; + + &:hover { + background: rgba(217, 35, 255, 0.3); + } + } +`; + +export const WelcomeTitle = styled.div` + padding: 0 0 2rem 0; + display: flex; + flex-direction: column; + gap: 0.5rem; + animation: ${fadeIn} 0.6s ease-out; +`; + +export const WelcomeMessage = styled.div` + display: flex; + flex-direction: column; + gap: 0.5rem; + align-items: flex-start; + background: rgba(105, 14, 124, 0.05); + border: 1px solid rgba(217, 35, 255, 0.1); + padding: 1.5rem; + border-radius: 1rem; + position: absolute; + bottom: 0; + left: 50%; + transform: translate(-50%, -10%); // 초기 위치를 최종 위치와 동일하게 설정 + backdrop-filter: blur(10px); + animation: ${WelcomeFadeIn} 0.6s ease-out; + max-width: calc(100% - 36px - 36px - 24px); + width: 60%; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +export const WelcomeMessageTitle = styled.div` + display: flex; + flex-direction: row; + gap: 1rem; + align-items: center; + justify-content: center; +`; + +export const SparklesIcon = styled(Sparkles)` + display: inline; + margin-right: 0.5rem; + color: #d923ff; + width: 24px; + height: 24px; +`; + +export const BotIcon = styled.div` + width: 2.5rem; + height: 2.5rem; + background: linear-gradient(135deg, #d923ff, #a78bfa); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + box-shadow: 0 2px 10px rgba(217, 35, 255, 0.2); +`; + +export const MessageContent = styled.div` + color: #e4e4e7; + font-size: 0.95rem; + line-height: 1.6; + + strong { + color: white; + font-size: 1.3rem; + font-weight: 500; + display: flex; + align-items: center; + gap: 0.5rem; + /* margin-bottom: 0.5rem; */ + } + + ul { + margin-top: 0.75rem; + padding-left: 3.5rem; + li { + margin: 0.5rem 0; + padding-left: 1rem; + position: relative; + + &:before { + content: '•'; + color: #d923ff; + position: absolute; + left: 0; + } + } + } +`; + +export const Badge = styled.span` + padding: 0.125rem 0.5rem; + background-color: #d923ff; + font-size: 0.75rem; + border-radius: 9999px; + color: white; + margin-left: 0.5rem; + font-weight: 500; + margin: 0 0 0.2rem 0.2rem; +`; + +export const ChatMessages = styled.div` + display: flex; + flex-direction: column; + gap: 16px; + padding-bottom: 120px; +`; + +export const MessageBubble = styled.div` + display: flex; + align-items: flex-start; + gap: 12px; + margin: ${(props) => (props.isUser ? '0 0 0 auto' : '0 auto 0 0')}; + max-width: calc(100% - 36px - 36px - 24px); + animation: ${fadeIn} 0.3s ease-out; +`; + +export const Avatar = styled.div` + min-width: 36px; + min-height: 36px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + background: ${(props) => + props.isUser ? 'linear-gradient(135deg, #d923ff, #a78bfa)' : 'rgba(63, 63, 70, 0.8)'}; + color: white; + order: ${(props) => (props.isUser ? 1 : 0)}; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +`; + +export const Message = styled.div` + max-width: 100%; + background: rgba(63, 63, 70, 0.8); + padding: 14px 18px; + border-radius: 16px; + color: white; + font-size: 0.95rem; + line-height: 1.5; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + backdrop-filter: blur(20px) brightness(50%); + + ${(props) => + props.isUser && + css` + background: transparent; + border: 1px solid #a78bfa; + `} + + ${(props) => + props.$isError && + css` + background: rgba(220, 38, 38, 0.1); + border: 1px solid rgba(220, 38, 38, 0.2); + color: #ef4444; + `} +`; + +// 에러 메시지 배너 +export const ErrorBanner = styled.div` + background: rgba(220, 38, 38, 0.1); + border: 1px solid rgba(220, 38, 38, 0.2); + color: #ef4444; + padding: 12px 16px; + border-radius: 8px; + margin: 8px 16px; + font-size: 0.9rem; + display: flex; + align-items: center; + gap: 8px; + animation: ${fadeIn} 0.3s ease-out; +`; + +export const LoadingMessage = styled.div` + display: flex; + align-items: center; + gap: 12px; + padding: 4px; + animation: ${fadeIn} 0.3s ease-out; +`; + +export const LoadingText = styled.span` + color: rgba(255, 255, 255, 0.7); + font-size: 0.9rem; + white-space: nowrap; + animation: ${fadeIn} 0.3s ease-out; +`; +export const InputContainer = styled.div` + padding: 20px; + background: rgba(255, 255, 255, 0.03); + border-top: 1px solid rgba(255, 255, 255, 0.06); + border-radius: 0 0 16px 16px; +`; + +export const InputWrapper = styled.div` + display: flex; + gap: 12px; + align-items: center; + background: rgba(255, 255, 255, 0.07); + border-radius: 12px; + padding: 8px 16px; + transition: all 0.2s ease; + border: 1px solid rgba(255, 255, 255, 0.1); + position: relative; // LoadingStatus의 absolute positioning을 위해 필요 + + ${(props) => + props.$isLoading && + css` + background: rgba(217, 35, 255, 0.05); + border-color: rgba(217, 35, 255, 0.2); + `} + + &:focus-within { + background: rgba(255, 255, 255, 0.09); + border-color: rgba(217, 35, 255, 0.3); + box-shadow: 0 0 0 2px rgba(217, 35, 255, 0.1); + } +`; + +export const Input = styled.input` + flex: 1; + background: transparent; + border: none; + color: white; + font-size: 0.95rem; + padding: 10px 0; + + &::placeholder { + color: rgba(255, 255, 255, 0.3); + } + + &:focus { + outline: none; + } +`; + +export const ActionButton = styled.button` + background: transparent; + border: none; + color: ${(props) => (props.disabled ? 'rgba(255, 255, 255, 0.2)' : '#d923ff')}; + cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; + padding: 8px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + border-radius: 8px; + + &:hover:not(:disabled) { + background: rgba(217, 35, 255, 0.1); + color: #e048ff; + } + + &:active:not(:disabled) { + transform: scale(0.95); + } +`; +export const LoadingStatus = styled.div` + position: absolute; + right: 100px; // Send 버튼과의 간격 + opacity: ${(props) => (props.$isVisible ? 1 : 0)}; + visibility: ${(props) => (props.$isVisible ? 'visible' : 'hidden')}; + transform: ${(props) => (props.$isVisible ? 'translateY(0)' : 'translateY(5px)')}; + transition: all 0.2s ease-in-out; + display: flex; + align-items: center; + gap: 8px; +`; + +// LoadingDots 컴포넌트 (기존 스타일 수정) +export const LoadingDots = styled.div` + display: flex; + gap: 4px; + align-items: center; + padding: 4px; + + span { + width: 5px; + height: 5px; + border-radius: 50%; + background: #d923ff; + display: inline-block; + animation: ${bounce} 1.4s infinite ease-in-out both; + + &:nth-child(1) { + animation-delay: -0.32s; + } + + &:nth-child(2) { + animation-delay: -0.16s; + } + + &:nth-child(3) { + animation-delay: 0s; + } + } +`; diff --git a/src/components/organisms/RepoDetailContent/Chatting.jsx b/src/components/organisms/RepoDetailContent/Chatting.jsx deleted file mode 100644 index 1abeced..0000000 --- a/src/components/organisms/RepoDetailContent/Chatting.jsx +++ /dev/null @@ -1,643 +0,0 @@ -import React, { useState, useRef, useEffect } from 'react'; -import styled, { css, keyframes } from 'styled-components'; -import { Send, User, Bot, Sparkles, RefreshCw, EllipsisVertical } from 'lucide-react'; -import { Typo } from "../../index.js"; -import useClickAway from '../../../hooks/useClickAway.js'; -import { TypingMarkdownRenderer, MarkdownRenderer } from '../../index.js'; -import { chattingText } from "./chattingText.jsx"; - -// Animations -const fadeIn = keyframes` - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -`; - -const shimmer = keyframes` - 0% { background-position: -200% 0; } - 100% { background-position: 200% 0; } -`; - -const WelcomeFadeIn = keyframes` - from { - opacity: 0; - transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 - } - to { - opacity: 1; - transform: translate(-50%, -30%); // 최종 위치와 동일하게 설정 - } -` - -// Styled Components -const ChatWrapper = styled.div` - display: flex; - flex-direction : column; - align-items: flex-end; - height: 100%; - width: 100%; - background: rgba(45, 45, 58, 0.4); - backdrop-filter: blur(10px); - -`; - -const EllipsisMenuWrapper = styled.div` - width : 100%; - background: transparent; - display: flex; - justify-content: flex-end; - align-items: center; - height : 2.5rem; -` - -const EllipsisVerticalIcon = styled.div` - color : #ffffff; - cursor : pointer; - padding : 0.7rem; - display : flex; - justify-content: center; - align-items: center; - &:hover { - background: rgba(13, 15, 15, 0.3); - } - position : relative; - margin : 0 1rem 0 0 ; -` - -const EllipsisMenu = styled.div` - position: absolute; - cursor: default; - inset: 0px 0px auto auto; - margin: 0px; - transform: translate(0px, 2.5rem); - border-radius: 0.75rem; - padding: 1rem; - min-width: 15rem; - background-color: #232236; - z-index: 3000; - border: 1px solid rgba(63, 63, 70, 0.3); - - /* 최적화된 그림자 효과 */ - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.1), - 0 8px 24px rgba(0, 0, 0, 0.15); - - /* 최적화된 트랜지션 */ - transform-origin: top right; - transition: - opacity 0.2s ease-out, - visibility 0.2s ease-out, - transform 0.2s cubic-bezier(0.16, 1, 0.3, 1); - - /* 상태에 따른 스타일 변화 */ - pointer-events: ${props => props.isOpen ? 'auto' : 'none'}; - - ${props => props.isOpen ? css` - opacity: 1; - visibility: visible; - transform: translate(0, 2.5rem) scale(1); - ` : css` - opacity: 0; - visibility: hidden; - transform: translate(0, 2rem) scale(0.95); - `} - ul { - margin: 0; - padding-inline-start : 0; - li { - margin: 0.5rem 0; - background-color: #232236; - padding: 0.7rem 0.5rem; - color: ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#9492A6'}; // 비활성화 상태의 색상 변경 - transition: all 0.2s ease; // 부드러운 전환 효과 추가 - cursor: pointer; // hover 시 커서 변경 - border-radius: 4px; - display : flex; - align-items : center; - font-size : 1rem; - cursor: ${props => props.$disabled === true ? 'not-allowed' : 'cursor'}; - - span{ - padding : 0.3rem; - background: ${props => props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#373554'}; - display: flex; - transition: all 0.2s ease; - border-radius : 4px; - margin-right : .5rem; - } - svg{ - width : 1rem; - height : 1rem; - opacity: ${props => props.$disabled ? 0.4 : 1}; // 아이콘 투명도 - color : ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#d923ff'} - } - - &:hover { - background-color: ${props => props.$disabled ? '#232236' : '#373554'}; // 비활성화 상태에서는 hover 효과 제거 - color: ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff'}; - - span { - background: ${props => props.$disabled ? 'rgba(55, 53, 84, 0.5)' : '#a25cff'}; - } - svg { - color : ${props => props.$disabled ? 'rgba(113, 119, 144, 0.4)' : '#ffffff'} - } - } - } - } -` - -const ChatContainer = styled.div` - display: flex; - flex-direction: column; - /* flex : 1; */ - height : calc(100% - 2.5rem); - width: 100%; - border-radius: 16px; - /* border: 1px solid rgba(255, 255, 255, 0.1); */ -`; - -const MessageContainer = styled.div` - flex: 1; - overflow-y: auto; - padding: 24px; - position: relative; - scroll-behavior: smooth; - - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-thumb { - background: rgba(217, 35, 255, 0.2); - border-radius: 3px; - - &:hover { - background: rgba(217, 35, 255, 0.3); - } - } -`; - -const WelcomeTitle = styled.div` - padding: 0 0 2rem 0; - display: flex; - flex-direction: column; - gap: .5rem; - animation: ${fadeIn} 0.6s ease-out; -`; - -const WelcomeMessage = styled.div` - display: flex; - flex-direction: column; - gap: .5rem; - align-items: flex-start; - background: rgba(105, 14, 124, 0.05); - border: 1px solid rgba(217, 35, 255, 0.1); - padding: 1.5rem; - border-radius: 1rem; - position: absolute; - bottom: 0; - left: 50%; - transform: translate(-50%, -10%); // 초기 위치를 최종 위치와 동일하게 설정 - backdrop-filter: blur(10px); - animation: ${WelcomeFadeIn} 0.6s ease-out; - max-width: 90%; - width : 60%; - box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); -`; - -const WelcomeMessageTitle = styled.div` -display : flex; -flex-direction: row; -gap : 1rem; -align-items: center; -justify-content: center; -` - -const SparklesIcon = styled(Sparkles)` - display: inline; - margin-right: 0.5rem; - color: #d923ff; - width: 24px; - height: 24px; -`; - -const BotIcon = styled.div` - width: 2.5rem; - height: 2.5rem; - background: linear-gradient(135deg, #d923ff, #a78bfa); - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - box-shadow: 0 2px 10px rgba(217, 35, 255, 0.2); -`; - -const MessageContent = styled.div` - color: #e4e4e7; - font-size: 0.95rem; - line-height: 1.6; - - strong { - color: white; - font-size : 1.3rem; - font-weight: 500; - display: flex; - align-items: center; - gap: 0.5rem; - /* margin-bottom: 0.5rem; */ - } - - ul { - margin-top: 0.75rem; - padding-left : 3.5rem; - li { - margin: 0.5rem 0; - padding-left: 1rem; - position: relative; - - &:before { - content: "•"; - color: #d923ff; - position: absolute; - left: 0; - } - } - } -`; - -const Badge = styled.span` -padding: 0.125rem 0.5rem; -background-color: #d923ff; -font-size: 0.75rem; -border-radius: 9999px; -color: white; -margin-left: 0.5rem; -font-weight: 500; -margin : 0 0 0.2rem 0.2rem; -`; - - -const ChatMessages = styled.div` - display: flex; - flex-direction: column; - gap: 16px; - padding-bottom: 120px; -`; - -const MessageBubble = styled.div` - display: flex; - align-items: flex-start; - gap: 12px; - margin: ${props => props.isUser ? '0 0 0 auto' : '0 auto 0 0'}; - max-width: 70%; - animation: ${fadeIn} 0.3s ease-out; -`; - -const Avatar = styled.div` - width: 36px; - height: 36px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - background: ${props => props.isUser ? - 'linear-gradient(135deg, #d923ff, #a78bfa)' : - 'rgba(63, 63, 70, 0.8)'}; - color: white; - order: ${props => props.isUser ? 1 : 0}; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); -`; - -const Message = styled.div` - background: rgba(63, 63, 70, 0.8); - padding: 14px 18px; - border-radius: 16px; - color: white; - font-size: 0.95rem; - line-height: 1.5; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - backdrop-filter: blur(10px); - ${props => props.isUser ? ` - background: transparent; - border : 1px solid #a78bfa - `: - null - } -`; - -const InputContainer = styled.div` - padding: 20px; - background: rgba(255, 255, 255, 0.03); - border-top: 1px solid rgba(255, 255, 255, 0.06); - border-radius: 0 0 16px 16px; -`; - -const InputWrapper = styled.div` - display: flex; - gap: 12px; - align-items: center; - background: rgba(255, 255, 255, 0.07); - border-radius: 12px; - padding: 8px 16px; - transition: all 0.2s ease; - border: 1px solid rgba(255, 255, 255, 0.1); - - &:focus-within { - background: rgba(255, 255, 255, 0.09); - border-color: rgba(217, 35, 255, 0.3); - box-shadow: 0 0 0 2px rgba(217, 35, 255, 0.1); - } -`; - -const Input = styled.input` - flex: 1; - background: transparent; - border: none; - color: white; - font-size: 0.95rem; - padding: 10px 0; - - &::placeholder { - color: rgba(255, 255, 255, 0.3); - } - - &:focus { - outline: none; - } -`; - -const ActionButton = styled.button` - background: transparent; - border: none; - color: ${props => props.disabled ? 'rgba(255, 255, 255, 0.2)' : '#d923ff'}; - cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'}; - padding: 8px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; - border-radius: 8px; - - &:hover:not(:disabled) { - background: rgba(217, 35, 255, 0.1); - color: #e048ff; - } - - &:active:not(:disabled) { - transform: scale(0.95); - } -`; - -const LoadingDots = styled.div` - display: flex; - gap: 4px; - align-items: center; - padding: 4px 8px; - - span { - width: 4px; - height: 4px; - border-radius: 50%; - background: #d923ff; - animation: ${shimmer} 1.5s infinite; - opacity: 0.7; - - &:nth-child(2) { animation-delay: 0.2s; } - &:nth-child(3) { animation-delay: 0.4s; } - } -`; - -const ChatbotUI = () => { - const [isMenuOpen, setIsMenuOpen] = useState(false); - const menuRef = useRef(null); // 메뉴 ref 생성 - - const [messages, setMessages] = useState([]); - const [inputText, setInputText] = useState(''); - const [isLoading, setIsLoading] = useState(false); - const messagesEndRef = useRef(null); - - const [isTest, setIsTest] = useState(0); - const [completedMessages, setCompletedMessages] = useState(new Set()); - - const handleMessageComplete = (messageId) => { - setCompletedMessages(prev => new Set([...prev, messageId])); - }; - - useClickAway(menuRef, () => { - if (isMenuOpen) { - setIsMenuOpen(false); - } - }); - - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }; - - useEffect(() => { - scrollToBottom(); - }, [messages]); - - const handleSend = async () => { - if (!inputText.trim() || isLoading) return; - - // Add user message - setMessages(prev => [...prev, { - id: Date.now(), - text: inputText, - isUser: true - }]); - setInputText(''); - setIsLoading(true); - - try { - // Simulate API call - await new Promise(resolve => setTimeout(resolve, 2000)); - if (isTest === 0) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[0], - isUser: false - }]); - setIsTest((state) => state + 1); - } - if (isTest === 1) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[1], - isUser: false - }]); - setIsTest((state) => state + 1); - } - - if (isTest > 1) { - setMessages(prev => [...prev, { - id: Date.now(), - text: chattingText[2], - isUser: false - }]); - setIsTest((state) => state + 1); - } - - } catch (error) { - setMessages(prev => [...prev, { - id: Date.now(), - text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", - isUser: false, - isError: true - }]); - } finally { - setIsLoading(false); - } - }; - - const handleReset = () => { - setMessages([]); - }; - - const handleKeyPress = (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSend(); - } - }; - console.log(messages.length === 0) - return ( - - - setIsMenuOpen(!isMenuOpen)}> - - -
    -
  • - - reset -
  • -
-
-
-
- - - - - - Dododocs AI Chat - - - 연결된 GitHub 레포지토리의 코드를 기반으로 답변을 제공해드립니다. - - - { - messages.length === 0 ? - - - - - - - - DODODOCS AI - {/* AI 챗봇 */} - -

- 안녕하세요! GitHub 레포지토리의 코드에 대해 궁금하신 점을 물어보세요. - 자세한 분석과 함께 답변해드리겠습니다. -

-
-
- -
    -
  • 코드 구조와 아키텍처 분석
  • -
  • 함수와 클래스의 역할 설명
  • -
  • 코드 개선 방안 제안
  • -
  • 버그 해결 방안 제시
  • -
-
-
- : null - } - - - - {messages.map(message => ( - - - {message.isUser ? : } - - - { - message.isUser ? - message.text - : - handleMessageComplete(message.id)} - onComplete={() => console.log('타이핑 완료')} // 필요한 경우에만 사용 - - /> - - } - {/* */} - {/* {message.text} */} - - - ))} - {isLoading && ( - - - - - - - - - - - - - )} -
- - - - - - setInputText(e.target.value)} - onKeyPress={handleKeyPress} - disabled={isLoading} - /> - - - - - - - - - - - ); -}; - -export default ChatbotUI; - - diff --git a/src/components/organisms/RepoDetailContent/Document.jsx b/src/components/organisms/RepoDetailContent/Document.jsx index 4c64ccf..e8ed8bd 100644 --- a/src/components/organisms/RepoDetailContent/Document.jsx +++ b/src/components/organisms/RepoDetailContent/Document.jsx @@ -5,9 +5,9 @@ import { Camera, Pencil, Video, Palette, Layout, Box, MoreVertical } from 'lucid import api from "../../../api/axios.js"; import { Splitter } from "../../index.js" import { MarkdownRenderer, LoadingSpinner } from '../../index.js'; -import documentText from './documentText.jsx'; import { useDocument } from '../../../hooks/RepoDetailContent/useDocument.js'; -import { useRegisteredRepoStore } from "../../../store/store.js" +import { useRegisteredRepoStore, useAppModalStore } from "../../../store/store.js" +import NoDocument from "./NoDocuDetailContent/NoDocument.jsx" const Container = styled.div` display: flex; @@ -173,7 +173,7 @@ const NavItem = ({ onClick, icon: Icon, children, active }) => ( const Document = () => { //SECTION Document - const { activeRepositoryId } = useRegisteredRepoStore(); + const { AppRepo } = useAppModalStore(); const { @@ -181,42 +181,12 @@ const Document = () => { isLoading, isError, error - } = useDocument(activeRepositoryId); + } = useDocument(AppRepo.registeredRepoId); useEffect(() => { console.log('docsData', docsData) }, [docsData]) - const [selectedDoc, setSelectedDoc] = useState('AuthController'); // 선택된 문서 상태 추가 - const [content, setContent] = useState(documentText[0]); // 현재 표시될 내용 - - // 문서 메뉴 데이터 정의 - // const controllerDocs = [ - // { id: 'AuthController', fileName: 'AuthController.md', content: documentText[0] }, - // { id: 'KeywordController', fileName: 'KeywordController.md', content: documentText[1] }, - // { id: 'LiveInformationController', fileName: 'LiveInformationController.md', content: documentText[2] }, - // { id: 'MemberController', fileName: 'MemberController.md', content: documentText[0] }, - // { id: 'MemberLiveInformationController', fileName: 'MemberLiveInformationController.md', content: documentText[0] }, - // { id: 'PlannerController', fileName: 'PlannerController.md', content: documentText[0] }, - // { id: 'RecommendController', fileName: 'RecommendController.md', content: documentText[0] }, - // { id: 'TripController', fileName: 'TripController.md', content: documentText[0] }, - // { id: 'TripScheduleController', fileName: 'TripScheduleController.md', content: documentText[0] } - // ]; - - - - - // 문서 선택 핸들러 - // const handleDocSelect = (fileName) => { - // setSelectedDoc(fileName); - // const selectedContent = controllerDocs.find(doc => doc.fileName === fileName)?.content; - // if (selectedContent) { - // setContent(selectedContent); - // } - - // }; - - // 선택된 파일명과 내용을 관리하는 상태 const [selectedFileName, setSelectedFileName] = useState(''); const [currentContent, setCurrentContent] = useState(''); @@ -228,18 +198,21 @@ const Document = () => { // docsData가 변경될 때마다 문서 목록 업데이트 useEffect(() => { if (!docsData) return; - // Controller 문서 필터링 (Test 파일 제외) const controllers = docsData.regularFiles + console.log('controllers', controllers) // Summary 문서 설정 const summaries = docsData.summaryFiles || []; setControllerDocs(controllers); setSummaryDocs(summaries); + if (controllers.length === 0 && summaries.length === 0) { + console.log("둘다 ----0000") + } // 초기 선택된 문서 설정 - if (controllers.length && !selectedFileName) { + if (controllers?.length && !selectedFileName) { setSelectedFileName(controllers[0].fileName); setCurrentContent(controllers[0].fileContents); } @@ -253,6 +226,9 @@ const Document = () => { if (isLoading) return ; + if (controllerDocs.length === 0 && summaryDocs.length === 0) { + return ; + } return ( @@ -267,7 +243,7 @@ const Document = () => {
regular Docs - {controllerDocs.map(doc => ( + {controllerDocs?.map(doc => ( { {isError ? ( - ) : ( )} diff --git a/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.jsx b/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.jsx new file mode 100644 index 0000000..20a42b9 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { MessageSquareOff, ArrowRight, FileText, BookOpen, GitPullRequest } from 'lucide-react'; +import * as S from './GuideChatting.styles.js'; +import { useNavigate } from 'react-router-dom'; + +const NoChatNotice = () => { + const navigate = useNavigate(); + + return ( + + + + + + + 가이드에선 채팅 기능이 제공되지 않습니다 + + + DODODOCS는 AI 문서화에 집중하여 더 나은 문서 생성 경험을 제공합니다. + 지금 바로 문서 생성을 시작해보세요. + + +
  • + + 프로젝트 코드 기반 문서 자동 생성 +
  • +
  • + + API 문서 및 아키텍처 문서화 +
  • +
  • + + 코드 변경사항 자동 문서 업데이트 +
  • +
    + navigate('/repositories')} > + 문서 생성하기 + + +
    +
    + ); +}; + +export default NoChatNotice; \ No newline at end of file diff --git a/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.styles.js b/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.styles.js new file mode 100644 index 0000000..671d0b3 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/GuideChattingDetailContent/GuideChatting.styles.js @@ -0,0 +1,102 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + height: 100%; + background: rgba(45, 45, 58, 0.4); + backdrop-filter: blur(10px); + display: flex; + justify-content: center; + align-items: center; + padding: 2rem; +`; + +export const NoticeCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border: 1px solid rgba(63, 63, 70, 0.3); + border-radius: 1rem; + padding: 2.5rem; + max-width: 32rem; + width: 100%; + text-align: center; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +export const IconWrapper = styled.div` + width: 3.5rem; + height: 3.5rem; + background: rgba(147, 51, 234, 0.2); + border-radius: 0.75rem; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const Title = styled.h2` + color: white; + font-size: 1.5rem; + font-weight: 600; + margin-bottom: 1rem; + + span { + background: linear-gradient(to right, rgb(162, 92, 255), #d923ff); + -webkit-background-clip: text; + color: transparent; + } +`; + +export const Description = styled.p` + color: #a1a1aa; + line-height: 1.6; + margin-bottom: 1.5rem; +`; + +export const FeatureList = styled.ul` + list-style: none; + padding: 0; + text-align: left; + margin: 1rem 0; + + li { + color: #e4e4e7; + padding: 0.75rem; + display: flex; + align-items: center; + gap: 0.75rem; + border-radius: 0.5rem; + margin-bottom: 0.5rem; + background: rgba(63, 63, 70, 0.3); + + svg { + color: #9333ea; + flex-shrink: 0; + } + } +`; + +export const Button = styled.button` + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + background: #9333ea; + color: white; + border-radius: 0.5rem; + border: none; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: #7e22ce; + } + + svg { + transition: transform 0.2s; + } + + &:hover svg { + transform: translateX(4px); + } +`; diff --git a/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.jsx b/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.jsx new file mode 100644 index 0000000..e9775e1 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Coffee, ArrowRight, Code, XCircle } from 'lucide-react'; +import * as S from './NoDocument.style.js'; + +const MoDocument = () => { + return ( + + + + + + + + 현재 Java 프로젝트만 지원합니다 + + + DODODOCS는 현재 Java 프로젝트에 대한 문서화만 지원하고 있습니다. + 다른 언어 지원은 순차적으로 추가될 예정입니다. + + +
  • + + 타 프로그래밍 언어는 현재 지원되지 않습니다 +
  • +
    +
    +
    + ); +}; + +export default MoDocument; \ No newline at end of file diff --git a/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.style.js b/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.style.js new file mode 100644 index 0000000..4f6590f --- /dev/null +++ b/src/components/organisms/RepoDetailContent/NoDocuDetailContent/NoDocument.style.js @@ -0,0 +1,103 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + height: 100%; + width: 100%; + background: rgba(45, 45, 58, 0.4); + backdrop-filter: blur(10px); + display: flex; + justify-content: center; + align-items: center; + padding: 2rem; +`; + +export const NoticeCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border: 1px solid rgba(63, 63, 70, 0.3); + border-radius: 1rem; + padding: 2.5rem; + max-width: 32rem; + width: 100%; + text-align: center; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +export const IconWrapper = styled.div` + width: 3.5rem; + height: 3.5rem; + background: rgba(147, 51, 234, 0.2); + border-radius: 0.75rem; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; +`; + +export const Title = styled.h2` + color: white; + font-size: 1.5rem; + font-weight: 600; + margin-bottom: 1rem; + + span { + background: linear-gradient(to right, rgb(162, 92, 255), #d923ff); + -webkit-background-clip: text; + color: transparent; + } +`; + +export const Description = styled.p` + color: #a1a1aa; + line-height: 1.6; + margin-bottom: 1.5rem; +`; + +export const FeatureList = styled.ul` + list-style: none; + padding: 0; + text-align: left; + margin: 1rem 0; + + li { + color: #e4e4e7; + padding: 0.75rem; + display: flex; + align-items: center; + gap: 0.75rem; + border-radius: 0.5rem; + margin-bottom: 0.5rem; + background: rgba(63, 63, 70, 0.3); + + svg { + color: #9333ea; + flex-shrink: 0; + } + } +`; + +export const Button = styled.button` + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1.5rem; + background: #9333ea; + color: white; + border-radius: 0.5rem; + border: none; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s; + + &:hover { + background: #7e22ce; + } + + svg { + transition: transform 0.2s; + } + + &:hover svg { + transform: translateX(4px); + } +`; diff --git a/src/components/organisms/RepoDetailContent/ReadMe.jsx b/src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.jsx similarity index 72% rename from src/components/organisms/RepoDetailContent/ReadMe.jsx rename to src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.jsx index b1dc303..b563465 100644 --- a/src/components/organisms/RepoDetailContent/ReadMe.jsx +++ b/src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.jsx @@ -1,267 +1,23 @@ // src/components/organisms/RepoDetailContent/ReadMe.jsx import React, { useState, useEffect, useRef } from 'react'; -import styled from 'styled-components'; -import { Camera, Pencil, Video, Palette, Layout, Box, MoreVertical, GripVertical, Check, X, Plus } from 'lucide-react'; -import api from "../../../api/axios.js"; -import { Splitter } from "../../index.js" -import { MarkdownRenderer, LoadingSpinner, MarkdownEditor } from '../../index.js'; +import { Camera, Pencil, Video, Palette, Layout, Box, Loader, GripVertical, Check, X, Plus } from 'lucide-react'; +import api from "../../../../api/axios.js"; +import { Splitter } from "../../../index.js" +import { MarkdownRenderer, LoadingSpinner } from '../../../index.js'; import { useLocation, useNavigate } from 'react-router-dom'; -import { Button } from "../../index.js" -import { markdownText } from './markdownText.jsx'; -import { useReadme } from '../../../hooks/RepoDetailContent/useReadme.js'; -import { useRegisteredRepoStore } from "../../../store/store.js" - -const Container = styled.div` - display: flex; - height: 100%; - width : 100%; - background: #10121b66; -`; - -const SideBar = styled.div` - display : flex; - flex-direction : column; - justify-content: space-between; - gap :1rem; - background: rgba(24, 24, 27, 0.5); - padding: 1.5rem 0; - /* overflow-y: auto; */ - height : 100%; - -`; - - - -const Section = styled.div` - margin-bottom: ${props => props.mb || 0}rem; - flex: ${props => props.flex || 1}; - min-height: 0; // 중요: flex 자식 요소의 overflow 처리를 위해 필요 - display: flex; - flex-direction: column; - justify-content: center; -`; - -const SectionTitle = styled.h2` - text-transform: uppercase; - color: #71717a; - font-size: 0.75rem; - padding: 0 1.5rem; - margin-bottom: 0.15rem; -`; - - -const SectionContent = styled.div` -flex: 1; // Section 내부에서 남은 공간을 모두 차지 - display: flex; - flex-direction: column; - overflow-y: auto; - -// 스크롤바 기본 상태 -&::-webkit-scrollbar { - width: 6px; - opacity: 0; - transition: opacity 0.3s ease; - } +import { Button } from "../../../index.js" +import { useReadme } from '../../../../hooks/RepoDetailContent/useReadme.js'; +import { useAppModalStore } from "../../../../store/store.js" - &:hover::-webkit-scrollbar { - opacity: 1; - } +import { + Container, SideBar, Section, SectionTitle, SectionContent, - &::-webkit-scrollbar-track { - background: rgb(1 2 3 / 40%); - } + DragHandle, DropIndicator, ToggleButton, NavItemContainer, NavItemWrapper, + ExcludedSectionsContainer, RestorableNavItem, IconWrapper, Badge, + ActionBtnWrapper, MainContent, + ErrorContainer, ErrorLoadingCard, ErrorIconWrapper, ErrorMessage, ErrorSubMessage - &::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.3); - border-radius: 3px; - - &:hover { - background-color: rgba(255, 255, 255, 0.5); - } - } - - // Firefox용 스크롤바 스타일 - scrollbar-width: thin; - scrollbar-color: transparent transparent; - - &:hover { - scrollbar-color: rgba(255, 255, 255, 0.3) rgb(1 2 3 / 40%); - } -` - - - - -const DragHandle = styled.div` - margin-right: 0.5rem; - cursor: grab; - opacity: ${props => props.isCustomMode ? 0.7 : 0}; - transition: opacity 0.2s; - - &:active { - cursor: grabbing; - } -`; - -const DropIndicator = styled.div` - position: absolute; - left: 0; - right: 0; - height: 2px; - background: #9333ea; - z-index: 10; - ${props => props.isTop ? 'top: -1px;' : 'bottom: -1px;'} - - &::before { - content: ''; - position: absolute; - left: -0.5rem; - top: -0.375rem; - width: 1rem; - height: 1rem; - background: #9333ea; - border-radius: 50%; - } -`; - - -const ToggleButton = styled.button` - display: flex; - align-items: center; - justify-content: center; - width: 24px; - height: 24px; - border-radius: 4px; - margin-left: auto; - background: ${props => props.isIncluded ? 'rgba(147, 51, 234, 0.2)' : 'rgba(255, 255, 255, 0.1)'}; - color: ${props => props.isIncluded ? '#9333ea' : '#71717a'}; - transition: all 0.2s; - - &:hover { - background: ${props => props.isIncluded ? 'rgba(147, 51, 234, 0.3)' : 'rgba(255, 255, 255, 0.2)'}; - } -`; - - - -const NavItemContainer = styled.div` - position: relative; - opacity: 1; - - &::before { - content: ''; - position: absolute; - left: 0; - right: 0; - height: 2px; - background: #9333ea; - opacity: 0; - transition: opacity 0.2s; - } -`; - - - - -const NavItemWrapper = styled.div` - display: flex; - align-items: center; - padding: 0.75rem 1.5rem; - cursor: ${props => props.isCustomMode ? 'move' : 'pointer'}; - transition: all 0.2s; - color: ${props => props.active ? 'white' : '#a1a1aa'}; - background: ${props => props.active ? 'rgba(147, 51, 234, 0.1)' : 'transparent'}; - position: relative; - - &:hover { - background: ${props => props.active ? 'rgba(147, 51, 234, 0.1)' : 'rgba(255, 255, 255, 0.05)'}; - color: white; - } - - ${props => props.active && ` - &::before { - content: ''; - position: absolute; - left: 0; - top: 0; - width: 2px; - height: 100%; - background: #9333ea; - } - `} -`; - -const ExcludedSectionsContainer = styled.div` - margin-top: 1rem; - border-top: 1px solid rgba(255, 255, 255, 0.1); - padding-top: 1rem; -`; - -const RestorableNavItem = styled(NavItemWrapper)` - opacity: 0.6; - background: transparent; - - &:hover { - opacity: 1; - background: rgba(255, 255, 255, 0.05); - } -`; - - -const IconWrapper = styled.div` - margin-right: 0.75rem; - opacity: 0.7; -`; - -const Badge = styled.span` - margin-left: auto; - font-size: 0.75rem; - background: rgba(147, 51, 234, 0.2); - color: #9333ea; - padding: 0.125rem 0.5rem; - border-radius: 9999px; -`; - -const ActionBtnWrapper = styled.div` -display : flex; -flex-direction: column; -gap : 1rem; -justify-content: center; -align-items: center; -padding-top : 0.75rem; -` - -const MainContent = styled.div` - flex: 1; - height : 100%; - background: #10121b66; - overflow-y: auto; - - scroll-behavior: smooth; - scrollbar-width: thin; - scrollbar-color: rgba(255, 255, 255, 0.3) transparent; - - &::-webkit-scrollbar { - width: 6px; - } - - &::-webkit-scrollbar-track { - background: rgb(1 2 3 / 40%); - } - - &::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.3); - border-radius: 3px; - - &:hover { - background-color: rgba(255, 255, 255, 0.5); - } -} -`; - -const handleChange = (newContent) => { - console.log('마크다운이 변경되었습니다:', newContent); -}; +} from "./ReadMe.styles.js" @@ -329,7 +85,7 @@ const NavItem = ({ const ReadMe = () => { - const { activeRepositoryId } = useRegisteredRepoStore(); + const { AppRepo } = useAppModalStore(); const location = useLocation(); const navigate = useNavigate(); const mainContentRef = useRef(null); @@ -339,10 +95,10 @@ const ReadMe = () => { isLoading, isError, error - } = useReadme(activeRepositoryId); + } = useReadme(AppRepo.registeredRepoId); useEffect(() => { - console.log("😂😂 READ ME : ", markdownText) + console.log("😂😂 READ ME : ", [markdownText]) }, [markdownText]) @@ -443,7 +199,7 @@ const ReadMe = () => { const initialSections = parseSections(markdownText); setSectionsReadMe(initialSections); - console.log("😂😂😂😂😂 READ ME : ", markdownText) + console.log("😂😂😂😂😂 READ ME : ", [markdownText]) }, [markdownText]); @@ -495,7 +251,6 @@ const ReadMe = () => { }, [tableContent]); - if (isLoading) return ; function hasLeadingEmoji(str) { if (!str) { @@ -690,6 +445,8 @@ const ReadMe = () => { } }; + if (isLoading) return ; + return ( @@ -733,8 +490,6 @@ const ReadMe = () => { > {withoutEmoji}
    - - ); })} {/* 제외된 섹션 목록 (커스텀 모드일 때만 표시) */} @@ -791,21 +546,19 @@ const ReadMe = () => { { isLoading ? : isError ? - //
    - // Error: {error.message} - //
    - sectionsReadMe.map((section) => { - const [emoji, withoutEmoji] = hasLeadingEmoji(section.name); - const sectionId = `${emoji ? emoji + '-' : ''}${withoutEmoji}` - .toLowerCase() - .replace(/\s+/g, '-'); - return ( -
    - - -
    - ); - }) + + + + + + + 레포지토리 결과물을 생성중입니다. + + + 잠시만 기다려주세요. 레포지토리를 분석하고 문서를 생성하는데 시간이 소요됩니다. + + + : sectionsReadMe.filter(section => !section.excludeSection).map((section) => { const [emoji, withoutEmoji] = hasLeadingEmoji(section.name); @@ -822,12 +575,6 @@ const ReadMe = () => { }) } -
    - {/* */} -
    diff --git a/src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.styles.js b/src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.styles.js new file mode 100644 index 0000000..2776bf8 --- /dev/null +++ b/src/components/organisms/RepoDetailContent/ReadMeDetailContent/ReadMe.styles.js @@ -0,0 +1,295 @@ +import styled, { keyframes } from 'styled-components'; + +export const Container = styled.div` + display: flex; + height: 100%; + width: 100%; + background: #10121b66; +`; + +export const SideBar = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 1rem; + background: rgba(24, 24, 27, 0.5); + padding: 1.5rem 0; + /* overflow-y: auto; */ + height: 100%; +`; + +export const Section = styled.div` + margin-bottom: ${(props) => props.mb || 0}rem; + flex: ${(props) => props.flex || 1}; + min-height: 0; // 중요: flex 자식 요소의 overflow 처리를 위해 필요 + display: flex; + flex-direction: column; + justify-content: center; +`; + +export const SectionTitle = styled.h2` + text-transform: uppercase; + color: #71717a; + font-size: 0.75rem; + padding: 0 1.5rem; + margin-bottom: 0.15rem; +`; + +export const SectionContent = styled.div` + flex: 1; // Section 내부에서 남은 공간을 모두 차지 + display: flex; + flex-direction: column; + overflow-y: auto; + + // 스크롤바 기본 상태 + &::-webkit-scrollbar { + width: 6px; + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover::-webkit-scrollbar { + opacity: 1; + } + + &::-webkit-scrollbar-track { + background: rgb(1 2 3 / 40%); + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.3); + border-radius: 3px; + + &:hover { + background-color: rgba(255, 255, 255, 0.5); + } + } + + // Firefox용 스크롤바 스타일 + scrollbar-width: thin; + scrollbar-color: transparent transparent; + + &:hover { + scrollbar-color: rgba(255, 255, 255, 0.3) rgb(1 2 3 / 40%); + } +`; + +export const DragHandle = styled.div` + margin-right: 0.5rem; + cursor: grab; + opacity: ${(props) => (props.isCustomMode ? 0.7 : 0)}; + transition: opacity 0.2s; + + &:active { + cursor: grabbing; + } +`; + +export const DropIndicator = styled.div` + position: absolute; + left: 0; + right: 0; + height: 2px; + background: #9333ea; + z-index: 10; + ${(props) => (props.isTop ? 'top: -1px;' : 'bottom: -1px;')} + + &::before { + content: ''; + position: absolute; + left: -0.5rem; + top: -0.375rem; + width: 1rem; + height: 1rem; + background: #9333ea; + border-radius: 50%; + } +`; + +export const ToggleButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 4px; + margin-left: auto; + background: ${(props) => + props.isIncluded ? 'rgba(147, 51, 234, 0.2)' : 'rgba(255, 255, 255, 0.1)'}; + color: ${(props) => (props.isIncluded ? '#9333ea' : '#71717a')}; + transition: all 0.2s; + + &:hover { + background: ${(props) => + props.isIncluded ? 'rgba(147, 51, 234, 0.3)' : 'rgba(255, 255, 255, 0.2)'}; + } +`; + +export const NavItemContainer = styled.div` + position: relative; + opacity: 1; + + &::before { + content: ''; + position: absolute; + left: 0; + right: 0; + height: 2px; + background: #9333ea; + opacity: 0; + transition: opacity 0.2s; + } +`; + +export const NavItemWrapper = styled.div` + display: flex; + align-items: center; + padding: 0.75rem 1.5rem; + cursor: ${(props) => (props.isCustomMode ? 'move' : 'pointer')}; + transition: all 0.2s; + color: ${(props) => (props.active ? 'white' : '#a1a1aa')}; + background: ${(props) => (props.active ? 'rgba(147, 51, 234, 0.1)' : 'transparent')}; + position: relative; + + &:hover { + background: ${(props) => + props.active ? 'rgba(147, 51, 234, 0.1)' : 'rgba(255, 255, 255, 0.05)'}; + color: white; + } + + ${(props) => + props.active && + ` + &::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 2px; + height: 100%; + background: #9333ea; + } + `} +`; + +export const ExcludedSectionsContainer = styled.div` + margin-top: 1rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 1rem; +`; + +export const RestorableNavItem = styled(NavItemWrapper)` + opacity: 0.6; + background: transparent; + + &:hover { + opacity: 1; + background: rgba(255, 255, 255, 0.05); + } +`; + +export const IconWrapper = styled.div` + margin-right: 0.75rem; + opacity: 0.7; +`; + +export const Badge = styled.span` + margin-left: auto; + font-size: 0.75rem; + background: rgba(147, 51, 234, 0.2); + color: #9333ea; + padding: 0.125rem 0.5rem; + border-radius: 9999px; +`; + +export const ActionBtnWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 1rem; + justify-content: center; + align-items: center; + padding-top: 0.75rem; +`; + +export const MainContent = styled.div` + flex: 1; + height: 100%; + background: #10121b66; + overflow-y: auto; + + scroll-behavior: smooth; + scrollbar-width: thin; + scrollbar-color: rgba(255, 255, 255, 0.3) transparent; + will-change: transform; // 스크롤 성능 향상 + transform: translateZ(0); // GPU 가속 + -webkit-overflow-scrolling: touch; // iOS 스크롤 성능 향상 + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: rgb(1 2 3 / 40%); + } + + &::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.3); + border-radius: 3px; + + &:hover { + background-color: rgba(255, 255, 255, 0.5); + } + } +`; + +export const ErrorContainer = styled.div` + height: 100%; + background: rgba(45, 45, 58, 0.4); + backdrop-filter: blur(10px); + display: flex; + justify-content: center; + align-items: center; + padding: 2rem; +`; + +export const ErrorLoadingCard = styled.div` + background: rgba(39, 39, 42, 0.5); + border: 1px solid rgba(63, 63, 70, 0.3); + border-radius: 1rem; + padding: 2.5rem; + max-width: 32rem; + width: 100%; + text-align: center; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.1); +`; + +const pulse = keyframes` + 0% { transform: scale(1); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +`; + +export const ErrorIconWrapper = styled.div` + width: 3.5rem; + height: 3.5rem; + background: rgba(147, 51, 234, 0.2); + border-radius: 0.75rem; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1.5rem; + color: #e9d5ff; + animation: ${pulse} 2s infinite; +`; + +export const ErrorMessage = styled.p` + color: #e9d5ff; + font-size: 1.25rem; + margin-bottom: 1rem; + line-height: 1.6; +`; + +export const ErrorSubMessage = styled.p` + color: #a1a1aa; + font-size: 1rem; + line-height: 1.5; +`; diff --git a/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx b/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx index fd055e0..5462f28 100644 --- a/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx +++ b/src/components/organisms/RepoDetailContent/RepoDetailContent.jsx @@ -2,21 +2,21 @@ import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import bg_img from "../../../assets/images/bg_img.jpg" import { Moon, Sun } from 'lucide-react'; -import { - Image, Typo, Button, TextBox, Select, -} from "../../index.js"; -import useAppModalStore from '../../../store/appModalStore.js'; + +// import useAppModalStore from '../../../store/appModalStore.js'; +import { useAppModalStore } from '../../../store/store.js'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useQueryClient } from '@tanstack/react-query'; import Modal from 'react-modal'; -import Chatting from "./Chatting.jsx" +import Chatting from "./CattingDetailContent/Chatting.jsx" import Document from "./Document.jsx" -import ReadMe from "./ReadMe.jsx" - +import ReadMe from "./ReadMeDetailContent/ReadMe.jsx" +import GuideChatting from "./GuideChattingDetailContent/GuideChatting.jsx" // Styled Components - const modalStyles = { content: { border: 'none', @@ -60,7 +60,7 @@ const AppWrapper = styled.div` content: ""; /* opacity: 0.5; */ opacity: 1; - ${props => props.isImgMode ? `background-image: url(${bg_img});` : null} + ${props => props.isImgMode ? `background-image: url(${bg_img});` : null} background-size: cover; background-position: center; position: absolute; @@ -183,7 +183,7 @@ const MainHeaderContent = styled.div` color: rgb(113 119 144 / 78%); border-bottom: 2px solid transparent; transition: 0.3s; - + cursor: pointer; &.active, &:hover { color: #f9fafb; border-bottom: 2px solid #f9fafb; @@ -269,34 +269,61 @@ background-color : #10121b66; ` // Main App Component -const App = ({ onClose }) => { +const App = ({ }) => { - const { isAppModalOpen, AppRepo } = useAppModalStore(); + const { AppRepo, openAppModal, closeAppModal } = useAppModalStore(); + const { repoTitle } = useParams(); + const navigate = useNavigate(); + const queryClient = useQueryClient(); const [isFullscreen, setIsFullscreen] = useState(false); const [isImgMode, setIsImgMode] = useState(true); const [activeMenu, setActiveMenu] = useState('Read Me Maker'); + + + const registeredReposList = queryClient.getQueryData(['registeredRepos']) || null; + + + // 모달 열기 useEffect(() => { - if (isAppModalOpen) { + if (repoTitle) { + console.log("😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇😇모달 열기 시 데이터 받아오기 ", repoTitle) + openAppModal(repoTitle, registeredReposList); document.body.style.overflow = 'hidden'; } + }, [repoTitle, registeredReposList, openAppModal]); + // 모달 닫기 처리 + const closeModalHandler = () => { + closeAppModal(); + navigate('/repositories'); // URL 변경하면 자동으로 컴포넌트가 언마운트됨 + }; + + // cleanup - 스크롤만 처리 + useEffect(() => { return () => { document.body.style.overflow = 'unset'; }; - }, [isAppModalOpen]); + }, []); + + + + const toggleFullscreen = () => { setIsFullscreen(prev => !prev); }; + + if (!repoTitle || !AppRepo) return null; + return ( {
    - + @@ -341,6 +368,7 @@ const App = ({ onClose }) => { {/* Add remaining content components here */} All Apps + { ['AI Code Document', 'AI Chatting', 'Read Me Maker'].map(menu => ( { // Chatting activeMenu === 'AI Chatting' ? - + { + repoTitle === 'guide' ? + : + + } : activeMenu === 'AI Code Document' ? diff --git a/src/components/organisms/RepoDetailContent/chattingText.jsx b/src/components/organisms/RepoDetailContent/chattingText.jsx deleted file mode 100644 index 217ef01..0000000 --- a/src/components/organisms/RepoDetailContent/chattingText.jsx +++ /dev/null @@ -1,171 +0,0 @@ -export const chattingText = [ - ` - ### 개념 설명 -\`AuthController\`는 클라이언트의 인증 관련 요청을 처리하는 RESTful API 컨트롤러입니다. 이 컨트롤러는 OAuth 인증을 위한 URI 생성, 로그인 처리, Access Token 갱신, 로그아웃 기능을 제공합니다. 각 메서드는 \`AuthService\`와 상호작용하여 비즈니스 로직을 수행하고, 클라이언트에게 적절한 응답을 반환합니다. - -### 코드 구현 참조 -- **파일 이름**: AuthController.java -- **전체 파일 경로**: \`src/main/java/moheng/auth/presentation/AuthController.java\` -- **관련 코드 스니펫**: -\`\`\`java -@RestController -@RequestMapping("/api/auth") -public class AuthController { - private final AuthService authService; - - public AuthController(AuthService authService) { - this.authService = authService; - } - - @GetMapping("/{oAuthProvider}/link") - public ResponseEntity generateUri(@PathVariable final String oAuthProvider) { - return ResponseEntity.ok(new OAuthUriResponse(authService.generateUri(oAuthProvider))); - } - - @PostMapping("/{oAuthProvider}/login") - public ResponseEntity login(@PathVariable final String oAuthProvider, - @RequestBody final TokenRequest tokenRequest, - final HttpServletResponse httpServletResponse) { - final MemberToken memberToken = authService.generateTokenWithCode(tokenRequest.getCode(), oAuthProvider); - final ResponseCookie responseCookie = ResponseCookie.from("refresh-token", memberToken.getRefreshToken()) - .maxAge(604800) - .sameSite("None") - .secure(true) - .httpOnly(true) - .path("/") - .build(); - httpServletResponse.addHeader(SET_COOKIE, responseCookie.toString()); - final AccessTokenResponse accessTokenResponse = new AccessTokenResponse(memberToken.getAccessToken()); - return ResponseEntity.status(CREATED).body(accessTokenResponse); - } - - @PostMapping("/extend/login") - public ResponseEntity extendLogin(@CookieValue("refresh-token") final String refreshToken) { - final RenewalAccessTokenResponse renewalAccessTokenResponse = - authService.generateRenewalAccessToken(new RenewalAccessTokenRequest(refreshToken)); - return ResponseEntity.status(CREATED).body(renewalAccessTokenResponse); - } - - @DeleteMapping("/logout") - public ResponseEntity logout(@Authentication final Accessor accessor, - @CookieValue("refresh-token") final String refreshToken) { - authService.removeRefreshToken(new LogoutRequest(refreshToken)); - return ResponseEntity.noContent().build(); - } -} -\`\`\` -- **코드의 관련성 설명**: 위의 코드 스니펫은 \`AuthController\`의 주요 메서드들을 포함하고 있으며, 각 메서드는 클라이언트의 요청에 따라 OAuth URI를 생성하거나, 로그인 요청을 처리하고, Access Token을 갱신하며, 로그아웃 요청을 처리합니다. 이 컨트롤러는 인증 프로세스의 핵심 역할을 수행하고, 클라이언트와 서버 간의 상호작용을 관리합니다. - -### API 엔드포인트 요약 -1. **Generate OAuth URI** - - **메서드**: GET - - **엔드포인트**: \`/api/auth/{oAuthProvider}/link\` - - **설명**: OAuth 제공자에 대한 URI를 생성합니다. - - **응답 예시**: - \`\`\`json - { - "uri": "https://example.com/oauth/authorize" - } - \`\`\` - -2. **Login** - - **메서드**: POST - - **엔드포인트**: \`/api/auth/{oAuthProvider}/login\` - - **설명**: OAuth 로그인을 수행하고 Access Token을 생성합니다. - - **응답 예시**: - \`\`\`json - { - "accessToken": "generated_access_token" - } - \`\`\` - -3. **Extend Login** - - **메서드**: POST - - **엔드포인트**: \`/api/auth/extend/login\` - - **설명**: Refresh Token을 사용하여 Access Token을 갱신합니다. - - **응답 예시**: - \`\`\`json - { - "accessToken": "new_generated_access_token" - } - \`\`\` - -4. **Logout** - - **메서드**: DELETE - - **엔드포인트**: \`/api/auth/logout\` - - **설명**: 로그아웃 요청을 처리하고 Refresh Token을 제거합니다. - - **응답 예시**: - \`\`\`json - {} - \`\`\` - -이러한 엔드포인트들은 클라이언트가 인증 및 세션 관리를 수행하는 데 필요한 기능을 제공합니다. - `, - ` - ### 개념 설명 -\`AuthController\`에서 404 에러는 클라이언트가 요청한 리소스(예: 특정 OAuth 제공자에 대한 URI 생성, 로그인 요청 등)가 서버에서 존재하지 않을 때 발생합니다. 이는 일반적으로 잘못된 URL 경로, 잘못된 HTTP 메서드 사용, 또는 요청한 리소스가 서버에 존재하지 않을 때 발생합니다. - -### 404 에러 발생 원인 -1. **잘못된 URL 경로**: 클라이언트가 잘못된 경로를 요청할 경우, 예를 들어 \`/api/auth/invalidProvider/link\`와 같이 존재하지 않는 OAuth 제공자를 요청하면 404 에러가 발생합니다. -2. **잘못된 HTTP 메서드**: 특정 엔드포인트에 대해 잘못된 HTTP 메서드를 사용하면 404 에러가 발생할 수 있습니다. 예를 들어, GET 메서드가 필요한 엔드포인트에 POST 메서드를 사용하면 404 에러가 발생합니다. -3. **리소스 미존재**: 요청한 리소스가 서버에 존재하지 않는 경우, 예를 들어 데이터베이스에 해당 회원 정보가 없을 때 발생할 수 있습니다. - -### 해결 방법 -1. **URL 경로 확인**: 클라이언트가 요청하는 URL 경로가 정확한지 확인합니다. OAuth 제공자 이름이 올바르게 전달되었는지 확인하고, API 문서에서 제공하는 경로와 일치하는지 검토합니다. - - 예: \`/api/auth/{oAuthProvider}/link\`에서 \`{oAuthProvider}\`가 유효한 값인지 확인합니다. - -2. **HTTP 메서드 확인**: 요청하는 API 엔드포인트에 대해 올바른 HTTP 메서드를 사용하고 있는지 확인합니다. 예를 들어, 로그인 요청은 POST 메서드를 사용해야 합니다. - - 예: \`POST /api/auth/{oAuthProvider}/login\` - -3. **리소스 존재 여부 확인**: 요청한 리소스가 서버에 존재하는지 확인합니다. 예를 들어, 데이터베이스에서 해당 회원 정보가 존재하는지 확인합니다. 필요한 경우, 적절한 예외 처리를 통해 클라이언트에게 404 에러를 명확히 알릴 수 있습니다. - -### 코드 구현 참조 -- **파일 이름**: ControllerAdvice.java -- **전체 파일 경로**: \`src/main/java/moheng/global/error/ControllerAdvice.java\` -- **관련 코드 스니펫**: -\`\`\`java -@ExceptionHandler({ NoExistMemberException.class, NoExistTripException.class }) -public ResponseEntity handleNotFoundResourceException(final RuntimeException e) { - logger.error(e.getMessage(), e); - ExceptionResponse errorResponse = new ExceptionResponse(e.getMessage()); - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); -} -\`\`\` -- **코드의 관련성 설명**: 위의 코드 스니펫은 \`ControllerAdvice\`에서 404 에러를 처리하는 방법을 보여줍니다. 특정 리소스가 존재하지 않을 때 발생하는 예외를 처리하여 클라이언트에게 적절한 404 응답을 반환합니다. 이를 통해 클라이언트는 요청한 리소스가 존재하지 않음을 알 수 있습니다. - -이러한 방법들을 통해 \`AuthController\`에서 발생하는 404 에러를 진단하고 해결할 수 있습니다. - `, - ` - ### 개념 설명 -\`AuthController\`에서 404 에러는 클라이언트가 요청한 리소스(예: 특정 OAuth 제공자에 대한 URI 생성, 로그인 요청 등)가 서버에서 존재하지 않을 때 발생합니다. 이는 일반적으로 잘못된 URL 경로, 잘못된 HTTP 메서드 사용, 또는 요청한 리소스가 서버에 존재하지 않을 때 발생합니다. - -### 404 에러 발생 원인 -1. **잘못된 URL 경로**: 클라이언트가 잘못된 경로를 요청할 경우, 예를 들어 \`/api/auth/invalidProvider/link\`와 같이 존재하지 않는 OAuth 제공자를 요청하면 404 에러가 발생합니다. -2. **잘못된 HTTP 메서드**: 특정 엔드포인트에 대해 잘못된 HTTP 메서드를 사용하면 404 에러가 발생할 수 있습니다. 예를 들어, GET 메서드가 필요한 엔드포인트에 POST 메서드를 사용하면 404 에러가 발생합니다. -3. **리소스 미존재**: 요청한 리소스가 서버에 존재하지 않는 경우, 예를 들어 데이터베이스에 해당 회원 정보가 없을 때 발생할 수 있습니다. - -### 해결 방법 -1. **URL 경로 확인**: 클라이언트가 요청하는 URL 경로가 정확한지 확인합니다. OAuth 제공자 이름이 올바르게 전달되었는지 확인하고, API 문서에서 제공하는 경로와 일치하는지 검토합니다. - - 예: \`/api/auth/{oAuthProvider}/link\`에서 \`{oAuthProvider}\`가 유효한 값인지 확인합니다. - -2. **HTTP 메서드 확인**: 요청하는 API 엔드포인트에 대해 올바른 HTTP 메서드를 사용하고 있는지 확인합니다. 예를 들어, 로그인 요청은 POST 메서드를 사용해야 합니다. - - 예: \`POST /api/auth/{oAuthProvider}/login\` - -3. **리소스 존재 여부 확인**: 요청한 리소스가 서버에 존재하는지 확인합니다. 예를 들어, 데이터베이스에서 해당 회원 정보가 존재하는지 확인합니다. 필요한 경우, 적절한 예외 처리를 통해 클라이언트에게 404 에러를 명확히 알릴 수 있습니다. - -### 코드 구현 참조 -- **파일 이름**: ControllerAdvice.java -- **전체 파일 경로**: \`src/main/java/moheng/global/error/ControllerAdvice.java\` -- **관련 코드 스니펫**: -\`\`\`java -@ExceptionHandler({ NoExistMemberException.class, NoExistTripException.class }) -public ResponseEntity handleNotFoundResourceException(final RuntimeException e) { - logger.error(e.getMessage(), e); - ExceptionResponse errorResponse = new ExceptionResponse(e.getMessage()); - return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); -} -\`\`\` -- **코드의 관련성 설명**: 위의 코드 스니펫은 \`ControllerAdvice\`에서 404 에러를 처리하는 방법을 보여줍니다. 특정 리소스가 존재하지 않을 때 발생하는 예외를 처리하여 클라이언트에게 적절한 404 응답을 반환합니다. 이를 통해 클라이언트는 요청한 리소스가 존재하지 않음을 알 수 있습니다. - -이러한 방법들을 통해 \`AuthController\`에서 발생하는 404 에러를 진단하고 해결할 수 있습니다. -`] \ No newline at end of file diff --git a/src/components/organisms/RepoDetailContent/documentText.jsx b/src/components/organisms/RepoDetailContent/documentText.jsx deleted file mode 100644 index 87ba78f..0000000 --- a/src/components/organisms/RepoDetailContent/documentText.jsx +++ /dev/null @@ -1,907 +0,0 @@ -const documentText = []; - -documentText.push( - ` -# 시스템 아키텍처 문서 - -## 전체 구조 -\`\`\`mermaid -graph TD - A[Client] --> B[AuthController] - B --> C[AuthService] - C --> D[TokenManager] - C --> E[MemberService] - D --> F[Database] - E --> F -\`\`\` - -## 시스템 흐름 -\`\`\`mermaid -sequenceDiagram - Client->>AuthController: Request for URI - AuthController->>AuthService: generateUri(providerName) - AuthService->>OAuthProvider: getOAuthUriProvider(providerName) - AuthService->>Client: Return URI - - Client->>AuthController: Login Request - AuthController->>AuthService: generateTokenWithCode(code, providerName) - AuthService->>OAuthClient: getOAuthMember(code) - AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) - MemberService->>Database: Check if member exists - MemberService->>Database: Save new member if not exists - AuthService->>TokenManager: createMemberToken(memberId) - AuthService->>Client: Return MemberToken - - Client->>AuthController: Extend Login Request - AuthController->>AuthService: generateRenewalAccessToken(refreshToken) - AuthService->>TokenManager: generateRenewalAccessToken(refreshToken) - AuthService->>Client: Return RenewalAccessTokenResponse - - Client->>AuthController: Logout Request - AuthController->>AuthService: removeRefreshToken(logoutRequest) - AuthService->>TokenManager: removeRefreshToken(refreshToken) -\`\`\` - -## 주요 컴포넌트 설명 - -### AuthController -- 역할과 책임: 클라이언트의 요청을 처리하고, AuthService와 상호작용하여 인증 관련 작업을 수행합니다. -- 주요 메서드: - - \`generateUri\`: OAuth URI를 생성합니다. - - \`login\`: OAuth 로그인 요청을 처리합니다. - - \`extendLogin\`: Refresh Token을 사용하여 Access Token을 갱신합니다. - - \`logout\`: 로그아웃 요청을 처리합니다. - -### AuthService -- 역할과 책임: 비즈니스 로직을 처리하며, OAuth 인증 및 토큰 관리를 담당합니다. -- 주요 메서드: - - \`generateTokenWithCode\`: OAuth 코드를 사용하여 토큰을 생성합니다. - - \`generateRenewalAccessToken\`: Refresh Token을 사용하여 Access Token을 갱신합니다. - - \`removeRefreshToken\`: 로그아웃 시 Refresh Token을 제거합니다. - - \`extractMemberId\`: Access Token에서 멤버 ID를 추출합니다. - -### TokenManager -- 역할과 책임: 토큰 생성 및 검증을 담당합니다. -- 주요 메서드: - - \`createMemberToken\`: 멤버 ID를 기반으로 새로운 MemberToken을 생성합니다. - - \`generateRenewalAccessToken\`: Refresh Token을 사용하여 새로운 Access Token을 생성합니다. - - \`removeRefreshToken\`: Refresh Token을 제거합니다. - -### MemberService -- 역할과 책임: 멤버 관련 데이터 접근을 담당합니다. -- 주요 메서드: - - \`existsByEmail\`: 이메일로 멤버 존재 여부를 확인합니다. - - \`save\`: 새로운 멤버를 저장합니다. - - \`findByEmail\`: 이메일로 멤버를 조회합니다. - -## API 엔드포인트 - -### Generate OAuth URI -**GET** \`/api/auth/{oAuthProvider}/link\` - -#### 설명 -OAuth 제공자에 대한 URI를 생성합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|--------------|--------|-----------|--------------------------| -| oAuthProvider| string | Required | OAuth 제공자 이름 | - -#### 응답 -##### Success Response -- Status: 200 OK -\`\`\`json -{ - "uri": "https://example.com/oauth/authorize" -} -\`\`\` - -### Login -**POST** \`/api/auth/{oAuthProvider}/login\` - -#### 설명 -OAuth 로그인을 수행하고 Access Token을 생성합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|--------------|--------|-----------|--------------------------| -| oAuthProvider| string | Required | OAuth 제공자 이름 | - -##### Request Body -\`\`\`json -{ - "code": "authorization_code" -} -\`\`\` - -#### 응답 -##### Success Response -- Status: 201 Created -\`\`\`json -{ - "accessToken": "generated_access_token" -} -\`\`\` - -### Extend Login -**POST** \`/api/auth/extend/login\` - -#### 설명 -Refresh Token을 사용하여 Access Token을 갱신합니다. - -#### 요청 -##### Headers -| 이름 | 필수 여부 | 설명 | -|--------------|-----------|--------------------------| -| Cookie | Required | Refresh Token이 포함된 쿠키 | - -#### 응답 -##### Success Response -- Status: 201 Created -\`\`\`json -{ - "accessToken": "new_generated_access_token" -} -\`\`\` - -### Logout -**DELETE** \`/api/auth/logout\` - -#### 설명 -로그아웃 요청을 처리하고 Refresh Token을 제거합니다. - -#### 요청 -##### Headers -| 이름 | 필수 여부 | 설명 | -|--------------|-----------|--------------------------| -| Cookie | Required | Refresh Token이 포함된 쿠키 | - -#### 응답 -##### Success Response -- Status: 204 No Content -\`\`\`json -{} -\`\`\` - -## 가능한 모든 응답 상태 코드 -- 200 OK: 요청이 성공적으로 처리됨 -- 201 Created: 리소스가 성공적으로 생성됨 -- 204 No Content: 요청이 성공적으로 처리되었으나 반환할 내용이 없음 -- 400 Bad Request: 잘못된 요청 -- 401 Unauthorized: 인증 실패 -- 404 Not Found: 요청한 리소스를 찾을 수 없음 -- 500 Internal Server Error: 서버 오류 - -## 인증 및 권한 -- 모든 API 엔드포인트는 적절한 인증이 필요합니다. 특히, 로그인 및 로그아웃 요청은 유효한 Refresh Token이 필요합니다. - -` -) - -documentText.push( - ` - # 시스템 아키텍처 - -## 전체 구조 -\`\`\`mermaid -graph TD - A[Client] --> B[KeywordController] - B --> C[KeywordService] - C --> D[KeywordRepository] - C --> E[TripRepository] - C --> F[TripKeywordRepository] - D --> G[Database] - E --> G - F --> G -\`\`\` - -## 시스템 흐름 -\`\`\`mermaid -sequenceDiagram - Client->>KeywordController: Request - KeywordController->>KeywordService: Process - KeywordService->>KeywordRepository: Data Access -\`\`\` -# 시스템 아키텍처 문서 - -## 전체 구조 -\`\`\`mermaid -graph TD - A[Client] --> B[AuthController] - B --> C[AuthService] - C --> D[TokenManager] - C --> E[MemberService] - D --> F[Database] - E --> F -\`\`\` - -## 시스템 흐름 -\`\`\`mermaid -sequenceDiagram - Client->>AuthController: Request for URI - AuthController->>AuthService: generateUri(providerName) - AuthService->>OAuthProvider: getOAuthUriProvider(providerName) - AuthService->>Client: Return URI - - Client->>AuthController: Login Request - AuthController->>AuthService: generateTokenWithCode(code, providerName) - AuthService->>OAuthClient: getOAuthMember(code) - AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) - MemberService->>Database: Check if member exists - MemberService->>Database: Save new member if not exists - AuthService->>TokenManager: createMemberToken(memberId) - AuthService->>Client: Return MemberToken - - Client->>AuthController: Extend Login Request - AuthController->>AuthService: generateRenewalAccessToken(refreshToken) - AuthService->>TokenManager: generateRenewalAccessToken(refreshToken) - AuthService->>Client: Return RenewalAccessTokenResponse - - Client->>AuthController: Logout Request - AuthController->>AuthService: removeRefreshToken(logoutRequest) - AuthService->>TokenManager: removeRefreshToken(refreshToken) -\`\`\` - -## 주요 컴포넌트 설명 - -### AuthController -- 역할과 책임: 클라이언트의 요청을 처리하고, AuthService와 상호작용하여 인증 관련 작업을 수행합니다. -- 주요 메서드: - - \`generateUri\`: OAuth URI를 생성합니다. - - \`login\`: OAuth 로그인 요청을 처리합니다. - - \`extendLogin\`: Refresh Token을 사용하여 Access Token을 갱신합니다. - - \`logout\`: 로그아웃 요청을 처리합니다. - -### AuthService -- 역할과 책임: 비즈니스 로직을 처리하며, OAuth 인증 및 토큰 관리를 담당합니다. -- 주요 메서드: - - \`generateTokenWithCode\`: OAuth 코드를 사용하여 토큰을 생성합니다. - - \`generateRenewalAccessToken\`: Refresh Token을 사용하여 Access Token을 갱신합니다. - - \`removeRefreshToken\`: 로그아웃 시 Refresh Token을 제거합니다. - - \`extractMemberId\`: Access Token에서 멤버 ID를 추출합니다. - -### TokenManager -- 역할과 책임: 토큰 생성 및 검증을 담당합니다. -- 주요 메서드: - - \`createMemberToken\`: 멤버 ID를 기반으로 새로운 MemberToken을 생성합니다. - - \`generateRenewalAccessToken\`: Refresh Token을 사용하여 새로운 Access Token을 생성합니다. - - \`removeRefreshToken\`: Refresh Token을 제거합니다. - -### MemberService -- 역할과 책임: 멤버 관련 데이터 접근을 담당합니다. -- 주요 메서드: - - \`existsByEmail\`: 이메일로 멤버 존재 여부를 확인합니다. - - \`save\`: 새로운 멤버를 저장합니다. - - \`findByEmail\`: 이메일로 멤버를 조회합니다. - -## API 엔드포인트 - -### Generate OAuth URI -**GET** \`/api/auth/{oAuthProvider}/link\` - -#### 설명 -OAuth 제공자에 대한 URI를 생성합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|--------------|--------|-----------|--------------------------| -| oAuthProvider| string | Required | OAuth 제공자 이름 | - -#### 응답 -##### Success Response -- Status: 200 OK -\`\`\`json -{ - "uri": "https://example.com/oauth/authorize" -} -\`\`\` - -### Login -**POST** \`/api/auth/{oAuthProvider}/login\` - -#### 설명 -OAuth 로그인을 수행하고 Access Token을 생성합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|--------------|--------|-----------|--------------------------| -| oAuthProvider| string | Required | OAuth 제공자 이름 | - -##### Request Body -\`\`\`json -{ - "code": "authorization_code" -} -\`\`\` - -#### 응답 -##### Success Response -- Status: 201 Created -\`\`\`json -{ - "accessToken": "generated_access_token" -} -\`\`\` - -### Extend Login -**POST** \`/api/auth/extend/login\` - -#### 설명 -Refresh Token을 사용하여 Access Token을 갱신합니다. - -#### 요청 -##### Headers -| 이름 | 필수 여부 | 설명 | -|--------------|-----------|--------------------------| -| Cookie | Required | Refresh Token이 포함된 쿠키 | - -#### 응답 -##### Success Response -- Status: 201 Created -\`\`\`json -{ - "accessToken": "new_generated_access_token" -} -\`\`\` - -### Logout -**DELETE** \`/api/auth/logout\` - -#### 설명 -로그아웃 요청을 처리하고 Refresh Token을 제거합니다. - -#### 요청 -##### Headers -| 이름 | 필수 여부 | 설명 | -|--------------|-----------|--------------------------| -| Cookie | Required | Refresh Token이 포함된 쿠키 | - -#### 응답 -##### Success Response -- Status: 204 No Content -\`\`\`json -{} -\`\`\` - -## 가능한 모든 응답 상태 코드 -- 200 OK: 요청이 성공적으로 처리됨 -- 201 Created: 리소스가 성공적으로 생성됨 -- 204 No Content: 요청이 성공적으로 처리되었으나 반환할 내용이 없음 -- 400 Bad Request: 잘못된 요청 -- 401 Unauthorized: 인증 실패 -- 404 Not Found: 요청한 리소스를 찾을 수 없음 -- 500 Internal Server Error: 서버 오류 - -## 인증 및 권한 -- 모든 API 엔드포인트는 적절한 인증이 필요합니다. 특히, 로그인 및 로그아웃 요청은 유효한 Refresh Token이 필요합니다. - KeywordService->>TripRepository: Data Access - KeywordService->>TripKeywordRepository: Data Access - KeywordRepository->>Database: Query - TripRepository->>Database: Query - TripKeywordRepository->>Database: Query -\`\`\` - -## 주요 컴포넌트 설명 -### KeywordController -- 역할과 책임: API 요청을 처리하고, 서비스 계층과의 상호작용을 통해 클라이언트에게 응답을 반환합니다. -- 주요 컨트롤러 목록: - - \`recommendTripsByKeywords\` - - \`findAllKeywords\` - - \`createKeyword\` - - \`createTripKeyword\` - - \`findTripsByRandomKeyword\` -- 공통 처리 로직: 인증을 위한 \`@Authentication\` 어노테이션을 사용하여 요청의 유효성을 검사합니다. - -### KeywordService -- 비즈니스 로직 구조: 키워드 관련 비즈니스 로직을 처리하며, 데이터베이스와의 상호작용을 관리합니다. -- 주요 서비스 목록: - - \`findAllKeywords\` - - \`createKeyword\` - - \`findRecommendTripsByKeywords\` - - \`findRecommendTripsByRandomKeyword\` -- 트랜잭션 경계: \`@Transactional\` 어노테이션을 사용하여 데이터베이스 트랜잭션을 관리합니다. - -### DTO (Data Transfer Object) -- \`Accessor\`: 사용자 인증 정보를 담고 있는 DTO입니다. -- \`FindAllKeywordResponses\`: 모든 키워드의 응답을 담고 있는 DTO입니다. -- \`FindTripsWithRandomKeywordResponse\`: 랜덤 키워드에 따른 여행 정보를 담고 있는 DTO입니다. -- \`KeywordCreateRequest\`: 키워드 생성 요청을 담고 있는 DTO입니다. -- \`TripsByKeyWordsRequest\`: 키워드 ID 목록을 담고 있는 DTO입니다. -- \`TripKeywordCreateRequest\`: 여행과 키워드의 관계를 생성하기 위한 요청을 담고 있는 DTO입니다. -- \`FindTripsResponse\`: 여행 정보를 담고 있는 응답 DTO입니다. - -## API 엔드포인트 - -### 추천 여행 키워드 -**POST** \`/api/keyword/trip/recommend\` - -#### 설명 -주어진 키워드에 따라 추천 여행을 반환합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|------|------|-----------|------| -| accessor | Accessor | Required | 인증 정보 | - -##### Headers -| 이름 | 필수 여부 | 설명 | -|------|-----------|------| -| Authorization | Required | Bearer {token} | - -##### Request Body -\`\`\`json -{ - "keywordIds": [1, 2, 3] -} -\`\`\` - -#### 응답 -##### Success Response -- Status: 200 OK -\`\`\`json -{ - "findTripResponses": [ - { - "trip": { - "id": 1, - "name": "Trip to Paris" - }, - "keywords": ["romantic", "city"] - } - ] -} -\`\`\` - -##### Error Response -- Status: 400 Bad Request -\`\`\`json -{ - "error": "일부 키워드가 존재하지 않습니다." -} -\`\`\` - -### 모든 키워드 조회 -**GET** \`/api/keyword\` - -#### 설명 -모든 키워드를 조회합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|------|------|-----------|------| -| accessor | Accessor | Required | 인증 정보 | - -##### Headers -| 이름 | 필수 여부 | 설명 | -|------|-----------|------| -| Authorization | Required | Bearer {token} | - -#### 응답 -##### Success Response -- Status: 200 OK -\`\`\`json -{ - "findAllKeywordResponses": [ - { - "keyword": "travel" - } - ] -} -\`\`\` - -### 키워드 생성 -**POST** \`/api/keyword\` - -#### 설명 -새로운 키워드를 생성합니다. - -#### 요청 -##### Request Body -\`\`\`json -{ - "keyword": "adventure" -} -\`\`\` - -#### 응답 -##### Success Response -- Status: 204 No Content - -### 여행 키워드 생성 -**POST** \`/api/keyword/trip\` - -#### 설명 -여행과 키워드의 관계를 생성합니다. - -#### 요청 -##### Request Body -\`\`\`json -{ - "tripId": 1, - "keywordId": 2 -} -\`\`\` - -#### 응답 -##### Success Response -- Status: 204 No Content - -### 랜덤 키워드에 따른 여행 조회 -**GET** \`/api/keyword/random/trip\` - -#### 설명 -랜덤 키워드에 따른 추천 여행을 조회합니다. - -#### 요청 -##### Parameters -| 이름 | 타입 | 필수 여부 | 설명 | -|------|------|-----------|------| -| accessor | Accessor | Required | 인증 정보 | - -##### Headers -| 이름 | 필수 여부 | 설명 | -|------|-----------|------| -| Authorization | Required | Bearer {token} | - -#### 응답 -##### Success Response -- Status: 200 OK -\`\`\`json -{ - "keywordName": "beach", - "findTripResponses": [ - { - "trip": { - "id": 1, - "name": "Beach Vacation" - }, - "keywords": ["sun", "sea"] - } - ] -} -\`\`\` -`) - - -documentText.push( - ` -# Controller Files Summary - -## MemberLiveInformationController.md -## 시스템 아키텍처 요약 - -### 1. 컴포넌트 개요 -- **주요 책임 및 목적**: - - 클라이언트 요청 처리, 비즈니스 로직 수행, 데이터베이스와의 상호작용. -- **주요 기능 및 능력**: - - 클라이언트 요청에 대한 API 제공, 데이터 저장 및 업데이트, CRUD 작업 수행. -- **핵심 패턴 및 접근법**: - - MVC 패턴을 기반으로 한 구조, 트랜잭션 관리 및 인증 처리. - -### 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 처리하고 서비스 레이어와 상호작용. - - **Service Layer**: 비즈니스 로직을 구현하고 트랜잭션 경계를 관리. - - **Repository Layer**: 데이터베이스와의 CRUD 작업을 담당. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 순으로 요청 흐름이 진행됨. - -- **핵심 구현 패턴**: - - \`@Transactional\` 어노테이션을 통한 트랜잭션 관리. - - \`@Authentication\` 어노테이션을 통한 인증 처리. - -- **중요한 흐름 및 프로세스**: - - 클라이언트가 요청을 보내면, 컨트롤러가 이를 처리하고 서비스 레이어를 호출하여 비즈니스 로직을 수행. - - 서비스는 리포지토리를 통해 데이터베이스와 상호작용하여 필요한 정보를 조회하거나 업데이트. - -## AuthController.md -# 시스템 아키텍처 요약 - -## 1. 컴포넌트 개요 -- **AuthController** - - 클라이언트 요청 처리 및 인증 관련 작업 수행 - - 주요 메서드: URI 생성, 로그인 처리, Access Token 갱신, 로그아웃 처리 - -- **AuthService** - - 비즈니스 로직 처리 및 OAuth 인증, 토큰 관리 담당 - - 주요 메서드: OAuth 코드로 토큰 생성, Refresh Token으로 Access Token 갱신, 로그아웃 시 Refresh Token 제거 - -- **TokenManager** - - 토큰 생성 및 검증 담당 - - 주요 메서드: 멤버 ID 기반 MemberToken 생성, Refresh Token으로 Access Token 생성, Refresh Token 제거 - -- **MemberService** - - 멤버 관련 데이터 접근 담당 - - 주요 메서드: 이메일로 멤버 존재 여부 확인, 새로운 멤버 저장, 이메일로 멤버 조회 - -## 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할** - - AuthController: 클라이언트의 요청을 받아 AuthService와 상호작용 - - AuthService: 비즈니스 로직을 처리하고, TokenManager 및 MemberService와 연계 - - TokenManager: 토큰 생성 및 검증을 전담 - - MemberService: 데이터베이스와 상호작용하여 멤버 정보를 관리 - -- **컴포넌트 상호작용 및 의존성** - - 클라이언트가 AuthController에 요청을 보내면, AuthController는 AuthService를 호출하여 필요한 작업을 수행 - - AuthService는 TokenManager와 MemberService를 호출하여 토큰 생성 및 멤버 정보를 처리 - -- **주요 구현 패턴** - - MVC 패턴: AuthController는 View와 Controller 역할을 수행 - - 서비스 패턴: AuthService와 MemberService는 비즈니스 로직을 분리하여 관리 - -- **핵심 흐름 및 프로세스** - - 클라이언트가 URI 요청 → AuthController가 AuthService에 URI 생성 요청 → AuthService가 OAuth 제공자로부터 URI를 받아 클라이언트에 반환 - - 클라이언트가 로그인 요청 → AuthController가 AuthService에 로그인 요청 → AuthService가 MemberService와 TokenManager를 통해 멤버 정보 확인 및 토큰 생성 - - 클라이언트가 Access Token 갱신 요청 → AuthController가 AuthService에 갱신 요청 → AuthService가 TokenManager를 통해 새로운 Access Token 생성 - - 클라이언트가 로그아웃 요청 → AuthController가 AuthService에 로그아웃 요청 → AuthService가 TokenManager를 통해 Refresh Token 제거 - -이 요약은 시스템 아키텍처의 주요 구성 요소와 그 상호작용을 간결하게 정리한 것입니다. - -## MemberController.md -# 시스템 아키텍처 요약 - -## 1. 컴포넌트 개요 -- **주요 책임 및 목적**: 클라이언트 요청을 처리하고, 비즈니스 로직을 수행하며, 데이터베이스와 상호작용하여 정보를 저장 및 조회합니다. -- **주요 기능 및 역량**: - - 클라이언트 요청에 대한 응답 처리 - - 비즈니스 로직 구현 - - 데이터베이스 접근 및 쿼리 실행 -- **핵심 패턴 및 접근 방식**: - - MVC 패턴을 기반으로 한 구조 - - RESTful API 설계 - -## 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 수신하고, 적절한 서비스로 전달하여 응답을 반환합니다. - - **Service Layer**: 비즈니스 로직을 처리하며, 데이터베이스 작업을 트랜잭션으로 관리합니다. - - **Repository Layer**: 데이터베이스와의 직접적인 상호작용을 담당합니다. - - **Database**: 모든 데이터 저장소 역할을 수행합니다. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트는 Controller에 요청을 보내고, Controller는 Service에 요청을 전달합니다. - - Service는 Repository를 통해 Database에 접근하여 데이터를 처리합니다. - -- **주요 구현 패턴**: - - RESTful API 설계 원칙을 따름 - - 트랜잭션 관리 및 오류 처리 전략 적용 - -- **핵심 흐름 및 프로세스**: - - 클라이언트 요청 → Controller가 요청 수신 → Service가 비즈니스 로직 처리 → Repository가 데이터베이스 쿼리 실행 → 응답 반환 - -## 3. API 문서 요약 -- **API 엔드포인트**: - - 회원 정보 조회, 프로필 등록, 닉네임 중복 확인, 프로필 업데이트, 권한 및 프로필 이미지 조회 기능 제공. - -- **공통 요청/응답 형식**: - - JSON 형식 사용 - - 모든 API는 인증이 필요하며, Bearer 토큰을 포함해야 함. - -- **주요 응답 상태 코드**: - - 200 OK: 성공적으로 처리됨 - - 204 No Content: 처리되었으나 반환할 데이터 없음 - - 400 Bad Request: 잘못된 요청 - - 404 Not Found: 리소스 없음 - - 409 Conflict: 요청 충돌 - -이 요약은 시스템 아키텍처의 주요 구성 요소와 흐름을 간결하게 정리한 것입니다. - -## TripController.md -## 시스템 아키텍처 요약 - -### 1. 컴포넌트 개요 -- **주요 책임 및 목적**: 클라이언트 요청을 처리하고 비즈니스 로직을 수행하며 데이터베이스와 상호작용하여 정보를 관리합니다. -- **주요 기능 및 능력**: - - 클라이언트 요청에 대한 응답 처리 - - 비즈니스 로직 수행 및 데이터 접근 - - API를 통한 여행 정보 관리 -- **핵심 패턴 및 접근 방식**: - - MVC 패턴을 기반으로 한 구조 - - RESTful API 설계 - -### 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 수신하고 응답을 반환하는 역할. - - **Service Layer**: 비즈니스 로직을 처리하고 데이터 접근을 위한 레포지토리와 상호작용. - - **Repository Layer**: 데이터베이스와의 직접적인 상호작용을 담당. - - **Database**: 데이터 저장소. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트가 컨트롤러에 요청을 보내고, 컨트롤러는 서비스 레이어를 호출하여 비즈니스 로직을 처리한 후, 레포지토리를 통해 데이터베이스에 접근. - -- **핵심 구현 패턴**: - - RESTful API 설계 원칙을 따름. - - 트랜잭션 관리 및 데이터 일관성 유지. - -- **중요 흐름 및 프로세스**: - - 클라이언트 요청 → 컨트롤러 처리 → 서비스 로직 실행 → 데이터베이스 쿼리 → 응답 반환. - -이 요약은 시스템 아키텍처의 주요 구성 요소와 그 상호작용을 간결하게 설명합니다. - -## KeywordController.md -## 시스템 아키텍처 요약 - -### 1. 컴포넌트 개요 -- **주요 책임 및 목적**: 클라이언트의 요청을 처리하고, 키워드 기반의 여행 추천 서비스를 제공. -- **주요 기능 및 능력**: - - 키워드 생성 및 조회 - - 키워드에 따른 여행 추천 - - 랜덤 키워드에 따른 여행 조회 -- **핵심 패턴 및 접근법**: - - RESTful API 설계 - - 서비스 계층을 통한 비즈니스 로직 처리 - - DTO를 통한 데이터 전송 및 응답 관리 - -### 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **KeywordController**: API 요청을 처리하고 클라이언트에 응답. - - **KeywordService**: 비즈니스 로직을 처리하고 데이터베이스와 상호작용. - - **KeywordRepository, TripRepository, TripKeywordRepository**: 데이터베이스 접근을 담당. -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트가 요청을 보내면, \`KeywordController\`가 이를 받아 \`KeywordService\`에 전달. - - \`KeywordService\`는 필요한 데이터를 위해 여러 리포지토리에 접근. -- **핵심 구현 패턴**: - - 트랜잭션 관리: \`@Transactional\` 어노테이션 사용. - - 인증 처리: \`@Authentication\` 어노테이션을 통한 요청 유효성 검사. -- **중요 흐름 및 프로세스**: - - 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 쿼리 → 응답 반환. - -## PlannerController.md -## 시스템 아키텍처 요약 - -### 1. 컴포넌트 개요 -- **주요 책임 및 목적**: 클라이언트의 요청을 처리하고 비즈니스 로직을 수행하여 데이터베이스와 상호작용하는 구조입니다. -- **주요 기능 및 역량**: - - 클라이언트 요청을 처리하는 컨트롤러 계층 - - 비즈니스 로직을 처리하는 서비스 계층 - - 데이터 접근을 담당하는 리포지토리 계층 - - 데이터 저장을 위한 데이터베이스 -- **핵심 패턴 및 접근법**: - - MVC 패턴을 기반으로 한 구조 - - 트랜잭션 관리에 \`@Transactional\` 어노테이션 사용 - -### 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 받아 서비스 계층과 상호작용 - - **Service Layer**: 비즈니스 로직을 처리하고 데이터 접근 요청을 리포지토리로 전달 - - **Repository Layer**: 데이터베이스와의 상호작용을 통해 데이터 쿼리 수행 - - **Database**: 실제 데이터 저장소 -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 순으로 요청 흐름 -- **주요 구현 패턴**: - - RESTful API 설계 - - JSON 형식의 요청 및 응답 처리 -- **핵심 흐름 및 프로세스**: - - 클라이언트가 API 요청 → 컨트롤러가 요청 처리 → 서비스가 비즈니스 로직 수행 → 리포지토리가 데이터베이스 쿼리 수행 → 결과 반환 - -### 3. API 문서 -- **API 엔드포인트 및 기능**: - - 최근 여행 일정 조회, 이름순 여행 일정 조회, 날짜순 여행 일정 조회, 여행 일정 업데이트, 여행 일정 삭제 -- **공통 요청/응답 형식**: JSON 형식 사용 -- **응답 상태 코드**: - - 200 OK: 요청 성공 - - 204 No Content: 요청 성공, 반환 데이터 없음 - - 400 Bad Request: 잘못된 요청 - - 404 Not Found: 요청한 리소스 없음 - - 401 Unauthorized: 인증 필요 - -이 요약은 시스템 아키텍처의 주요 구성 요소와 흐름을 간결하게 정리한 것입니다. - -## LiveInformationController.md -# 시스템 아키텍처 요약 - -## 1. 컴포넌트 개요 -- **주요 책임 및 목적**: - - 클라이언트 요청 처리 및 비즈니스 로직 실행. - - 데이터베이스와의 상호작용을 통한 CRUD 작업 수행. - - 비즈니스 도메인 모델 정의 및 데이터 구조 설정. - -- **주요 기능 및 역량**: - - API를 통한 생활정보 CRUD 기능 제공. - - 트랜잭션 관리 및 예외 처리. - - 인증 및 권한 관리. - -- **핵심 패턴 및 접근법**: - - MVC 패턴을 기반으로 한 구조. - - 서비스 레이어에서 비즈니스 로직 처리. - - 리포지토리 패턴을 통한 데이터 접근. - -## 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 처리하고 서비스 레이어와 상호작용. - - **Service Layer**: 비즈니스 로직을 처리하고 리포지토리 레이어 호출. - - **Repository Layer**: 데이터베이스와의 CRUD 작업 수행. - - **Domain Layer**: 비즈니스 도메인 모델 정의. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스. - - 각 레이어는 명확한 책임을 가지고 있으며, 상위 레이어가 하위 레이어를 호출. - -- **핵심 구현 패턴**: - - \`@Transactional\` 어노테이션을 통한 트랜잭션 관리. - - JSON 형식의 요청 및 응답 처리. - -- **중요 흐름 및 프로세스**: - - 클라이언트가 API 요청을 보내면, 컨트롤러가 요청을 처리하고 서비스 레이어로 전달. - - 서비스 레이어가 비즈니스 로직을 수행하고, 필요한 경우 리포지토리 레이어를 통해 데이터베이스와 상호작용. - - 최종적으로 결과를 클라이언트에게 응답. - -## RecommendTripController.md -## 시스템 아키텍처 요약 - -### 1. 컴포넌트 개요 -- **주요 책임 및 목적**: - - 클라이언트 요청을 처리하고 비즈니스 로직을 실행하여 데이터베이스와 상호작용합니다. -- **주요 기능 및 능력**: - - 추천 여행지 생성 및 조회 기능 제공. - - 인증 및 트랜잭션 관리. -- **핵심 패턴 및 접근 방식**: - - MVC 패턴을 기반으로 한 구조. - - RESTful API 설계. - -### 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 처리하고 서비스 계층과 상호작용. - - **Service Layer**: 비즈니스 로직을 처리하고 데이터 일관성을 보장. - - **Repository Layer**: 데이터베이스와의 상호작용을 담당. - - **Database**: 데이터의 영속성을 보장. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트가 요청을 보내면 컨트롤러가 이를 처리하고 서비스에 전달. - - 서비스는 레포지토리를 통해 데이터베이스와 상호작용하여 결과를 반환. - -- **주요 구현 패턴**: - - \`@Transactional\` 어노테이션을 통한 트랜잭션 관리. - - \`@Authentication\` 어노테이션을 통한 인증 처리. - -- **핵심 흐름 및 프로세스**: - - 클라이언트 요청 → 컨트롤러 → 서비스 → 레포지토리 → 데이터베이스 → 응답 반환. - -## TripScheduleController.md -# 시스템 아키텍처 요약 - -## 1. 컴포넌트 개요 -- **주요 책임 및 목적**: 클라이언트 요청을 처리하고 비즈니스 로직을 수행하여 데이터베이스와 상호작용합니다. -- **주요 기능 및 역량**: - - 클라이언트 요청을 처리하는 컨트롤러 레이어 - - 비즈니스 로직을 처리하는 서비스 레이어 - - 데이터 접근을 담당하는 리포지토리 레이어 - - 데이터 저장을 위한 데이터베이스 -- **핵심 패턴 및 접근법**: - - MVC 패턴을 기반으로 한 아키텍처 - - 트랜잭션 관리 및 인증 처리 - -## 2. 아키텍처 및 구현 -- **주요 컴포넌트 및 역할**: - - **Controller Layer**: 클라이언트 요청을 수신하고 서비스 레이어와 연결하여 비즈니스 로직을 수행. - - **Service Layer**: 비즈니스 로직을 처리하고 트랜잭션 경계를 관리. - - **Repository Layer**: 데이터베이스와의 상호작용을 담당. - - **Database**: 데이터 저장소. - -- **컴포넌트 상호작용 및 의존성**: - - 클라이언트가 컨트롤러에 요청을 보내고, 컨트롤러는 서비스에 요청을 전달하여 비즈니스 로직을 처리한 후, 리포지토리를 통해 데이터베이스에 접근. - -- **주요 구현 패턴**: - - RESTful API 설계 - - 트랜잭션 관리: \`@Transactional\` 어노테이션 사용 - - 인증 처리: \`@Authentication\` 어노테이션 사용 - -- **중요 흐름 및 프로세스**: - - 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 쿼리 - - API 요청 및 응답 처리 흐름이 명확하게 정의됨. - -이 문서는 시스템의 아키텍처와 주요 컴포넌트의 역할을 간결하게 설명하며, 각 레이어의 상호작용과 주요 패턴을 강조합니다. - - - ` -) - - - -export default documentText; \ No newline at end of file diff --git a/src/components/organisms/SearchContent/index.jsx b/src/components/organisms/SearchContent/index.jsx deleted file mode 100644 index 75642ff..0000000 --- a/src/components/organisms/SearchContent/index.jsx +++ /dev/null @@ -1,105 +0,0 @@ -import React from "react"; -import { Row, Col, ContentStyle } from "../../../layout/index.js" -import { Image, Typo } from "../../atoms/index.js" -import { GoodsForm } from "../../molecules/index.js" -import { Carousel } from 'antd'; -import mainImage from "../../../assets/images/mainImage2.png" -import mainImage_2 from "../../../assets/images/mainImage3.jpg" - -import Pagination from '@mui/material/Pagination'; - -const SearchContent = ({ - pagingClick, - pagingNum, - lists, - collectionProductOnClick, - totalPageNum, - keywordValue -}) => { - const onChange = (currentSlide) => { - console.log(currentSlide); - }; - const contentStyle = { - margin: 0, - // height: 'auto', - color: '#fff', - textAlign: 'center', - background: '#364d79', - height: "300px" - }; - return ( - <> - {/* //SECTION Carousel */} - -
    - -
    -
    - -
    -
    - {/* //!SECTION Carousel */} - - - - {/* //SECTION Title */} - - -

    '{keywordValue}' 

    검색 결과
    - - {/* //!SECTION Title */} - {/* //SECTION Content */} - - - - {/* //SECTION list */} - - - - { - (lists) ? - lists.map((lists) => { - return ( - collectionProductOnClick(lists.id)} - /> - ) - }) - : - null - } - { - lists.length === 0 ? - - 등록된 상품이 없습니다. - - : null - } - - - - {/* //!SECTION list */} - {/* //SECTION Pagination */} - - - - - - - {/* //!SECTION Pagination */} - -
    -
    - - ) -} - -export default SearchContent; \ No newline at end of file diff --git a/src/config/constants.js b/src/config/constants.js index fe31bb1..2c33cdd 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -1,9 +1,9 @@ // src/config/constants.js export const LAYOUT = { HEADER: { - TOTAL_DVH: 11.5, + TOTAL_DVH: 13.5, T_DVH: 3.5, - HEIGHT_DVH: 8, + HEIGHT_DVH: 10, PADDING: '0 20px', Z_INDEX: 100, diff --git a/src/constants/guides/AuthController.js b/src/constants/guides/AuthController.js new file mode 100644 index 0000000..0b7f7a1 --- /dev/null +++ b/src/constants/guides/AuthController.js @@ -0,0 +1,197 @@ +export const AuthController = ` + + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + + + + +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[AuthController] + B --> C[AuthService] + C --> D[TokenManager] + C --> E[MemberService] + D --> F[Database] + E --> F +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>AuthController: Request for OAuth URI + AuthController->>AuthService: generateUri(providerName) + AuthService->>OAuthProvider: getOAuthUriProvider(providerName) + OAuthProvider->>AuthService: Return OAuth URI + AuthService->>AuthController: Return OAuth URI + AuthController->>Client: Return OAuth URI + + Client->>AuthController: Login with code + AuthController->>AuthService: generateTokenWithCode(code, providerName) + AuthService->>OAuthClient: getOAuthMember(code) + OAuthClient->>AuthService: Return OAuthMember + AuthService->>MemberService: findOrCreateMember(oAuthMember, providerName) + MemberService->>AuthService: Return Member + AuthService->>TokenManager: createMemberToken(memberId) + TokenManager->>AuthService: Return MemberToken + AuthService->>AuthController: Return MemberToken + AuthController->>Client: Return Access Token and Set Refresh Token Cookie + + Client->>AuthController: Extend login with refresh token + AuthController->>AuthService: generateRenewalAccessToken(refreshToken) + AuthService->>TokenManager: generateRenewalAccessToken(refreshToken) + TokenManager->>AuthService: Return RenewalToken + AuthService->>AuthController: Return RenewalAccessTokenResponse + AuthController->>Client: Return New Access Token + + Client->>AuthController: Logout + AuthController->>AuthService: removeRefreshToken(refreshToken) + AuthService->>TokenManager: removeRefreshToken(refreshToken) + AuthController->>Client: No Content +\`\`\` + +## 주요 컴포넌트 설명 + +### AuthController +- **역할과 책임**: 클라이언트의 요청을 처리하고, AuthService와 상호작용하여 인증 관련 작업을 수행합니다. +- **주요 메서드**: + - \`generateUri\`: OAuth URI를 생성합니다. + - \`login\`: 로그인 요청을 처리하고, 액세스 토큰과 리프레시 토큰을 반환합니다. + - \`extendLogin\`: 리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + - \`logout\`: 로그아웃 요청을 처리합니다. + +### AuthService +- **역할과 책임**: 인증 관련 비즈니스 로직을 처리합니다. OAuth 인증, 토큰 생성 및 갱신, 회원 관리 등을 담당합니다. +- **주요 메서드**: + - \`generateTokenWithCode\`: OAuth 코드를 사용하여 토큰을 생성합니다. + - \`generateUri\`: OAuth URI를 생성합니다. + - \`generateRenewalAccessToken\`: 리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + - \`removeRefreshToken\`: 로그아웃 시 리프레시 토큰을 제거합니다. + +### TokenManager +- **역할과 책임**: 토큰 생성 및 관리 기능을 제공합니다. 액세스 토큰과 리프레시 토큰을 생성하고 검증합니다. + +### MemberService +- **역할과 책임**: 회원 관련 데이터 접근 및 관리 기능을 제공합니다. 회원의 존재 여부를 확인하고, 회원 정보를 저장합니다. + +## API 엔드포인트 + +### Generate OAuth URI +**GET** \`/api/auth/{oAuthProvider}/link\` + +#### 설명 +주어진 OAuth 공급자에 대한 인증 URI를 생성합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|--------------|--------|-----------|----------------------------| +| oAuthProvider| string | Required | OAuth 공급자의 이름 | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "oAuthUri": "https://example.com/oauth/authorize?client_id=xxx&redirect_uri=xxx" +} +\`\`\` + +### Login +**POST** \`/api/auth/{oAuthProvider}/login\` + +#### 설명 +주어진 OAuth 공급자와 함께 로그인합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|--------------|--------|-----------|----------------------------| +| oAuthProvider| string | Required | OAuth 공급자의 이름 | + +##### Request Body +\`\`\`json +{ + "code": "authorization_code" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 201 Created +\`\`\`json +{ + "accessToken": "access_token_value" +} +\`\`\` + +### Extend Login +**POST** \`/api/auth/extend/login\` + +#### 설명 +리프레시 토큰을 사용하여 액세스 토큰을 갱신합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "refreshToken": "refresh_token_value" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 201 Created +\`\`\`json +{ + "accessToken": "new_access_token_value" +} +\`\`\` + +### Logout +**DELETE** \`/api/auth/logout\` + +#### 설명 +사용자를 로그아웃합니다. + +#### 요청 +##### Headers +| 이름 | 필수 여부 | 설명 | +|----------------|-----------|----------------------------| +| Cookie | Required | 리프레시 토큰이 포함된 쿠키 | + +#### 응답 +##### Success Response +- Status: 204 No Content + +## SUMMARY + +### Component Overview +- **AuthController**: 클라이언트 요청을 처리하고 응답을 반환하는 역할. +- **AuthService**: 인증 및 회원 관리 비즈니스 로직을 처리. +- **TokenManager**: 토큰 생성 및 검증 기능 제공. +- **MemberService**: 회원 데이터 접근 및 관리. + +### Architecture & Implementation +- **구성 요소 상호 작용**: 클라이언트는 AuthController에 요청을 보내고, AuthController는 AuthService와 상호작용하여 비즈니스 로직을 처리합니다. AuthService는 TokenManager와 MemberService를 통해 토큰 관리 및 회원 정보를 처리합니다. +- **주요 흐름 및 프로세스**: 클라이언트의 요청은 AuthController를 통해 AuthService로 전달되고, 필요한 데이터는 TokenManager와 MemberService를 통해 처리됩니다. 최종적으로 응답은 클라이언트에게 반환됩니다. +`; diff --git a/src/constants/guides/KeywordController.js b/src/constants/guides/KeywordController.js new file mode 100644 index 0000000..4e835f2 --- /dev/null +++ b/src/constants/guides/KeywordController.js @@ -0,0 +1,201 @@ +export const KeywordController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[KeywordController] + B --> C[KeywordService] + C --> D[KeywordRepository] + C --> D2[TripKeywordRepository] + C --> D3[TripRepository] + C --> E[TripsByStatisticsFinder] + C --> E2[RandomKeywordGeneratable] + D --> F[Database] + D2 --> F + D3 --> F +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>KeywordController: Request + KeywordController->>KeywordService: Process + KeywordService->>KeywordRepository: Data Access + KeywordService->>TripKeywordRepository: Data Access + KeywordService->>TripRepository: Data Access + KeywordService->>TripsByStatisticsFinder: Process Statistics + KeywordService->>RandomKeywordGeneratable: Generate Random Keyword +\`\`\` + +## 주요 컴포넌트 설명 + +### KeywordController +- 역할과 책임: API 요청을 처리하고, 서비스 계층과의 상호작용을 통해 클라이언트에게 응답을 반환합니다. +- 주요 컨트롤러 목록: + - \`recommendTripsByKeywords\` + - \`findAllKeywords\` + - \`createKeyword\` + - \`createTripKeyword\` + - \`findTripsByRandomKeyword\` +- 공통 처리 로직: 모든 요청에 대해 인증을 수행합니다. + +### KeywordService +- 비즈니스 로직 구조: 키워드 관련 비즈니스 로직을 처리합니다. +- 주요 서비스 목록: + - \`findAllKeywords\` + - \`createKeyword\` + - \`findRecommendTripsByKeywords\` + - \`findRecommendTripsByRandomKeyword\` +- 트랜잭션 경계: 데이터베이스 변경이 필요한 메서드는 \`@Transactional\` 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 엔드포인트 + +### 추천 여행지 조회 +**POST** \`/api/keyword/trip/recommend\` + +#### 설명 +주어진 키워드에 따라 추천 여행지를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증 정보 | + +##### Request Body +\`\`\`json +{ + "keywordIds": [1, 2, 3] +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "Trip Name" + }, + "keywords": ["Keyword1", "Keyword2"] + } + ] +} +\`\`\` + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "일부 키워드가 존재하지 않습니다." +} +\`\`\` + +### 모든 키워드 조회 +**GET** \`/api/keyword\` + +#### 설명 +모든 키워드를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증 정보 | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findAllKeywordResponses": [ + { + "keyword": "Keyword1" + }, + { + "keyword": "Keyword2" + } + ] +} +\`\`\` + +### 키워드 생성 +**POST** \`/api/keyword\` + +#### 설명 +새로운 키워드를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "keyword": "New Keyword" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +### 여행지 키워드 생성 +**POST** \`/api/keyword/trip\` + +#### 설명 +여행지에 키워드를 추가합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "tripId": 1, + "keywordId": 2 +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +### 랜덤 키워드로 여행지 조회 +**GET** \`/api/keyword/random/trip\` + +#### 설명 +랜덤 키워드를 사용하여 추천 여행지를 조회합니다. + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "keywordName": "Random Keyword", + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "Trip Name" + }, + "keywords": ["Keyword1", "Keyword2"] + } + ] +} +\`\`\` + +## SUMMARY + +### Component Overview +- 주요 책임 및 목적: 키워드와 관련된 데이터의 생성, 조회 및 추천 기능을 제공합니다. +- 주요 기능 및 역량: 키워드 관리, 여행지 추천, 통계 기반 추천 기능을 포함합니다. + +### Architecture & Implementation +- 주요 구성 요소 및 역할: + - \`KeywordController\`: API 요청 처리 + - \`KeywordService\`: 비즈니스 로직 처리 + - \`KeywordRepository\`, \`TripKeywordRepository\`, \`TripRepository\`: 데이터 접근 + - \`TripsByStatisticsFinder\`, \`RandomKeywordGeneratable\`: 통계 및 랜덤 키워드 생성 +- 구성 요소 상호 작용 및 종속성: 컨트롤러는 서비스에 의존하고, 서비스는 여러 리포지토리에 의존합니다. +- 주요 흐름 및 프로세스: 클라이언트 요청 -> 컨트롤러 -> 서비스 -> 리포지토리 -> 데이터베이스. +`; diff --git a/src/constants/guides/LiveInformationController.js b/src/constants/guides/LiveInformationController.js new file mode 100644 index 0000000..2f8ca39 --- /dev/null +++ b/src/constants/guides/LiveInformationController.js @@ -0,0 +1,171 @@ +export const LiveInformationController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[LiveInformationController] + B --> C[LiveInformationService] + C --> D[LiveInformationRepository] + C --> E[TripLiveInformationRepository] + C --> F[TripRepository] + D --> G[Database] + E --> G + F --> G +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>LiveInformationController: GET /api/live/info/all + LiveInformationController->>LiveInformationService: findAllLiveInformation() + LiveInformationService->>LiveInformationRepository: findAll() + LiveInformationRepository-->>LiveInformationService: List + LiveInformationService-->>LiveInformationController: FindAllLiveInformationResponse + LiveInformationController-->>Client: 200 OK + + Client->>LiveInformationController: POST /api/live/info + LiveInformationController->>LiveInformationService: createLiveInformation(request) + LiveInformationService->>LiveInformationRepository: save(liveInformation) + LiveInformationRepository-->>LiveInformationService: LiveInformation + LiveInformationService-->>LiveInformationController: void + LiveInformationController-->>Client: 204 No Content + + Client->>LiveInformationController: POST /api/live/info/trip/{tripId}/{liveInfoId} + LiveInformationController->>LiveInformationService: createTripLiveInformation(tripId, liveInfoId) + LiveInformationService->>TripRepository: findById(tripId) + TripRepository-->>LiveInformationService: Trip + LiveInformationService->>LiveInformationRepository: findById(liveInfoId) + LiveInformationRepository-->>LiveInformationService: LiveInformation + LiveInformationService->>TripLiveInformationRepository: save(new TripLiveInformation(liveInformation, trip)) + TripLiveInformationRepository-->>LiveInformationService: void + LiveInformationService-->>LiveInformationController: void + LiveInformationController-->>Client: 204 No Content +\`\`\` + +## 주요 컴포넌트 설명 + +### LiveInformationController +- 역할과 책임: 클라이언트의 요청을 처리하고, 서비스 계층과 상호작용하여 응답을 반환합니다. +- 주요 메서드: + - \`findAllLiveInformation()\`: 모든 생활 정보를 조회합니다. + - \`createLiveInformation()\`: 새로운 생활 정보를 생성합니다. + - \`createTripLiveInformation()\`: 여행지와 생활 정보를 연결합니다. + +### LiveInformationService +- 비즈니스 로직을 처리하며, 데이터베이스와의 상호작용을 관리합니다. +- 주요 메서드: + - \`findAllLiveInformation()\`: 모든 생활 정보를 조회하여 응답 객체를 생성합니다. + - \`createLiveInformation()\`: 새로운 생활 정보를 생성하고 저장합니다. + - \`createTripLiveInformation()\`: 여행지와 생활 정보를 연결하여 저장합니다. + - \`findByName()\`: 이름으로 생활 정보를 조회합니다. + +### LiveInformationRepository +- 데이터베이스와의 CRUD 작업을 수행합니다. + +### TripLiveInformationRepository +- 여행지와 생활 정보 간의 관계를 관리합니다. + +### TripRepository +- 여행지에 대한 CRUD 작업을 수행합니다. + +## API 엔드포인트 + +### Find All Live Information +**GET** \`/api/live/info/all\` + +#### 설명 +모든 생활 정보를 조회합니다. + +#### 요청 +- Headers: 없음 + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "liveInformationResponses": [ + { + "name": "생활정보1" + }, + { + "name": "생활정보2" + } + ] +} +\`\`\` + +##### Error Response +- Status: 500 Internal Server Error +\`\`\`json +{ + "error": "서버 오류 메시지" +} +\`\`\` + +### Create Live Information +**POST** \`/api/live/info\` + +#### 설명 +새로운 생활 정보를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "name": "새로운 생활정보" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "생활정보 이름은 공백일 수 없습니다." +} +\`\`\` + +### Create Trip Live Information +**POST** \`/api/live/info/trip/{tripId}/{liveInfoId}\` + +#### 설명 +여행지와 생활 정보를 연결합니다. + +#### 요청 +- Path Variables: + - \`tripId\`: 여행지 ID + - \`liveInfoId\`: 생활 정보 ID + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 여행지입니다." +} +\`\`\`json +{ + "error": "존재하지 않는 생활정보입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **LiveInformationController**: 클라이언트 요청을 처리하고 서비스 계층과 상호작용. +- **LiveInformationService**: 비즈니스 로직 처리 및 데이터베이스와의 상호작용. +- **Repositories**: 데이터베이스 CRUD 작업 수행. + +### Architecture & Implementation +- **구성 요소 상호 작용**: Controller는 Service를 호출하고, Service는 Repository를 통해 데이터베이스와 상호작용. +- **주요 흐름**: 클라이언트 요청 -> Controller -> Service -> Repository -> Database. + +`; diff --git a/src/constants/guides/MemberController.js b/src/constants/guides/MemberController.js new file mode 100644 index 0000000..e1fd698 --- /dev/null +++ b/src/constants/guides/MemberController.js @@ -0,0 +1,208 @@ +export const MemberController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[MemberController] + B --> C[MemberService] + C --> D[MemberRepository] + C --> E[LiveInformationRepository] + C --> F[MemberLiveInformationService] + C --> G[TripService] + C --> H[RecommendTripService] + D --> I[Database] + E --> I + F --> I + G --> I + H --> I +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>MemberController: Request + MemberController->>MemberService: Process + MemberService->>MemberRepository: Data Access + MemberService->>LiveInformationRepository: Data Access + MemberService->>MemberLiveInformationService: Data Access + MemberService->>TripService: Data Access + MemberService->>RecommendTripService: Data Access + MemberRepository->>Database: Query + LiveInformationRepository->>Database: Query + MemberLiveInformationService->>Database: Query + TripService->>Database: Query + RecommendTripService->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### MemberController +- 역할과 책임: 클라이언트의 요청을 처리하고, 적절한 서비스 메소드를 호출하여 응답을 반환합니다. +- 주요 메소드: + - \`getUserInfo()\`: 현재 사용자 정보를 조회합니다. + - \`signupProfile()\`: 프로필 정보를 등록합니다. + - \`signupLiveInfo()\`: 생활 정보를 등록합니다. + - \`signupInterestTrip()\`: 관심 여행지를 등록합니다. + - \`checkDuplicateNickname()\`: 닉네임 중복 여부를 확인합니다. + - \`updateMemberProfile()\`: 사용자 프로필을 업데이트합니다. + - \`findMemberAuthorityAndProfileImg()\`: 사용자 권한 및 프로필 이미지를 조회합니다. + +### MemberService +- 비즈니스 로직을 처리하며, 데이터베이스와의 상호작용을 관리합니다. +- 주요 메소드: + - \`findById()\`: ID로 회원을 조회합니다. + - \`signUpByProfile()\`: 프로필 정보를 등록합니다. + - \`signUpByLiveInfo()\`: 생활 정보를 등록합니다. + - \`signUpByInterestTrips()\`: 관심 여행지를 등록합니다. + - \`updateByProfile()\`: 프로필 정보를 업데이트합니다. + - \`checkIsAlreadyExistNickname()\`: 닉네임 중복 여부를 확인합니다. + - \`findMemberAuthorityAndProfileImg()\`: 사용자 권한 및 프로필 이미지를 조회합니다. + +### MemberRepository +- 회원 정보를 데이터베이스에 저장하고 조회하는 역할을 합니다. + +### LiveInformationRepository +- 생활 정보 데이터를 데이터베이스에 저장하고 조회하는 역할을 합니다. + +### MemberLiveInformationService +- 회원의 생활 정보를 관리하는 서비스입니다. + +### TripService +- 여행 관련 데이터를 관리하는 서비스입니다. + +### RecommendTripService +- 추천 여행지 관련 데이터를 관리하는 서비스입니다. + +## API 엔드포인트 + +### 사용자 정보 조회 +**GET** \`/api/member/me\` + +#### 설명 +현재 로그인한 사용자의 정보를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "id": 1, + "profileImageUrl": "http://example.com/image.jpg", + "nickname": "user123", + "birthday": "1990-01-01", + "genderType": "MALE" +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 프로필 등록 +**POST** \`/api/member/signup/profile\` + +#### 설명 +사용자의 프로필 정보를 등록합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "nickname": "user123", + "birthday": "1990-01-01", + "genderType": "MALE" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "중복되는 닉네임이 존재합니다." +} +\`\`\` + +### 닉네임 중복 확인 +**POST** \`/api/member/check/nickname\` + +#### 설명 +닉네임의 중복 여부를 확인합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| - | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "nickname": "user123" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "message": "사용 가능한 닉네임입니다." +} +\`\`\` + +##### Error Response +- Status: 409 Conflict +\`\`\`json +{ + "error": "중복되는 닉네임이 존재합니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **MemberController**: 클라이언트 요청을 처리하고, 서비스 메소드를 호출하여 응답을 반환합니다. +- **MemberService**: 비즈니스 로직을 처리하고, 데이터베이스와의 상호작용을 관리합니다. +- **Repositories**: 데이터베이스와의 CRUD 작업을 수행합니다. +- **DTOs**: 데이터 전송 객체로, 클라이언트와 서버 간의 데이터 전송을 담당합니다. + +### Architecture & Implementation +- **구성 요소 상호 작용**: 클라이언트는 \`MemberController\`에 요청을 보내고, \`MemberController\`는 \`MemberService\`를 호출하여 비즈니스 로직을 처리합니다. \`MemberService\`는 필요한 데이터를 \`MemberRepository\`, \`LiveInformationRepository\` 등에서 조회합니다. +- **주요 흐름 및 프로세스**: 클라이언트의 요청이 들어오면, 해당 요청에 맞는 메소드가 호출되고, 필요한 데이터가 처리된 후 응답이 반환됩니다. + +`; diff --git a/src/constants/guides/MemberLiveInformationController.js b/src/constants/guides/MemberLiveInformationController.js new file mode 100644 index 0000000..7aa238e --- /dev/null +++ b/src/constants/guides/MemberLiveInformationController.js @@ -0,0 +1,153 @@ +export const MemberLiveInformationController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와 상호작용하여 비즈니스 로직을 실행합니다. +- **주요 컨트롤러 목록**: + - \`MemberLiveInformationController\`: 회원의 생활 정보를 조회하고 업데이트합니다. +- **공통 처리 로직**: 인증 정보(\`Accessor\`)를 요청 매개변수로 받아 사용합니다. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 처리하고, 데이터베이스와의 상호작용을 관리합니다. +- **주요 서비스 목록**: + - \`MemberLiveInformationService\`: 회원의 생활 정보를 관리합니다. +- **트랜잭션 경계**: 데이터 업데이트 시 트랜잭션을 관리합니다. + +### Repository Layer +- **역할과 책임**: 데이터베이스와의 상호작용을 담당하며, CRUD 작업을 수행합니다. +- **주요 리포지토리 목록**: + - \`MemberLiveInformationRepository\` + - \`LiveInformationRepository\` + - \`MemberRepository\` + +### Database +- **역할과 책임**: 모든 데이터의 영속성을 보장합니다. + +## API 문서 + +### MemberLiveInformationController + +#### 개요 +- **컨트롤러 설명**: 회원의 생활 정보를 조회하고 업데이트하는 API를 제공합니다. +- **기본 URL 경로**: \`/api/live/info/member\` +- **공통 요청/응답 형식**: JSON 형식 + +#### API 엔드포인트 + +### 1. Find Member Live Information +**GET** \`/api/live/info/member\` + +#### 설명 +회원의 선택된 생활 정보를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|-----------|--------|-----------|---------------------| +| accessor | Accessor | Required | 인증된 회원 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|---------------|-----------|--------------------------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "liveInfoResponses": [ + { + "id": 1, + "name": "Live Info 1", + "selected": true + }, + { + "id": 2, + "name": "Live Info 2", + "selected": false + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 2. Update Member Live Information +**PUT** \`/api/live/info/member\` + +#### 설명 +회원의 생활 정보를 업데이트합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|-----------|--------|-----------|---------------------| +| accessor | Accessor | Required | 인증된 회원 정보 | + +##### Request Body +\`\`\`json +{ + "liveInfoIds": [1, 2, 3] +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "업데이트 할 생활정보는 비어있을 수 없습니다." +} +\`\`\` +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 클라이언트 요청을 처리하고, 비즈니스 로직을 실행하며, 데이터베이스와의 상호작용을 관리합니다. +- **주요 기능 및 역량**: 회원의 생활 정보를 조회하고 업데이트하는 기능을 제공합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`MemberLiveInformationController\`: 클라이언트 요청을 처리합니다. + - \`MemberLiveInformationService\`: 비즈니스 로직을 처리합니다. + - \`MemberLiveInformationRepository\`, \`LiveInformationRepository\`, \`MemberRepository\`: 데이터베이스와의 상호작용을 담당합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스. + +`; diff --git a/src/constants/guides/PlannerController.js b/src/constants/guides/PlannerController.js new file mode 100644 index 0000000..e13e22e --- /dev/null +++ b/src/constants/guides/PlannerController.js @@ -0,0 +1,210 @@ +export const PlannerController = ` +# 시스템 아키텍처 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와의 상호작용을 통해 비즈니스 로직을 수행합니다. +- **주요 컨트롤러 목록**: + - \`PlannerController\`: 여행 일정 관련 API를 제공합니다. +- **공통 처리 로직**: \`@Authentication\` 어노테이션을 통해 인증된 사용자의 정보를 가져옵니다. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 구현하며, 데이터베이스와의 상호작용을 위한 리포지토리 호출을 포함합니다. +- **주요 서비스 목록**: + - \`PlannerService\`: 여행 일정 관련 비즈니스 로직을 처리합니다. +- **트랜잭션 경계**: \`@Transactional\` 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 문서 + +# Planner API + +## 개요 +- **컨트롤러 설명**: 여행 일정 관련 API를 제공하는 컨트롤러입니다. +- **기본 URL 경로**: \`/api/planner\` +- **공통 요청/응답 형식**: JSON 형식의 요청 및 응답을 사용합니다. + +## API 엔드포인트 + +### 최근 여행 일정 조회 +**GET** \`/api/planner/recent\` + +#### 설명 +최근 여행 일정을 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "tripSchedules": [ + { + "id": 1, + "name": "여행 일정 1", + "startDate": "2023-10-01", + "endDate": "2023-10-05" + } + ] +} +\`\`\` + +##### Error Response +- Status: 401 Unauthorized +\`\`\`json +{ + "error": "인증이 필요합니다." +} +\`\`\` + +### 여행 일정 이름으로 조회 +**GET** \`/api/planner/name\` + +#### 설명 +이름으로 여행 일정을 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "tripSchedules": [ + { + "id": 2, + "name": "여행 일정 2", + "startDate": "2023-10-10", + "endDate": "2023-10-15" + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "여행 일정을 찾을 수 없습니다." +} +\`\`\` + +### 여행 일정 업데이트 +**PUT** \`/api/planner/schedule\` + +#### 설명 +여행 일정을 업데이트합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| 없음 | - | - | - | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "scheduleId": 1, + "scheduleName": "업데이트된 여행 일정", + "startDate": "2023-10-01", + "endDate": "2023-10-05" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "잘못된 요청입니다." +} +\`\`\` + +### 여행 일정 삭제 +**DELETE** \`/api/planner/schedule/{scheduleId}\` + +#### 설명 +여행 일정을 삭제합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| scheduleId | Long | Required | 삭제할 여행 일정의 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 여행 일정입니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 클라이언트의 요청을 처리하고, 비즈니스 로직을 수행하여 데이터베이스와 상호작용합니다. +- **주요 기능 및 역량**: 여행 일정의 생성, 조회, 업데이트, 삭제 기능을 제공합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`PlannerController\`: API 요청을 처리합니다. + - \`PlannerService\`: 비즈니스 로직을 구현합니다. + - \`TripScheduleRepository\`: 데이터베이스와의 상호작용을 담당합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하며, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 -> 컨트롤러 처리 -> 서비스 로직 실행 -> 데이터베이스 쿼리 -> 응답 반환. + +`; diff --git a/src/constants/guides/RecommendTripController.js b/src/constants/guides/RecommendTripController.js new file mode 100644 index 0000000..216924f --- /dev/null +++ b/src/constants/guides/RecommendTripController.js @@ -0,0 +1,152 @@ +export const RecommendTripController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 계층과의 상호작용을 통해 비즈니스 로직을 수행합니다. +- **주요 컨트롤러 목록**: + - \`RecommendTripController\`: 추천 여행지 생성 및 조회 기능을 제공합니다. +- **공통 처리 로직**: 인증 정보를 처리하기 위해 \`@Authentication\` 어노테이션을 사용합니다. + +### Service Layer +- **비즈니스 로직 구조**: 추천 여행지 생성 및 필터링 전략을 통해 여행지를 추천하는 비즈니스 로직을 포함합니다. +- **주요 서비스 목록**: + - \`RecommendTripService\`: 추천 여행지 저장 및 생성 기능을 제공합니다. +- **트랜잭션 경계**: \`@Transactional\` 어노테이션을 사용하여 데이터베이스 트랜잭션을 관리합니다. + +### Repository Layer +- **역할과 책임**: 데이터베이스와의 상호작용을 통해 데이터를 저장하고 조회합니다. +- **주요 리포지토리 목록**: + - \`RecommendTripRepository\`: 추천 여행지 관련 데이터 접근을 담당합니다. + - \`MemberRepository\`: 회원 관련 데이터 접근을 담당합니다. + - \`TripRepository\`: 여행지 관련 데이터 접근을 담당합니다. + - \`TripKeywordRepository\`: 여행지 키워드 관련 데이터 접근을 담당합니다. + +### Domain Layer +- **역할과 책임**: 비즈니스 도메인 모델을 정의하고, 필터링 전략 및 추천 로직을 구현합니다. +- **주요 도메인 클래스**: + - \`RecommendTrip\`: 추천 여행지 정보를 담고 있는 엔티티입니다. + - \`PreferredLocationsFilterInfo\`: 선호하는 위치에 대한 필터 정보를 담고 있습니다. + - \`TripFilterStrategy\`: 여행지 필터링 전략의 인터페이스입니다. + +## API 문서 + +### 추천 여행지 생성 API +**POST** \`/api/recommend\` + +#### 설명 +추천 여행지를 생성합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증된 사용자 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +##### Request Body +\`\`\`json +{ + "tripId": 1 +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "Null 일 수 없습니다." +} +\`\`\` +- Status: 404 Not Found +\`\`\`json +{ + "error": "존재하지 않는 회원입니다." +} +\`\`\` + +### 추천 여행지 조회 API +**GET** \`/api/recommend\` + +#### 설명 +사용자의 선호 위치에 따라 추천 여행지를 조회합니다. + +#### 요청 +##### Parameters +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| accessor | Accessor | Required | 인증된 사용자 정보 | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "여행지 이름" + }, + "keywords": ["키워드1", "키워드2"] + } + ] +} +\`\`\` + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "추천 여행지를 찾을 수 없습니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 사용자 인증을 통해 추천 여행지를 생성하고 조회하는 기능을 제공합니다. +- **주요 기능 및 역량**: 추천 여행지 생성, 사용자 선호 기반 여행지 필터링 및 조회 기능을 포함합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`RecommendTripController\`: 클라이언트 요청을 처리하고, 서비스 계층과 상호작용합니다. + - \`RecommendTripService\`: 비즈니스 로직을 처리합니다. + - \`TripFilterStrategyProvider\`: 필터링 전략을 제공하고 실행합니다. + - \`TripsWithKeywordProvider\`: 추천 여행지와 관련된 키워드를 조회합니다. +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 여러 리포지토리와 필터링 전략 제공자에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 → 컨트롤러 → 서비스 → 리포지토리 → 데이터베이스 쿼리. + +`; diff --git a/src/constants/guides/TripController.js b/src/constants/guides/TripController.js new file mode 100644 index 0000000..60cdaf6 --- /dev/null +++ b/src/constants/guides/TripController.js @@ -0,0 +1,192 @@ +export const TripController = ` +# 시스템 아키텍처 문서 + +## 전체 구조 +\`\`\`mermaid +graph TD + A[Client] --> B[Controller Layer] + B --> C[Service Layer] + C --> D[Repository Layer] + D --> E[Database] +\`\`\` + +## 시스템 흐름 +\`\`\`mermaid +sequenceDiagram + Client->>Controller: Request + Controller->>Service: Process + Service->>Repository: Data Access + Repository->>Database: Query +\`\`\` + +## 주요 컴포넌트 설명 + +### Controller Layer +- **역할과 책임**: 클라이언트의 요청을 처리하고, 서비스 레이어와 상호작용하여 비즈니스 로직을 실행합니다. +- **주요 컨트롤러 목록**: + - \`TripController\`: 여행 관련 API를 처리합니다. +- **공통 처리 로직**: 요청 매핑, 응답 생성 및 상태 코드 반환. + +### Service Layer +- **비즈니스 로직 구조**: 비즈니스 로직을 처리하며, 데이터 접근을 위한 리포지토리와 상호작용합니다. +- **주요 서비스 목록**: + - \`TripService\`: 여행 관련 비즈니스 로직을 처리합니다. +- **트랜잭션 경계**: @Transactional 어노테이션을 사용하여 트랜잭션을 관리합니다. + +## API 문서 + +### Trip API + +#### 개요 +- **컨트롤러 설명**: 여행 관련 API를 제공하는 컨트롤러입니다. +- **기본 URL 경로**: \`/api/trip\` +- **공통 요청/응답 형식**: JSON 형식의 요청 및 응답을 사용합니다. + +#### API 엔드포인트 + +### 1. Create Trip +**POST** \`/api/trip\` + +#### 설명 +여행지를 생성합니다. + +#### 요청 +##### Request Body +\`\`\`json +{ + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" +} +\`\`\` + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 400 Bad Request +\`\`\`json +{ + "error": "여행지 이름은 공백일 수 없습니다." +} +\`\`\` + +### 2. Find Top Trips +**GET** \`/api/trip/find/interested\` + +#### 설명 +방문 수에 따라 상위 30개의 여행지를 조회합니다. + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponses": [ + { + "trip": { + "id": 1, + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" + }, + "keywords": ["키워드1", "키워드2"] + } + ] +} +\`\`\` + +### 3. Find Trip with Similar Trips +**GET** \`/api/trip/find/{tripId}\` + +#### 설명 +특정 여행지와 유사한 여행지를 조회합니다. + +#### 요청 +##### Path Variables +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| tripId | long | Required | 여행지 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 200 OK +\`\`\`json +{ + "findTripResponse": { + "trip": { + "id": 1, + "name": "여행지 이름", + "placeName": "여행지 장소명", + "contentId": 123, + "description": "여행지 설명", + "tripImageUrl": "이미지 URL" + }, + "keywords": ["키워드1", "키워드2"] + }, + "similarTripResponses": { + "trips": [ + { + "id": 2, + "name": "유사 여행지 이름", + "placeName": "유사 여행지 장소명" + } + ] + } +} +\`\`\` + +### 4. Create Member Trip +**POST** \`/api/trip/member/{tripId}\` + +#### 설명 +특정 여행지를 회원의 여행 목록에 추가합니다. + +#### 요청 +##### Path Variables +| 이름 | 타입 | 필수 여부 | 설명 | +|------|------|-----------|------| +| tripId | long | Required | 여행지 ID | + +##### Headers +| 이름 | 필수 여부 | 설명 | +|------|-----------|------| +| Authorization | Required | Bearer {token} | + +#### 응답 +##### Success Response +- Status: 204 No Content + +##### Error Response +- Status: 404 Not Found +\`\`\`json +{ + "error": "여행지가 존재하지 않습니다." +} +\`\`\` + +## SUMMARY + +### Component Overview +- **주요 책임 및 목적**: 각 레이어는 명확한 책임을 가지고 있으며, 클라이언트의 요청을 처리하고 비즈니스 로직을 실행합니다. +- **주요 기능 및 역량**: 여행지 생성, 조회, 추천 기능을 포함합니다. + +### Architecture & Implementation +- **주요 구성 요소 및 역할**: + - \`TripController\`: API 요청 처리 + - \`TripService\`: 비즈니스 로직 처리 + - \`TripRepository\`: 데이터 접근 +- **구성 요소 상호 작용 및 종속성**: 컨트롤러는 서비스에 의존하고, 서비스는 리포지토리에 의존합니다. +- **주요 흐름 및 프로세스**: 클라이언트 요청 -> 컨트롤러 -> 서비스 -> 리포지토리 -> 데이터베이스. + +`; diff --git a/src/constants/guides/docsGuide.js b/src/constants/guides/docsGuide.js new file mode 100644 index 0000000..1e1ea47 --- /dev/null +++ b/src/constants/guides/docsGuide.js @@ -0,0 +1,55 @@ +import { AuthController } from './AuthController.js'; +import { KeywordController } from './KeywordController.js'; +import { LiveInformationController } from './LiveInformationController.js'; +import { MemberController } from './MemberController.js'; +import { MemberLiveInformationController } from './MemberLiveInformationController.js'; +import { PlannerController } from './PlannerController.js'; +import { RecommendTripController } from './RecommendTripController.js'; +import { TripController } from './TripController.js'; + +export const docsGuideText = { + regularFiles: [ + { + fileName: 'AuthController.md', + fileContents: AuthController, + }, + { + fileName: 'KeywordController.md', + fileContents: KeywordController, + }, + { + fileName: 'LiveInformationController.md', + fileContents: LiveInformationController, + }, + { + fileName: 'MemberController.md', + fileContents: MemberController, + }, + { + fileName: 'MemberLiveInformationController.md', + fileContents: MemberLiveInformationController, + }, + { + fileName: 'PlannerController.md', + fileContents: PlannerController, + }, + { + fileName: 'RecommendTripController.md', + fileContents: RecommendTripController, + }, + { + fileName: 'TripController.md', + fileContents: TripController, + }, + ], + summaryFiles: [ + { + fileName: 'Controller_Summary.md', + fileContents: '전체 컨트롤러 요약 내용', + }, + { + fileName: 'Service_Summary.md', + fileContents: '전체 서비스 요약 내용', + }, + ], +}; diff --git a/src/components/organisms/RepoDetailContent/markdownText.jsx b/src/constants/guides/readmeGuide.js similarity index 99% rename from src/components/organisms/RepoDetailContent/markdownText.jsx rename to src/constants/guides/readmeGuide.js index e4efebe..8276b1b 100644 --- a/src/components/organisms/RepoDetailContent/markdownText.jsx +++ b/src/constants/guides/readmeGuide.js @@ -1,4 +1,4 @@ -export const markdownText = ` +export const readmeGuideText = ` # Project Name moheng @@ -143,4 +143,4 @@ python3 main.py ## 📈 Performance - 성능 벤치마크 및 최적화 기술을 통해 시스템의 응답 속도를 개선할 수 있습니다. - 확장성 고려 사항: 시스템이 증가하는 사용자 수를 처리할 수 있도록 설계되었습니다. -`; \ No newline at end of file +`; diff --git a/src/hooks/RepoDetailContent/useChatbot.js b/src/hooks/RepoDetailContent/useChatbot.js new file mode 100644 index 0000000..3a04da3 --- /dev/null +++ b/src/hooks/RepoDetailContent/useChatbot.js @@ -0,0 +1,138 @@ +// src/hooks/useChatbot.js +import { useState, useCallback } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { chatbotAPI } from '../../api/index.js'; + +export const useChatbot = (registeredRepoId) => { + const queryClient = useQueryClient(); + const [isLoading, setIsLoading] = useState(false); + + // 채팅 히스토리 조회 + const { + data: chatHistory, + isLoading: isHistoryLoading, + isError, + error, + refetch: refetchHistory, + } = useQuery({ + queryKey: ['chatHistory', registeredRepoId], + queryFn: () => chatbotAPI.getChatbotHistory(registeredRepoId), + enabled: !!registeredRepoId, + select: (data) => { + return data.findChatLogResponses.map((chat, index) => ({ + id: index, + text: chat.answer, + isUser: false, + question: chat.question, + })); + }, + }); + + // 메시지 전송 핸들러 + const handleSendMessage = useCallback( + //TODO if guide 경우 + // const handleSend = async () => { + // if (!inputText.trim() || isLoading) return; + + // // Add user message + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: inputText, + // isUser: true + // }]); + // setInputText(''); + // setIsLoading(true); + + // try { + // // Simulate API call + // await new Promise(resolve => setTimeout(resolve, 2000)); + // if (isTest === 0) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[0], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + // if (isTest === 1) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[1], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + + // if (isTest > 1) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: chattingText[2], + // isUser: false + // }]); + // setIsTest((state) => state + 1); + // } + + // } catch (error) { + // setMessages(prev => [...prev, { + // id: Date.now(), + // text: "죄송합니다. 오류가 발생했습니다. 다시 시도해 주세요.", + // isUser: false, + // isError: true + // }]); + // } finally { + // setIsLoading(false); + // } + // }; + + async (message) => { + if (!message.trim()) return null; + + setIsLoading(true); + try { + // API 호출 및 응답 처리 + const response = await chatbotAPI.postChatMessage(registeredRepoId, { + question: message, + }); + + // 채팅 히스토리 갱신 + await refetchHistory(); + + return { + success: true, + response: response.answer, + }; + } catch (error) { + console.error('Failed to send message:', error); + return { + success: false, + error: error.message, + }; + } finally { + setIsLoading(false); + } + }, + [registeredRepoId, refetchHistory], + ); + + // 채팅 초기화 + const resetChat = useCallback(async () => { + try { + // 채팅 초기화 API 호출 (필요한 경우) + // await chatBotAPI.resetChat(registeredRepoId); + + // 캐시 무효화 + await queryClient.invalidateQueries(['chatHistory', registeredRepoId]); + } catch (error) { + console.error('Failed to reset chat:', error); + } + }, [queryClient, registeredRepoId]); + + return { + chatHistory, + isLoading: isLoading || isHistoryLoading, + isError, + error, + // sendMessage: handleSendMessage, + resetChat, + }; +}; diff --git a/src/hooks/RepoDetailContent/useDocument.js b/src/hooks/RepoDetailContent/useDocument.js index 3746ac0..ebb61a6 100644 --- a/src/hooks/RepoDetailContent/useDocument.js +++ b/src/hooks/RepoDetailContent/useDocument.js @@ -1,6 +1,7 @@ // src/hooks/RepoDetailContent/useDocument.js import { useQuery } from '@tanstack/react-query'; import { downloadAPI } from '../../api/index.js'; +import { docsGuideText } from '../../constants/guides/docsGuide.js'; export const useDocument = (registeredRepoId) => { console.log('useDocument hook called with ID:', registeredRepoId); @@ -11,6 +12,11 @@ export const useDocument = (registeredRepoId) => { if (!registeredRepoId) { throw new Error('Repository ID is required'); } + if (registeredRepoId === 'guide') { + console.log('Returning readme guide'); + return docsGuideText; + } + console.log('Fetching data from documentAPI'); const response = await downloadAPI.postDownloadDocs(registeredRepoId); console.log('API Response:', response); return response; diff --git a/src/hooks/RepoDetailContent/useReadme.js b/src/hooks/RepoDetailContent/useReadme.js index 5ffdb4d..554b304 100644 --- a/src/hooks/RepoDetailContent/useReadme.js +++ b/src/hooks/RepoDetailContent/useReadme.js @@ -1,22 +1,52 @@ // src/hooks/RepoDetailContent/useReadme.js import { useQuery } from '@tanstack/react-query'; import { downloadAPI } from '../../api/index.js'; +import { readmeGuideText } from '../../constants/guides/readmeGuide.js'; export const useReadme = (registeredRepoId) => { console.log('useReadme hook called with ID:', registeredRepoId); + // return useQuery({ + // queryKey: ['readme', registeredRepoId], + // queryFn: async () => { + // if (!registeredRepoId) { + // throw new Error('Repository ID is required'); + // } + // if (registeredRepoId === 'guide') { + // return { + // contents: readmeGuideText, + // }; + // } + // const response = await downloadAPI.postDownloadReadme(registeredRepoId); + // return response?.contents || ''; // contents가 없을 경우 빈 문자열 반환 + // }, + // enabled: Boolean(registeredRepoId), + // staleTime: 1000 * 60 * 5, + // cacheTime: 1000 * 60 * 30, + // retry: 1, + // refetchOnWindowFocus: false, // 윈도우 포커스 시 리패치 방지 + // onError: (error) => { + // console.error('README 데이터 불러오기 실패:', error); + // }, + // }); + return useQuery({ queryKey: ['readme', registeredRepoId], queryFn: async () => { if (!registeredRepoId) { throw new Error('Repository ID is required'); } + if (registeredRepoId === 'guide') { + console.log('Returning readme guide'); + return { + contents: readmeGuideText, + }; + } const response = await downloadAPI.postDownloadReadme(registeredRepoId); console.log('API Response:', response); return response; }, select: (data) => { - console.log('Processing data:', data); // API 응답에서 content 필드를 추출하고 유효성 검사 if (!data?.contents) return ''; return data.contents; diff --git a/src/hooks/useAppModal.js b/src/hooks/useAppModal.js new file mode 100644 index 0000000..53693f1 --- /dev/null +++ b/src/hooks/useAppModal.js @@ -0,0 +1,26 @@ +// src/hooks/useAppModal.js +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +const useAppModal = () => { + const [AppRepo, setAppRepo] = useState(null); + const navigate = useNavigate(); + + const openModal = (repo) => { + setAppRepo(repo); + navigate(`/repositories/${repo.repositoryName}`); + }; + + const closeModal = () => { + setAppRepo(null); + navigate('/repositories'); + }; + + return { + AppRepo, + setAppRepo, + openModal, + closeModal, + }; +}; +export default useAppModal; diff --git a/src/hooks/useRegisteredRepo.js b/src/hooks/useRegisteredRepo.js index 4a34504..3aa4317 100644 --- a/src/hooks/useRegisteredRepo.js +++ b/src/hooks/useRegisteredRepo.js @@ -152,7 +152,7 @@ export const useRepoManagement = () => { formData, validationErrors, isLoading: isAddingRepoLoading, - error: addRepoError, + addRepoError, handleChange, handleSubmit, resetForm, diff --git a/src/hooks/useRepoManagement.js b/src/hooks/useRepoManagement.js index f11a571..5cc60a5 100644 --- a/src/hooks/useRepoManagement.js +++ b/src/hooks/useRepoManagement.js @@ -6,41 +6,20 @@ import { useAddRepo } from './useAddRepo'; import { registerAPI } from '../api/index.js'; import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { - useAppModalStore, - useRepoStore, - useRegisteredRepoStore, -} from '../store/store.js'; - -/** - * 레포지토리 관리를 위한 통합 커스텀 훅 - */ +import { useAppModalStore, useRegisteredRepoStore } from '../store/store.js'; + export const useRepoManagement = () => { const navigate = useNavigate(); const queryClient = useQueryClient(); - /** NOTE 로직 생각 - * @desc 페이지 진입 시 레포지터리 데이터 받아오기 - * @detail store에 setRegisteredRepositories - * @detail 각 레포별로 readmeComplete,chatbotComplete ,docsComplete를 각각 확인해서 로딩중인 레포지터리가 있는지 확인 - * @detail 로딩중인 레포지터리가 있으면 store에 setIsLoadingRepository - */ - - //TODO 지울거 Store Actions - // const setSelectedCard = useRepoStore((state) => state.setSelectedCard); + // App Modal Store + const { openAppModal, closeAppModal, setAppRepo } = useAppModalStore(); - //NOTE Store + // Registered Repo Store const { setActiveRepositoryId, setRepositoryToRemove, repositoryToRemove } = useRegisteredRepoStore(); - /** - * @axios React Query를 사용한 레포지토리 데이터 페칭 - * @axios registerAPI.getRegisteredRepoList - * @desc 레포지터리 등록한 리스트 목록 가져오기 - * @desc 레포지토리 데이터를 스토어에 저장 - */ - - // useState로 폴링 관련 상태 관리 + // 폴링 관련 상태 관리 const [pollingStartTime, setPollingStartTime] = useState(null); const [isPolling, setIsPolling] = useState(false); @@ -48,10 +27,10 @@ export const useRepoManagement = () => { const checkPollingTimeout = useCallback(() => { if (!pollingStartTime) return false; const timeElapsed = Date.now() - pollingStartTime; - // return timeElapsed >= 5 * 60 * 1000; // 5분 - return timeElapsed >= 1 * 60 * 1000; // 5분 + return timeElapsed >= 10 * 60 * 1000; // 5분 () }, [pollingStartTime]); + // React Query로 레포지토리 데이터 페칭 const { data: registeredRepositoriesList, isError, @@ -83,8 +62,11 @@ export const useRepoManagement = () => { throw error; } }, - refetchInterval: (queryInfo) => { - const data = queryInfo?.state?.data; + refetchInterval: (query) => { + console.log('실제 fetch 횟수:', query.state.fetchCount); + console.log('refetch 횟수:', query); + + const data = query?.state?.data; if (!data) return false; // if (!pollingStartTime) { // setPollingStartTime(Date.now()); @@ -152,28 +134,20 @@ export const useRepoManagement = () => { staleTime: 0, }); - //SECTION - App Modal 관리 - - // App Modal Store - const { isAppModalOpen, setOpenAppModal, setAppRepo, setCloseAppModal } = - useAppModalStore(); - // Modal Management const addRepoModal = useModal(); const deleteRepoModal = useModal(); - // Add Repository Form Management with success callback - + // Loading 상태 관리 const [extendedLoading, setExtendedLoading] = useState(false); useEffect(() => { - return () => { - setExtendedLoading(false); - }; + return () => setExtendedLoading(false); }, []); + + // useAddRepo 관련 로직 const { formData, validationErrors, - // isLoading, isPending: isAddingRepoLoading, error: addRepoError, handleChange, @@ -197,7 +171,6 @@ export const useRepoManagement = () => { type: 'active', exact: true, }); - modalHandlers.addRepo.close(); // 폴링 상태 초기화 및 시작 setPollingStartTime(Date.now()); @@ -205,7 +178,11 @@ export const useRepoManagement = () => { console.log('✅ Repository added and list refreshed successfully:', newRepo); - setExtendedLoading(false); + // 5초 후에 모달 닫기 + setTimeout(() => { + modalHandlers.addRepo.close(); + setExtendedLoading(false); + }, 10000); // 5000ms = 5초 // 모든 데이터 작업이 완료된 후 모달 닫기 } catch (error) { console.error('Failed to refresh repository list:', error); @@ -220,27 +197,20 @@ export const useRepoManagement = () => { app: { open: useCallback( (registeredRepoId) => { - const repo = registeredRepositoriesList.find( - (repo) => repo.registeredRepoId === registeredRepoId, - ); + console.log('Opening modal for repo:', registeredRepoId); + setActiveRepositoryId(registeredRepoId); + const repo = openAppModal(registeredRepoId, registeredRepositoriesList); if (repo) { - setAppRepo(repo); - setOpenAppModal(); - navigate(`/repositories/${repo.repositoryName}`); + navigate(`/repositories/${repo.registeredRepoId}`); } }, - [navigate, setOpenAppModal, setAppRepo, registeredRepositoriesList], + [openAppModal, setActiveRepositoryId, registeredRepositoriesList, navigate], ), - - close: useCallback(() => { - setCloseAppModal(); - navigate('/repositories'); - }, [navigate, setCloseAppModal]), }, addRepo: { open: useCallback(() => { - resetForm(); // 폼 초기화 + resetForm(); addRepoModal.openModal(); }, [resetForm, addRepoModal]), @@ -249,7 +219,6 @@ export const useRepoManagement = () => { }, [addRepoModal]), }, }; - //!SECTION - 모달관리 /** * 이벤트 핸들러 @@ -257,14 +226,14 @@ export const useRepoManagement = () => { const eventHandlers = { handleCardClick: useCallback( (cardId) => { - console.log('useRepoManagement cardClick', cardId); - setActiveRepositoryId(cardId); + console.log('Card clicked:', cardId); modalHandlers.app.open(cardId); }, - [setActiveRepositoryId, modalHandlers.app], + [modalHandlers.app], ), }; + // Delete 핸들러 const deleteRepoHandlers = { handleDeleteClick: useCallback( (e, repo) => { @@ -275,21 +244,6 @@ export const useRepoManagement = () => { [deleteRepoModal, setRepositoryToRemove], ), - // handleConfirmDelete: useCallback(async () => { - // try { - // const success = deleteRepo(); - // if (success) { - // // React Query 캐시 갱신 - // queryClient.invalidateQueries({ queryKey: ['repositories'] }); - // deleteRepoModal.closeModal(); - // // TODO: 성공 토스트 메시지 표시 - // console.log('Repository deleted successfully'); - // } - // } catch (error) { - // console.error('Failed to delete repository:', error); - // // TODO: 에러 토스트 메시지 표시 - // } - // }, [deleteRepoModal, deleteRepo, queryClient]), handleConfirmDelete: useCallback(async () => { console.log(repositoryToRemove); if (!repositoryToRemove) { @@ -335,16 +289,17 @@ export const useRepoManagement = () => { }; return { - //RegisteredRepositories Data RegisteredRepositories: { registeredRepositoriesList, + isLoading, + isError, + error, }, - // Modal States & Controls modals: { app: { - isOpen: isAppModalOpen, ...modalHandlers.app, + close: closeAppModal, }, addRepo: { isOpen: addRepoModal.isOpen, @@ -357,20 +312,17 @@ export const useRepoManagement = () => { }, }, - // Add Repository 관련 상태와 핸들러 addRepoForm: { formData, validationErrors, isAddingRepoLoading: isAddingRepoLoading || extendedLoading, - error: addRepoError, + addRepoError: addRepoError, handleChange, handleSubmit, resetForm, }, - // Delete Repository 관련 상태와 핸들러 - deleteRepoHandlers, - // Event Handlers + deleteRepoHandlers, handlers: eventHandlers, }; }; diff --git a/src/layouts/Header/header.jsx b/src/layouts/Header/header.jsx index 4024dab..a62d588 100644 --- a/src/layouts/Header/header.jsx +++ b/src/layouts/Header/header.jsx @@ -14,6 +14,8 @@ import { useAuthStore, useUserStore } from "../../store/store.js" const HomeHeader = ({ role }) => { // 여러 상태 한번에 가져오기 const { userNickname } = useUserStore() + + const { isAuthenticated } = useAuthStore(); @@ -22,36 +24,6 @@ const HomeHeader = ({ role }) => { const { logout } = useAuth(); - //SECTION 스크롤 - const [scrolled, setScrolled] = useState(false); - const sentinelRef = useRef(null); - - useEffect(() => { - const sentinel = sentinelRef.current; - - const observer = new IntersectionObserver( - ([entry]) => { - setScrolled(!entry.isIntersecting); - }, - { - threshold: 0, - rootMargin: `0px 0px 0px 0px` - } - ); - - if (sentinel) { - observer.observe(sentinel); - } - - return () => { - if (sentinel) { - observer.unobserve(sentinel); - } - }; - }, [location.pathname]); - - console.log(scrolled) - //!SECTION @@ -78,8 +50,8 @@ const HomeHeader = ({ role }) => { return ( <> -
    - +
    + {/* SECTION - jsx 장바구니 | 로그인 | 회원가입 */} @@ -95,7 +67,7 @@ const HomeHeader = ({ role }) => { - + @@ -103,10 +75,10 @@ const HomeHeader = ({ role }) => { navigate("/")} fontFamily={'Roboto'} weight={100} size={'32px'} cursor={"pointer"} $isGradient>Dododocs - - navigate("/docs")}>AI Code document - navigate("/chatting")} >AI chatting - navigate("/chatting")} >Read Me Editor + + navigate("/landing/docs")}>Code document + navigate("/landing/chatting")} >Chatbot + navigate("/landing/readme")} >Read Me Editor @@ -124,10 +96,19 @@ const HomeHeader = ({ role }) => { null } - + { + isAuthenticated ? + + : + + } + diff --git a/src/pages/ChattingPage/index.jsx b/src/pages/ChattingPage/index.jsx deleted file mode 100644 index 0120c84..0000000 --- a/src/pages/ChattingPage/index.jsx +++ /dev/null @@ -1,16 +0,0 @@ - -import React, { useState, useRef, useEffect } from "react"; -import { Header } from "../../layouts/index.js" -import { ChattingContent } from "../../components/index.js" -const Home = () => { - - return ( - <> -
    - - - - ); -}; - -export default Home; diff --git a/src/pages/DocsPage/index.jsx b/src/pages/DocsPage/index.jsx deleted file mode 100644 index 29bb40b..0000000 --- a/src/pages/DocsPage/index.jsx +++ /dev/null @@ -1,17 +0,0 @@ - -import React, { useState, useRef, useEffect } from "react"; -import { Header } from "../../layouts/index.js" -import { DocsContent } from "../../components/index.js" -import _ from '../../config/index.js'; -const Home = () => { - - return ( - <> -
    - - - - ); -}; - -export default Home; diff --git a/src/pages/LandingPage/index.jsx b/src/pages/LandingPage/index.jsx new file mode 100644 index 0000000..b5e0eef --- /dev/null +++ b/src/pages/LandingPage/index.jsx @@ -0,0 +1,16 @@ + +import React from "react"; +import { Header } from "../../layouts/index.js" +import { LandingContent } from "../../components/index.js" +const Landing = () => { + + return ( + <> +
    + + + + ); +}; + +export default Landing; diff --git a/src/router/ChattingRouter.jsx b/src/router/ChattingRouter.jsx deleted file mode 100644 index 3f518a1..0000000 --- a/src/router/ChattingRouter.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import Chatting from "../pages/ChattingPage/index.jsx" -const ChattingRouter = () => { - return ( - - ); -}; - -export default ChattingRouter; diff --git a/src/router/DocsRouter.jsx b/src/router/DocsRouter.jsx deleted file mode 100644 index d0f60b2..0000000 --- a/src/router/DocsRouter.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import Docs from "../pages/DocsPage/index.jsx" -const DocsRouter = () => { - return ( - - ); -}; - -export default DocsRouter; diff --git a/src/router/LandingRouter.jsx b/src/router/LandingRouter.jsx new file mode 100644 index 0000000..69a2a52 --- /dev/null +++ b/src/router/LandingRouter.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import Landing from "../pages/LandingPage/index.jsx" +const LandingRouter = () => { + return ( + + ); +}; + +export default LandingRouter; diff --git a/src/router/RepoRouter.jsx b/src/router/RepoRouter.jsx index 3166169..31f9a4d 100644 --- a/src/router/RepoRouter.jsx +++ b/src/router/RepoRouter.jsx @@ -1,8 +1,14 @@ import React from 'react'; import Repo from "../pages/RepoPage/index.jsx" +import { Outlet } from 'react-router-dom'; + const RepoRouter = () => { return ( - + <> + + {/* 모달이 여기에 렌더링됨 */} + + ); }; diff --git a/src/router/index.jsx b/src/router/index.jsx index 98dcf78..a2ce754 100644 --- a/src/router/index.jsx +++ b/src/router/index.jsx @@ -1,18 +1,21 @@ // /src/router/index.jsx //TODO ProtectedRoute by role -import React, { Suspense, lazy } from 'react'; -import { Navigate, BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import React, { useEffect, Suspense, lazy } from 'react'; +import { Navigate, BrowserRouter as Router, Routes, Route, useNavigate, useParams } from 'react-router-dom'; -// import { notification } from 'antd'; import Home from './HomeRouter.jsx'; -import Docs from './DocsRouter.jsx'; import Login from "./LoginRouter.jsx" -import Chatting from "./ChattingRouter.jsx"; import OAuthCallback from "./OAuthCallbackRouter.jsx" import Repo from "./RepoRouter.jsx" +import Landing from "./LandingRouter.jsx" // 모달용 컴포넌트 import { RepoDetailContent } from "../components" +// 모달용 store import 추가 +import { useAppModalStore } from '../store/store.js'; + +import Modal from 'react-modal'; + // const ProtectedRoute = ({ children, allowedRoles }) => { // const role = useSelector(selectUserRole); @@ -27,20 +30,31 @@ import { RepoDetailContent } from "../components" // }; +// Modal의 루트 엘리먼트 설정 +Modal.setAppElement('#root'); + + function App() { return ( {/* Loading...
    }> */} } /> - } /> + } /> + } /> } /> } /> - } /> - } /> - - } /> - } /> + } > + } + /> + + {/* } + /> */} {/* diff --git a/src/store/appModalStore.js b/src/store/appModalStore.js index ad11f5d..40e8725 100644 --- a/src/store/appModalStore.js +++ b/src/store/appModalStore.js @@ -1,9 +1,9 @@ +// src/stores/useAppModalStore.js import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; const initialState = { - isAppModalOpen: false, // 모달 열림 상태 - AppRepo: null, // 열릴 repo 이름 + AppRepo: null, }; const useAppModalStore = create( @@ -11,20 +11,44 @@ const useAppModalStore = create( (set, get) => ({ ...initialState, - setAppRepo: (repo) => set({ AppRepo: repo }), // 선택된 repo 설정 - setOpenAppModal: () => { - const AppRepo = get().AppRepo; - if (!AppRepo) return; - set({ isAppModalOpen: true }); // Redux DevTools에서도 상태를 볼 수 있음 - }, // 모달 열기 - setCloseAppModal: () => set({ isAppModalOpen: false, AppRepo: null }), // 모달 닫기 - reset: () => set(initialState, false, 'resetAppModalState'), // false는 state 교체, 'reset'은 액션 이름 + // 선택된 repo 설정 + setAppRepo: (repo) => set({ AppRepo: repo }), + + // repo 데이터로 모달 열기 + openAppModal: (registeredRepoId, registeredReposList) => { + const repo = + registeredRepoId === 'guide' + ? { + registeredRepoId: 'guide', + repositoryName: 'guide', + branchName: 'main', + createdAt: '2024-12-18', + readmeComplete: true, + chatbotComplete: true, + docsComplete: true, + } + : registeredReposList?.find( + (repo) => repo.registeredRepoId === registeredRepoId, + ); + + if (repo) { + set({ AppRepo: repo }); + return repo; // 라우팅을 위해 repo 반환 + } + return null; + }, + + // 모달 닫기 + closeAppModal: () => set({ AppRepo: null }), + + // store 초기화 + reset: () => set(initialState, false, 'resetAppModalStore'), }), { name: 'AppModalStore', - enabled: process.env.NODE_ENV === 'development', // 개발 환경에서만 활성화 + enabled: process.env.NODE_ENV === 'development', }, - ), // Redux DevTools에서 표시될 스토어 이름 + ), ); export default useAppModalStore; diff --git a/src/store/repoStore.js b/src/store/repoStore.js index 35b7a64..9f5fd43 100644 --- a/src/store/repoStore.js +++ b/src/store/repoStore.js @@ -27,6 +27,15 @@ const initialState = { Action: 'Delete', }, ], + testRepo: { + registeredRepoId: 1, + repositoryName: 'testREpo', + branchName: 'main', + createdAt: '2024-12-18', + readmeComplete: true, + chatbotComplete: true, + docsComplete: true, + }, }; /** diff --git a/src/utils/jwtUtils.js b/src/utils/jwtUtils.js index 7d113bd..07163cf 100644 --- a/src/utils/jwtUtils.js +++ b/src/utils/jwtUtils.js @@ -1,7 +1,5 @@ // src/utils/jwtUtils.js import { jwtDecode } from 'jwt-decode'; -import { notification } from 'antd'; - const TOKEN_KEY = 'golla-golla'; const jwtUtils = { @@ -60,11 +58,11 @@ const jwtUtils = { localStorage.removeItem(TOKEN_KEY); - notification.error({ - message: '로그인에 실패하였습니다.', - description: '다시 시도해주세요', - duration: 2, - }); + // notification.error({ + // message: '로그인에 실패하였습니다.', + // description: '다시 시도해주세요', + // duration: 2, + // }); }, }; diff --git a/src/utils/storeUtils.js b/src/utils/storeUtils.js index 0e33b54..6979de4 100644 --- a/src/utils/storeUtils.js +++ b/src/utils/storeUtils.js @@ -8,7 +8,7 @@ import { export const resetAllStores = () => { // 각 store의 초기화 함수 실행 useAuthStore.getState().clearAuth(); - useUserStore.getState().clearMemberInfo(); + useUserStore.getState().clearUserInfo(); useAppModalStore.getState().reset(); useRepoStore.getState().reset(); }; diff --git a/yarn.lock b/yarn.lock index 71e7adf..2d44b02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20,69 +20,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" - integrity sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg== - dependencies: - "@ctrl/tinycolor" "^3.6.1" - -"@ant-design/cssinjs-utils@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz#57abb43671023f937348bd33442862c60ac8e8b2" - integrity sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ== - dependencies: - "@ant-design/cssinjs" "^1.21.0" - "@babel/runtime" "^7.23.2" - rc-util "^5.38.0" - -"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": - version "1.21.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz#7320813c5f747e0cde52c388eff5198d78d57230" - integrity sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg== - dependencies: - "@babel/runtime" "^7.11.1" - "@emotion/hash" "^0.8.0" - "@emotion/unitless" "^0.7.5" - classnames "^2.3.1" - csstype "^3.1.3" - rc-util "^5.35.0" - stylis "^4.3.3" - -"@ant-design/fast-color@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" - integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== - dependencies: - "@babel/runtime" "^7.24.7" - -"@ant-design/icons-svg@^4.4.0": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" - integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== - -"@ant-design/icons@^5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.5.1.tgz#4ff57b2a0d3bafae3d990c2781fd857ead36c935" - integrity sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w== - dependencies: - "@ant-design/colors" "^7.0.0" - "@ant-design/icons-svg" "^4.4.0" - "@babel/runtime" "^7.24.8" - classnames "^2.2.6" - rc-util "^5.31.1" - -"@ant-design/react-slick@~1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" - integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== - dependencies: - "@babel/runtime" "^7.10.4" - classnames "^2.2.5" - json2mq "^0.2.0" - resize-observer-polyfill "^1.5.1" - throttle-debounce "^5.0.0" - "@antfu/install-pkg@^0.4.0": version "0.4.1" resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-0.4.1.tgz#d1d7f3be96ecdb41581629cafe8626d1748c0cf1" @@ -1245,13 +1182,6 @@ "@babel/plugin-transform-modules-commonjs" "^7.25.7" "@babel/plugin-transform-typescript" "^7.25.7" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.8", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.7.tgz#7ffb53c37a8f247c8c4d335e89cdf16a2e0d0fb6" @@ -1259,6 +1189,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.18.3", "@babel/runtime@^7.23.8", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.25.7", "@babel/template@^7.3.3": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.7.tgz#27f69ce382855d915b14ab0fe5fb4cbf88fa0769" @@ -1473,11 +1410,6 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw== -"@ctrl/tinycolor@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" - integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== - "@emotion/babel-plugin@^11.12.0": version "11.12.0" resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.12.0.tgz#7b43debb250c313101b3f885eba634f1d723fcc2" @@ -1506,11 +1438,6 @@ "@emotion/weak-memoize" "^0.4.0" stylis "4.2.0" -"@emotion/hash@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== - "@emotion/hash@^0.9.2": version "0.9.2" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.2.tgz#ff9221b9f58b4dfe61e619a7788734bd63f6898b" @@ -1592,11 +1519,6 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== -"@emotion/unitless@^0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" - integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== - "@emotion/use-insertion-effect-with-fallbacks@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" @@ -2043,98 +1965,6 @@ schema-utils "^4.2.0" source-map "^0.7.3" -"@rc-component/async-validator@^5.0.3": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" - integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== - dependencies: - "@babel/runtime" "^7.24.4" - -"@rc-component/color-picker@~2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" - integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== - dependencies: - "@ant-design/fast-color" "^2.0.6" - "@babel/runtime" "^7.23.6" - classnames "^2.2.6" - rc-util "^5.38.1" - -"@rc-component/context@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" - integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== - dependencies: - "@babel/runtime" "^7.10.1" - rc-util "^5.27.0" - -"@rc-component/mini-decimal@^1.0.1": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" - integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== - dependencies: - "@babel/runtime" "^7.18.0" - -"@rc-component/mutate-observer@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" - integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" - integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== - dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/qrcode@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.0.0.tgz#48a8de5eb11d0e65926f1377c4b1ef4c888997f5" - integrity sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== - dependencies: - "@babel/runtime" "^7.24.7" - classnames "^2.3.2" - rc-util "^5.38.0" - -"@rc-component/tour@~1.15.1": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" - integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== - dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/portal" "^1.0.0-9" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.2" - rc-util "^5.24.4" - -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" - integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== - dependencies: - "@babel/runtime" "^7.23.2" - "@rc-component/portal" "^1.1.0" - classnames "^2.3.2" - rc-motion "^2.0.0" - rc-resize-observer "^1.3.1" - rc-util "^5.38.0" - -"@reduxjs/toolkit@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.3.0.tgz#d00134634d6c1678e8563ac50026e429e3b64420" - integrity sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA== - dependencies: - immer "^10.0.3" - redux "^5.0.1" - redux-thunk "^3.1.0" - reselect "^5.1.0" - "@remix-run/router@1.20.0": version "1.20.0" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4" @@ -2669,13 +2499,6 @@ "@types/d3-transition" "*" "@types/d3-zoom" "*" -"@types/debug@^4.0.0": - version "4.1.12" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" - integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== - dependencies: - "@types/ms" "*" - "@types/eslint@^7.29.0 || ^8.4.1": version "8.56.12" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.12.tgz#1657c814ffeba4d2f84c0d4ba0f44ca7ea1ca53a" @@ -2684,14 +2507,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree-jsx@^1.0.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz#858a88ea20f34fe65111f005a689fa1ebf70dc18" - integrity sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg== - dependencies: - "@types/estree" "*" - -"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.5": +"@types/estree@*", "@types/estree@^1.0.5": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== @@ -2753,13 +2569,6 @@ dependencies: "@types/node" "*" -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - "@types/html-minifier-terser@^6.0.0": version "6.1.0" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" @@ -2814,23 +2623,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/mdast@^4.0.0": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6" - integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA== - dependencies: - "@types/unist" "*" - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== -"@types/ms@*": - version "0.7.34" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" - integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== - "@types/node-forge@^1.3.0": version "1.3.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" @@ -2960,16 +2757,6 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" - integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== - -"@types/unist@^2.0.0": - version "2.0.11" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" - integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA== - "@types/use-sync-external-store@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" @@ -3092,7 +2879,7 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": +"@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== @@ -3389,61 +3176,6 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -antd@^5.21.5: - version "5.21.5" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.21.5.tgz#c82f14b36f6821490ccd30d8befaa65a11b5e782" - integrity sha512-g/c8VkdruKDCVA6di9Ow1fG6dLtYJ1IOraPo7vXaY7DoQ56A3HExaFaH0fBEwTYKC0ICeftC4iA5eAjrF6/b9w== - dependencies: - "@ant-design/colors" "^7.1.0" - "@ant-design/cssinjs" "^1.21.1" - "@ant-design/cssinjs-utils" "^1.1.1" - "@ant-design/icons" "^5.5.1" - "@ant-design/react-slick" "~1.1.2" - "@babel/runtime" "^7.25.6" - "@ctrl/tinycolor" "^3.6.1" - "@rc-component/color-picker" "~2.0.1" - "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/qrcode" "~1.0.0" - "@rc-component/tour" "~1.15.1" - "@rc-component/trigger" "^2.2.3" - classnames "^2.5.1" - copy-to-clipboard "^3.3.3" - dayjs "^1.11.11" - rc-cascader "~3.28.2" - rc-checkbox "~3.3.0" - rc-collapse "~3.8.0" - rc-dialog "~9.6.0" - rc-drawer "~7.2.0" - rc-dropdown "~4.2.0" - rc-field-form "~2.4.0" - rc-image "~7.11.0" - rc-input "~1.6.3" - rc-input-number "~9.2.0" - rc-mentions "~2.16.1" - rc-menu "~9.15.1" - rc-motion "^2.9.3" - rc-notification "~5.6.2" - rc-pagination "~4.3.0" - rc-picker "~4.6.15" - rc-progress "~4.0.0" - rc-rate "~2.13.0" - rc-resize-observer "^1.4.0" - rc-segmented "~2.5.0" - rc-select "~14.15.2" - rc-slider "~11.1.7" - rc-steps "~6.0.1" - rc-switch "~4.1.0" - rc-table "~7.47.5" - rc-tabs "~15.3.0" - rc-textarea "~1.8.2" - rc-tooltip "~6.2.1" - rc-tree "~5.9.0" - rc-tree-select "~5.23.0" - rc-upload "~4.8.1" - rc-util "^5.43.0" - scroll-into-view-if-needed "^3.1.0" - throttle-debounce "^5.0.2" - any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -3511,11 +3243,6 @@ array-includes@^3.1.6, array-includes@^3.1.8: get-intrinsic "^1.2.4" is-string "^1.0.7" -array-tree-filter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" - integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" @@ -3805,11 +3532,6 @@ babel-preset-react-app@^10.0.1: babel-plugin-macros "^3.1.0" babel-plugin-transform-react-remove-prop-types "^0.4.24" -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -3940,6 +3662,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -4024,11 +3751,6 @@ case-sensitive-paths-webpack-plugin@^2.4.0: resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -4064,26 +3786,6 @@ char-regex@^2.0.0: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e" integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw== -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - -character-reference-invalid@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" - integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== - check-types@^11.2.3: version "11.2.3" resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71" @@ -4138,11 +3840,6 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" - integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== - clean-css@^5.2.2: version "5.3.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" @@ -4219,11 +3916,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -comma-separated-tokens@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" - integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== - commander@7, commander@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" @@ -4274,11 +3966,6 @@ compression@^1.7.4: safe-buffer "5.1.2" vary "~1.1.2" -compute-scroll-into-view@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" - integrity sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -4331,13 +4018,6 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -copy-to-clipboard@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" - integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== - dependencies: - toggle-selection "^1.0.6" - core-js-compat@^3.38.0, core-js-compat@^3.38.1: version "3.38.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" @@ -4614,7 +4294,7 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@3.1.3, csstype@^3.0.2, csstype@^3.1.3: +csstype@3.1.3, csstype@^3.0.2: version "3.1.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== @@ -4873,11 +4553,6 @@ d3-zoom@3: d3-selection "2 - 3" d3-transition "2 - 3" -d3@3.5.17: - version "3.5.17" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" - integrity sha512-yFk/2idb8OHPKkbAL8QaOaqENNoMhIaSHZerk3oQsECwkObkCpJyjYwCe+OHiq6UEdhe1m8ZGARRRO3ljFjlKg== - d3@^7.9.0: version "7.9.0" resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" @@ -4922,24 +4597,6 @@ dagre-d3-es@7.0.11: d3 "^7.9.0" lodash-es "^4.17.21" -dagre-d3-renderer@^0.4.25: - version "0.4.26" - resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.26.tgz#648a491209b853ae96ddf3fea41a1f104479a5a1" - integrity sha512-vOWj1uA4/APTrfDyfHaH/xpfXhPh9rszW+HOaEwPCeA6Afl06Lobfh7OpESuVMQW2QGuY4UQ7pte/p0WhdDs7w== - dependencies: - d3 "3.5.17" - dagre-layout "^0.8.0" - graphlib "^2.1.1" - lodash "^4.17.4" - -dagre-layout@^0.8.0: - version "0.8.8" - resolved "https://registry.yarnpkg.com/dagre-layout/-/dagre-layout-0.8.8.tgz#9b6792f24229f402441c14162c1049e3f261f6d9" - integrity sha512-ZNV15T9za7X+fV8Z07IZquUKugCxm5owoiPPxfEx6OJRD331nkiIaF3vSt0JEY5FkrY0KfRQxcpQ3SpXB7pLPQ== - dependencies: - graphlibrary "^2.2.0" - lodash "^4.17.5" - damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -4981,7 +4638,7 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -dayjs@^1.11.10, dayjs@^1.11.11: +dayjs@^1.11.10: version "1.11.13" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== @@ -4993,7 +4650,7 @@ debug@2.6.9, debug@^2.6.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.6: version "4.3.7" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== @@ -5012,13 +4669,6 @@ decimal.js@^10.2.1: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-named-character-reference@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" - integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== - dependencies: - character-entities "^2.0.0" - dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -5110,11 +4760,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -dequal@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" @@ -5138,13 +4783,6 @@ detect-port-alt@^1.1.6: address "^1.0.1" debug "^2.6.0" -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -5303,7 +4941,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -ejs@^3.1.6: +ejs@^3.1.5, ejs@^3.1.6: version "3.1.10" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== @@ -5528,7 +5166,7 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== @@ -5829,11 +5467,6 @@ estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -estree-util-is-identifier-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz#0b5ef4c4ff13508b34dcd01ecfa945f61fce5dbd" - integrity sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg== - estree-walker@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" @@ -5942,11 +5575,6 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -6376,20 +6004,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphlib@^2.1.1: - version "2.1.8" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da" - integrity sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A== - dependencies: - lodash "^4.17.15" - -graphlibrary@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/graphlibrary/-/graphlibrary-2.2.0.tgz#017a14899775228dec4497a39babfdd6bf56eac6" - integrity sha512-XTcvT55L8u4MBZrM37zXoUxsgxs/7sow7YSygd9CIwfWTVO8RVu7AYXhhCiTuFEf+APKgx6Jk4SuQbYR0vYKmQ== - dependencies: - lodash "^4.17.5" - gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -6458,70 +6072,11 @@ hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: dependencies: function-bind "^1.1.2" -hast-util-sanitize@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-5.0.2.tgz#edb260d94e5bba2030eb9375790a8753e5bf391f" - integrity sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg== - dependencies: - "@types/hast" "^3.0.0" - "@ungap/structured-clone" "^1.0.0" - unist-util-position "^5.0.0" - -hast-util-to-html@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz#a9999a0ba6b4919576a9105129fead85d37f302b" - integrity sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg== - dependencies: - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - comma-separated-tokens "^2.0.0" - hast-util-whitespace "^3.0.0" - html-void-elements "^3.0.0" - mdast-util-to-hast "^13.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - stringify-entities "^4.0.0" - zwitch "^2.0.4" - -hast-util-to-jsx-runtime@^2.0.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz#6d11b027473e69adeaa00ca4cfb5bb68e3d282fa" - integrity sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg== - dependencies: - "@types/estree" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - estree-util-is-identifier-name "^3.0.0" - hast-util-whitespace "^3.0.0" - mdast-util-mdx-expression "^2.0.0" - mdast-util-mdx-jsx "^3.0.0" - mdast-util-mdxjs-esm "^2.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - style-to-object "^1.0.0" - unist-util-position "^5.0.0" - vfile-message "^4.0.0" - -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - -he@^1.1.1, he@^1.2.0: +he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -highlight.js@^11.10.0: - version "11.10.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.10.0.tgz#6e3600dc4b33d6dc23d5bd94fbf72405f5892b92" - integrity sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ== - hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -6574,16 +6129,6 @@ html-minifier-terser@^6.0.2: relateurl "^0.2.7" terser "^5.10.0" -html-url-attributes@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz#83b052cd5e437071b756cd74ae70f708870c2d87" - integrity sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ== - -html-void-elements@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" - integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== - html-webpack-plugin@^5.5.0: version "5.6.2" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.6.2.tgz#174a67c8e55aa3fa2ba94c8e8e42894bfe4978ea" @@ -6714,11 +6259,6 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -immer@^10.0.3: - version "10.1.1" - resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" - integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== - immer@^9.0.7: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" @@ -6773,11 +6313,6 @@ ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inline-style-parser@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz#f4af5fe72e612839fcd453d989a586566d695f22" - integrity sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q== - internal-slot@^1.0.4, internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -6807,19 +6342,6 @@ ipaddr.js@^2.0.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.2.0.tgz#d33fa7bac284f4de7af949638c9d68157c6b92e8" integrity sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA== -is-alphabetical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" - integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== - -is-alphanumerical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" - integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== - dependencies: - is-alphabetical "^2.0.0" - is-decimal "^2.0.0" - is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -6896,11 +6418,6 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" -is-decimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" - integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== - is-docker@^2.0.0, is-docker@^2.1.1: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -6942,11 +6459,6 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" - integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== - is-map@^2.0.2, is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" @@ -6989,11 +6501,6 @@ is-plain-obj@^3.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - is-potential-custom-element-name@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" @@ -7080,7 +6587,7 @@ is-weakset@^2.0.3: call-bind "^1.0.7" get-intrinsic "^1.2.4" -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -7806,13 +7313,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json2mq@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" - integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== - dependencies: - string-convert "^0.2.0" - json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -8065,16 +7565,11 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.7.0: +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -longest-streak@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" - integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -8144,18 +7639,6 @@ markdown-it-anchor@^9.2.0: resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-9.2.0.tgz#89375d9a2a79336403ab7c4fd36b1965cc45e5c8" integrity sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg== -markdown-it-mermaid@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/markdown-it-mermaid/-/markdown-it-mermaid-0.2.5.tgz#aa6e488a5f4ab2006b4a63e22f315683495c1877" - integrity sha512-ZUTFRX+cXEtWmn/9LMlpVklPJiDrHPWyHE/wamC2wm0Ojh1qOcuKWfWW3BqP83+7w6C59rS7M3OrGTs/u9mQTA== - dependencies: - mermaid "^7.1.2" - -markdown-it-toc-done-right@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.2.0.tgz#3ccdce22d5022ffae7b991d07261b1b1de5459d0" - integrity sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ== - markdown-it@^14.1.0: version "14.1.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" @@ -8181,111 +7664,6 @@ match-sorter@^6.0.2: "@babel/runtime" "^7.23.8" remove-accents "0.5.0" -mdast-util-from-markdown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz#4850390ca7cf17413a9b9a0fbefcd1bc0eb4160a" - integrity sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-mdx-expression@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz#43f0abac9adc756e2086f63822a38c8d3c3a5096" - integrity sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-mdx-jsx@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz#76b957b3da18ebcfd0de3a9b4451dcd6fdec2320" - integrity sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - -mdast-util-mdxjs-esm@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz#019cfbe757ad62dd557db35a695e7314bcc9fa97" - integrity sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - -mdast-util-to-hast@^13.0.0: - version "13.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz#5ca58e5b921cc0a3ded1bc02eed79a4fe4fe41f4" - integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - -mdast-util-to-markdown@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz#f910ffe60897f04bb4b7e7ee434486f76288361b" - integrity sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - mdn-data@2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" @@ -8354,217 +7732,11 @@ mermaid@^11.4.1: ts-dedent "^2.2.0" uuid "^9.0.1" -mermaid@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-7.1.2.tgz#6265728156c2e0891e004cba60a44022174487ad" - integrity sha512-bDLu3fQuf3/R0fNkNzB0GTaF7+6SxnZpfTs9DVQF1ougsuP23MBzvEIGfL0ML8zeyg7+J2D+0AaoLVhskW5ulw== - dependencies: - d3 "3.5.17" - dagre-d3-renderer "^0.4.25" - dagre-layout "^0.8.0" - he "^1.1.1" - lodash "^4.17.4" - moment "^2.20.1" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -micromark-core-commonmark@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d" - integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA== - dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-destination@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" - integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-label@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" - integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-space@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" - integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-title@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" - integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" - integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-character@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" - integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-chunked@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" - integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-classify-character@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" - integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-combine-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" - integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" - integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-decode-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" - integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" - integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== - -micromark-util-html-tag-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" - integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== - -micromark-util-normalize-identifier@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" - integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-resolve-all@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" - integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== - dependencies: - micromark-util-types "^2.0.0" - -micromark-util-sanitize-uri@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" - integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-subtokenize@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5" - integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-symbol@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" - integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== - -micromark-util-types@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" - integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== - -micromark@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" - integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" @@ -8654,7 +7826,7 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8671,11 +7843,6 @@ mlly@^1.7.1, mlly@^1.7.2, mlly@^1.7.3: pkg-types "^1.2.1" ufo "^1.5.4" -moment@^2.20.1: - version "2.30.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -8928,6 +8095,14 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@^7.3.1: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + open@^8.0.9, open@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" @@ -9034,20 +8209,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-entities@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.1.tgz#4e2a01111fb1c986549b944af39eeda258fc9e4e" - integrity sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w== - dependencies: - "@types/unist" "^2.0.0" - character-entities "^2.0.0" - character-entities-legacy "^3.0.0" - character-reference-invalid "^2.0.0" - decode-named-character-reference "^1.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -9812,6 +8973,11 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +prismjs@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -9841,11 +9007,6 @@ prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -property-information@^6.0.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.5.0.tgz#6212fbb52ba757e92ef4fb9d657563b933b7ffec" - integrity sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig== - proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -9925,357 +9086,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -rc-cascader@~3.28.2: - version "3.28.2" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.28.2.tgz#91720d3498261a7bff9fffc953501a8830f601fb" - integrity sha512-8f+JgM83iLTvjgdkgU7GfI4qY8icXOBP0cGZjOdx2iJAkEe8ucobxDQAVE69UD/c3ehCxZlcgEHeD5hFmypbUw== - dependencies: - "@babel/runtime" "^7.12.5" - array-tree-filter "^2.1.0" - classnames "^2.3.1" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.37.0" - -rc-checkbox@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.3.0.tgz#0ffcb65ab78c7d2fcd1a0d6554af36786516bd02" - integrity sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.25.2" - -rc-collapse@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.8.0.tgz#02bcf81e1601aa185cd3b9fab0ceefd8dc11aefb" - integrity sha512-YVBkssrKPBG09TGfcWWGj8zJBYD9G3XuTy89t5iUmSXrIXEAnO1M+qjUxRW6b4Qi0+wNWG6MHJF/+US+nmIlzA== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.3.4" - rc-util "^5.27.0" - -rc-dialog@~9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" - integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/portal" "^1.0.0-8" - classnames "^2.2.6" - rc-motion "^2.3.0" - rc-util "^5.21.0" - -rc-drawer@~7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.2.0.tgz#8d7de2f1fd52f3ac5a25f54afbb8ac14c62e5663" - integrity sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg== - dependencies: - "@babel/runtime" "^7.23.9" - "@rc-component/portal" "^1.1.1" - classnames "^2.2.6" - rc-motion "^2.6.1" - rc-util "^5.38.1" - -rc-dropdown@~4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.0.tgz#c6052fcfe9c701487b141e411cdc277dc7c6f061" - integrity sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng== - dependencies: - "@babel/runtime" "^7.18.3" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-util "^5.17.0" - -rc-field-form@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.4.0.tgz#26997160d12ae43a94c356c1290bfc011c69b3ca" - integrity sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg== - dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/async-validator" "^5.0.3" - rc-util "^5.32.2" - -rc-image@~7.11.0: - version "7.11.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.0.tgz#18c77ea557a6fdbe26856c688a9aace1505c0e77" - integrity sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/portal" "^1.0.2" - classnames "^2.2.6" - rc-dialog "~9.6.0" - rc-motion "^2.6.2" - rc-util "^5.34.1" - -rc-input-number@~9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.2.0.tgz#7e9344ff054421d2bfff0eebd7c1b8ef22d12220" - integrity sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/mini-decimal" "^1.0.1" - classnames "^2.2.5" - rc-input "~1.6.0" - rc-util "^5.40.1" - -rc-input@~1.6.0, rc-input@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.6.3.tgz#f1708fc3d5e68f95cb20faeb3eed1df8543cd444" - integrity sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.18.1" - -rc-mentions@~2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.16.1.tgz#5e54ebe3ce6cd79838846ff1c8cfaf2e7aa15cec" - integrity sha512-GnhSTGP9Mtv6pqFFGQze44LlrtWOjHNrUUAcsdo9DnNAhN4pwVPEWy4z+2jpjkiGlJ3VoXdvMHcNDQdfI9fEaw== - dependencies: - "@babel/runtime" "^7.22.5" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-input "~1.6.0" - rc-menu "~9.15.1" - rc-textarea "~1.8.0" - rc-util "^5.34.1" - -rc-menu@~9.15.1: - version "9.15.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.15.1.tgz#d8b38ea534a7f596a8da063881519e7eaafca698" - integrity sha512-UKporqU6LPfHnpPmtP6hdEK4iO5Q+b7BRv/uRpxdIyDGplZy9jwUjsnpev5bs3PQKB0H0n34WAPDfjAfn3kAPA== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.0.0" - classnames "2.x" - rc-motion "^2.4.3" - rc-overflow "^1.3.1" - rc-util "^5.27.0" - -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.3: - version "2.9.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.3.tgz#b1bdaf816f1ccb3e4b3b0c531c3037a59286379e" - integrity sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.43.0" - -rc-notification@~5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.2.tgz#8525b32d49dd96ec974acae61d1d1eabde61463a" - integrity sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.9.0" - rc-util "^5.20.1" - -rc-overflow@^1.3.1, rc-overflow@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" - integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-resize-observer "^1.0.0" - rc-util "^5.37.0" - -rc-pagination@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.3.0.tgz#c6022f820aa3a45fd734ae33a2915d39597dce1d" - integrity sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.38.0" - -rc-picker@~4.6.15: - version "4.6.15" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.6.15.tgz#1531c9c382a295e2d1f1f38440d6678b09cd0468" - integrity sha512-OWZ1yrMie+KN2uEUfYCfS4b2Vu6RC1FWwNI0s+qypsc3wRt7g+peuZKVIzXCTaJwyyZruo80+akPg2+GmyiJjw== - dependencies: - "@babel/runtime" "^7.24.7" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.1" - rc-overflow "^1.3.2" - rc-resize-observer "^1.4.0" - rc-util "^5.43.0" - -rc-progress@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" - integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-util "^5.16.1" - -rc-rate@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.0.tgz#642f591ccf55c3a5d84d8d212caf1f7951d203a8" - integrity sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.0.1" - -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" - integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== - dependencies: - "@babel/runtime" "^7.20.7" - classnames "^2.2.1" - rc-util "^5.38.0" - resize-observer-polyfill "^1.5.1" - -rc-segmented@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.5.0.tgz#3b5423adf57459345c77c39c7581fde786a16c11" - integrity sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-motion "^2.4.4" - rc-util "^5.17.0" - -rc-select@~14.15.0, rc-select@~14.15.2: - version "14.15.2" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.15.2.tgz#d85fcf3a708bdf837b003feeed653347b8980ad0" - integrity sha512-oNoXlaFmpqXYcQDzcPVLrEqS2J9c+/+oJuGrlXeVVX/gVgrbHa5YcyiRUXRydFjyuA7GP3elRuLF7Y3Tfwltlw== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.1.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-overflow "^1.3.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.2" - -rc-slider@~11.1.7: - version "11.1.7" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.7.tgz#3de333b1ec84d53a7bda2f816bb4779423628f09" - integrity sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.36.0" - -rc-steps@~6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" - integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== - dependencies: - "@babel/runtime" "^7.16.7" - classnames "^2.2.3" - rc-util "^5.16.1" - -rc-switch@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" - integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== - dependencies: - "@babel/runtime" "^7.21.0" - classnames "^2.2.1" - rc-util "^5.30.0" - -rc-table@~7.47.5: - version "7.47.5" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.47.5.tgz#3c530200baa82346c7e72fe9b1dbd47d4aa15838" - integrity sha512-fzq+V9j/atbPIcvs3emuclaEoXulwQpIiJA6/7ey52j8+9cJ4P8DGmp4YzfUVDrb3qhgedcVeD6eRgUrokwVEQ== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/context" "^1.4.0" - classnames "^2.2.5" - rc-resize-observer "^1.1.0" - rc-util "^5.41.0" - rc-virtual-list "^3.14.2" - -rc-tabs@~15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.3.0.tgz#3fcc332fbb9307d5eb147e0404daca871fb92a89" - integrity sha512-lzE18r+zppT/jZWOAWS6ntdkDUKHOLJzqMi5UAij1LeKwOaQaupupAoI9Srn73GRzVpmGznkECMRrzkRusC40A== - dependencies: - "@babel/runtime" "^7.11.2" - classnames "2.x" - rc-dropdown "~4.2.0" - rc-menu "~9.15.1" - rc-motion "^2.6.2" - rc-resize-observer "^1.0.0" - rc-util "^5.34.1" - -rc-textarea@~1.8.0, rc-textarea@~1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.8.2.tgz#57a6847304551c1883fc3fb0c5076d587f70bf7f" - integrity sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.1" - rc-input "~1.6.0" - rc-resize-observer "^1.0.0" - rc-util "^5.27.0" - -rc-tooltip@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.1.tgz#9a8f0335c86443a0c20c2557933205f645a381b7" - integrity sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.1" - -rc-tree-select@~5.23.0: - version "5.23.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.23.0.tgz#e56da0923c7c11dea98d4e14bb76969283c94468" - integrity sha512-aQGi2tFSRw1WbXv0UVXPzHm09E0cSvUVZMLxQtMv3rnZZpNmdRXWrnd9QkLNlVH31F+X5rgghmdSFF3yZW0N9A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.16.1" - -rc-tree@~5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.9.0.tgz#1835b2bef36cfeb4ec15d62e0319fc503aa485f1" - integrity sha512-CPrgOvm9d/9E+izTONKSngNzQdIEjMox2PBufWjS1wf7vxtvmCWzK1SlpHbRY6IaBfJIeZ+88RkcIevf729cRg== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.1" - -rc-upload@~4.8.1: - version "4.8.1" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.8.1.tgz#ac55f2bc101b95b52a6e47f3c18f0f55b54e16d2" - integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw== - dependencies: - "@babel/runtime" "^7.18.3" - classnames "^2.2.5" - rc-util "^5.2.0" - -rc-util@^5.0.1, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0: - version "5.43.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" - integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== - dependencies: - "@babel/runtime" "^7.18.3" - react-is "^18.2.0" - -rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.14.8" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.14.8.tgz#abf6e8809b7f5c955aa7f59c2a9d57443e9942fd" - integrity sha512-8D0KfzpRYi6YZvlOWIxiOm9BGt4Wf2hQyEaM6RXlDDiY2NhLheuYI+RA+7ZaZj1lq+XQqy3KHlaeeXQfzI5fGg== - dependencies: - "@babel/runtime" "^7.20.0" - classnames "^2.2.6" - rc-resize-observer "^1.0.0" - rc-util "^5.36.0" - react-app-polyfill@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7" @@ -10341,7 +9151,7 @@ react-is@^17.0.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -react-is@^18.0.0, react-is@^18.2.0: +react-is@^18.0.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== @@ -10351,22 +9161,6 @@ react-lifecycles-compat@^3.0.0: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== -react-markdown@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-9.0.1.tgz#c05ddbff67fd3b3f839f8c648e6fb35d022397d1" - integrity sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg== - dependencies: - "@types/hast" "^3.0.0" - devlop "^1.0.0" - hast-util-to-jsx-runtime "^2.0.0" - html-url-attributes "^3.0.0" - mdast-util-to-hast "^13.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - unified "^11.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - react-modal@^3.16.1: version "3.16.1" resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.16.1.tgz#34018528fc206561b1a5467fc3beeaddafb39b2b" @@ -10527,16 +9321,6 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redux-thunk@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" - integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== - -redux@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" - integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== - reflect.getprototypeof@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" @@ -10623,38 +9407,6 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== -remark-html@^16.0.1: - version "16.0.1" - resolved "https://registry.yarnpkg.com/remark-html/-/remark-html-16.0.1.tgz#9246d0cf22254c208a86531cbeb26203ae2dd34c" - integrity sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ== - dependencies: - "@types/mdast" "^4.0.0" - hast-util-sanitize "^5.0.0" - hast-util-to-html "^9.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - -remark-rehype@^11.0.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-11.1.1.tgz#f864dd2947889a11997c0a2667cd6b38f685bca7" - integrity sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - remove-accents@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.5.0.tgz#77991f37ba212afba162e375b627631315bed687" @@ -10686,16 +9438,6 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== -reselect@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" - integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== - -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" @@ -10764,6 +9506,13 @@ rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rimraf@~2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + robust-predicates@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" @@ -10911,13 +9660,6 @@ schema-utils@^4.0.0, schema-utils@^4.2.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -scroll-into-view-if-needed@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" - integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== - dependencies: - compute-scroll-into-view "^3.0.2" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -11100,6 +9842,24 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== +source-map-explorer@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/source-map-explorer/-/source-map-explorer-2.5.3.tgz#33551b51e33b70f56d15e79083cdd4c43e583b69" + integrity sha512-qfUGs7UHsOBE5p/lGfQdaAj/5U/GWYBw2imEpD6UQNkqElYonkow8t+HBL1qqIl3CuGZx7n8/CQo4x1HwSHhsg== + dependencies: + btoa "^1.2.1" + chalk "^4.1.0" + convert-source-map "^1.7.0" + ejs "^3.1.5" + escape-html "^1.0.3" + glob "^7.1.6" + gzip-size "^6.0.0" + lodash "^4.17.20" + open "^7.3.1" + source-map "^0.7.4" + temp "^0.9.4" + yargs "^16.2.0" + source-map-js@^1.0.1, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" @@ -11132,7 +9892,7 @@ source-map@^0.5.7: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.7.3: +source-map@^0.7.3, source-map@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== @@ -11149,11 +9909,6 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -space-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" - integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -11223,11 +9978,6 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" -string-convert@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" - integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -11344,14 +10094,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-entities@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.4.tgz#b3b79ef5f277cc4ac73caeb0236c5ba939b3a4f3" - integrity sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -11412,13 +10154,6 @@ style-loader@^3.3.1: resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== -style-to-object@^1.0.0: - version "1.0.8" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-1.0.8.tgz#67a29bca47eaa587db18118d68f9d95955e81292" - integrity sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g== - dependencies: - inline-style-parser "0.2.4" - styled-components@^6.1.13: version "6.1.13" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.13.tgz#2d777750b773b31469bd79df754a32479e9f475e" @@ -11457,7 +10192,7 @@ stylis@4.3.2: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== -stylis@^4.3.1, stylis@^4.3.3: +stylis@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== @@ -11594,6 +10329,14 @@ temp-dir@^2.0.0: resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg== +temp@^0.9.4: + version "0.9.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" + integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== + dependencies: + mkdirp "^0.5.1" + rimraf "~2.6.2" + tempy@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.6.0.tgz#65e2c35abc06f1124a97f387b08303442bde59f3" @@ -11666,11 +10409,6 @@ throat@^6.0.1: resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== -throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" - integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== - thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -11698,11 +10436,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== - toidentifier@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" @@ -11732,16 +10465,6 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - -trough@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" - integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== - tryer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" @@ -11935,19 +10658,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -unified@^11.0.0: - version "11.0.5" - resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1" - integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -11955,44 +10665,6 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -12103,22 +10775,6 @@ vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - -vfile@^6.0.0: - version "6.0.3" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.3.tgz#3652ab1c496531852bf55a6bac57af981ebc38ab" - integrity sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q== - dependencies: - "@types/unist" "^3.0.0" - vfile-message "^4.0.0" - vscode-jsonrpc@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" @@ -12709,8 +11365,3 @@ zustand@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.1.tgz#2bdca5e4be172539558ce3974fe783174a48fdcf" integrity sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w== - -zwitch@^2.0.0, zwitch@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==