From 3304c8fc289ba1a8ee48c728032ca82be0021b85 Mon Sep 17 00:00:00 2001 From: JFe <33208246+Go-Jaecheol@users.noreply.github.com> Date: Wed, 12 Jul 2023 18:21:08 +0900 Subject: [PATCH 001/185] =?UTF-8?q?[BE]=20feat:=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=84=A4=EA=B3=84=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/funeat/category/domain/Category.java | 21 +++++++++ .../funeat/category/domain/CategoryType.java | 5 +++ .../java/com/funeat/member/domain/Gender.java | 5 +++ .../java/com/funeat/member/domain/Member.java | 45 +++++++++++++++++++ .../domain/bookmark/ProductBookmark.java | 28 ++++++++++++ .../domain/bookmark/RecipeBookmark.java | 28 ++++++++++++ .../domain/favorite/RecipeFavorite.java | 28 ++++++++++++ .../domain/favorite/ReviewFavorite.java | 28 ++++++++++++ .../com/funeat/product/domain/Product.java | 40 +++++++++++++++++ .../funeat/product/domain/ProductRecipe.java | 25 +++++++++++ .../java/com/funeat/recipe/domain/Recipe.java | 39 ++++++++++++++++ .../com/funeat/recipe/domain/RecipeImage.java | 22 +++++++++ .../java/com/funeat/review/domain/Review.java | 43 ++++++++++++++++++ .../com/funeat/review/domain/ReviewTag.java | 25 +++++++++++ .../main/java/com/funeat/tag/domain/Tag.java | 22 +++++++++ 15 files changed, 404 insertions(+) create mode 100644 backend/src/main/java/com/funeat/category/domain/Category.java create mode 100644 backend/src/main/java/com/funeat/category/domain/CategoryType.java create mode 100644 backend/src/main/java/com/funeat/member/domain/Gender.java create mode 100644 backend/src/main/java/com/funeat/member/domain/Member.java create mode 100644 backend/src/main/java/com/funeat/member/domain/bookmark/ProductBookmark.java create mode 100644 backend/src/main/java/com/funeat/member/domain/bookmark/RecipeBookmark.java create mode 100644 backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java create mode 100644 backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java create mode 100644 backend/src/main/java/com/funeat/product/domain/Product.java create mode 100644 backend/src/main/java/com/funeat/product/domain/ProductRecipe.java create mode 100644 backend/src/main/java/com/funeat/recipe/domain/Recipe.java create mode 100644 backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java create mode 100644 backend/src/main/java/com/funeat/review/domain/Review.java create mode 100644 backend/src/main/java/com/funeat/review/domain/ReviewTag.java create mode 100644 backend/src/main/java/com/funeat/tag/domain/Tag.java diff --git a/backend/src/main/java/com/funeat/category/domain/Category.java b/backend/src/main/java/com/funeat/category/domain/Category.java new file mode 100644 index 00000000..90f079e3 --- /dev/null +++ b/backend/src/main/java/com/funeat/category/domain/Category.java @@ -0,0 +1,21 @@ +package com.funeat.category.domain; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Category { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @Enumerated(EnumType.STRING) + private CategoryType type; +} diff --git a/backend/src/main/java/com/funeat/category/domain/CategoryType.java b/backend/src/main/java/com/funeat/category/domain/CategoryType.java new file mode 100644 index 00000000..ecd06d7d --- /dev/null +++ b/backend/src/main/java/com/funeat/category/domain/CategoryType.java @@ -0,0 +1,5 @@ +package com.funeat.category.domain; + +public enum CategoryType { + FOOD, STORE +} diff --git a/backend/src/main/java/com/funeat/member/domain/Gender.java b/backend/src/main/java/com/funeat/member/domain/Gender.java new file mode 100644 index 00000000..12e14d59 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/Gender.java @@ -0,0 +1,5 @@ +package com.funeat.member.domain; + +public enum Gender { + MALE, FEMALE +} diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java new file mode 100644 index 00000000..27adbc3a --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -0,0 +1,45 @@ +package com.funeat.member.domain; + +import com.funeat.member.domain.bookmark.ProductBookmark; +import com.funeat.member.domain.bookmark.RecipeBookmark; +import com.funeat.member.domain.favorite.RecipeFavorite; +import com.funeat.member.domain.favorite.ReviewFavorite; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity +public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String nickname; + + private String profileImage; + + private Integer age; + + @Enumerated(EnumType.STRING) + private Gender gender; + + private String phoneNumber; + + @OneToMany(mappedBy = "member") + private List reviewFavorites; + + @OneToMany(mappedBy = "member") + private List recipeFavorites; + + @OneToMany(mappedBy = "member") + private List productBookmarks; + + @OneToMany(mappedBy = "member") + private List recipeBookmarks; +} diff --git a/backend/src/main/java/com/funeat/member/domain/bookmark/ProductBookmark.java b/backend/src/main/java/com/funeat/member/domain/bookmark/ProductBookmark.java new file mode 100644 index 00000000..c18c84b5 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/bookmark/ProductBookmark.java @@ -0,0 +1,28 @@ +package com.funeat.member.domain.bookmark; + +import com.funeat.member.domain.Member; +import com.funeat.product.domain.Product; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class ProductBookmark { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne + @JoinColumn(name = "product_id") + private Product product; + + private Boolean checked; +} diff --git a/backend/src/main/java/com/funeat/member/domain/bookmark/RecipeBookmark.java b/backend/src/main/java/com/funeat/member/domain/bookmark/RecipeBookmark.java new file mode 100644 index 00000000..9dc0b75a --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/bookmark/RecipeBookmark.java @@ -0,0 +1,28 @@ +package com.funeat.member.domain.bookmark; + +import com.funeat.member.domain.Member; +import com.funeat.recipe.domain.Recipe; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class RecipeBookmark { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne + @JoinColumn(name = "recipe_id") + private Recipe recipe; + + private Boolean checked; +} diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java new file mode 100644 index 00000000..cc526247 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/favorite/RecipeFavorite.java @@ -0,0 +1,28 @@ +package com.funeat.member.domain.favorite; + +import com.funeat.member.domain.Member; +import com.funeat.recipe.domain.Recipe; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class RecipeFavorite { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne + @JoinColumn(name = "recipe_id") + private Recipe recipe; + + private Boolean checked; +} diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java new file mode 100644 index 00000000..afa23cf3 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java @@ -0,0 +1,28 @@ +package com.funeat.member.domain.favorite; + +import com.funeat.member.domain.Member; +import com.funeat.review.domain.Review; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class ReviewFavorite { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne + @JoinColumn(name = "review_id") + private Review review; + + private Boolean checked; +} diff --git a/backend/src/main/java/com/funeat/product/domain/Product.java b/backend/src/main/java/com/funeat/product/domain/Product.java new file mode 100644 index 00000000..1a17856d --- /dev/null +++ b/backend/src/main/java/com/funeat/product/domain/Product.java @@ -0,0 +1,40 @@ +package com.funeat.product.domain; + +import com.funeat.category.domain.Category; +import com.funeat.member.domain.bookmark.ProductBookmark; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +@Entity +public class Product { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private Long price; + + private String image; + + private String content; + + private Double averageRating; + + @ManyToOne + @JoinColumn(name = "category_id") + private Category category; + + @OneToMany(mappedBy = "product") + private List productRecipes; + + @OneToMany(mappedBy = "product") + private List productBookmarks; +} diff --git a/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java b/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java new file mode 100644 index 00000000..0d55845e --- /dev/null +++ b/backend/src/main/java/com/funeat/product/domain/ProductRecipe.java @@ -0,0 +1,25 @@ +package com.funeat.product.domain; + +import com.funeat.recipe.domain.Recipe; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class ProductRecipe { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "product_id") + private Product product; + + @ManyToOne + @JoinColumn(name = "recipe_id") + private Recipe recipe; +} diff --git a/backend/src/main/java/com/funeat/recipe/domain/Recipe.java b/backend/src/main/java/com/funeat/recipe/domain/Recipe.java new file mode 100644 index 00000000..b52c125b --- /dev/null +++ b/backend/src/main/java/com/funeat/recipe/domain/Recipe.java @@ -0,0 +1,39 @@ +package com.funeat.recipe.domain; + +import com.funeat.member.domain.Member; +import com.funeat.member.domain.bookmark.RecipeBookmark; +import com.funeat.member.domain.favorite.RecipeFavorite; +import com.funeat.product.domain.ProductRecipe; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +@Entity +public class Recipe { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private String content; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @OneToMany(mappedBy = "recipe") + private List productRecipes; + + @OneToMany(mappedBy = "recipe") + private List recipeFavorites; + + @OneToMany(mappedBy = "recipe") + private List recipeBookmarks; +} diff --git a/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java b/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java new file mode 100644 index 00000000..c3bf4ec0 --- /dev/null +++ b/backend/src/main/java/com/funeat/recipe/domain/RecipeImage.java @@ -0,0 +1,22 @@ +package com.funeat.recipe.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class RecipeImage { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String image; + + @ManyToOne + @JoinColumn(name = "recipe_id") + private Recipe recipe; +} diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java new file mode 100644 index 00000000..a355e09b --- /dev/null +++ b/backend/src/main/java/com/funeat/review/domain/Review.java @@ -0,0 +1,43 @@ +package com.funeat.review.domain; + +import com.funeat.member.domain.Member; +import com.funeat.member.domain.favorite.ReviewFavorite; +import com.funeat.product.domain.Product; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; + +@Entity +public class Review { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String image; + + private Double rating; + + private String content; + + private Boolean reBuy; + + @ManyToOne + @JoinColumn(name = "product_id") + private Product product; + + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + @OneToMany(mappedBy = "review") + private List reviewTags; + + @OneToMany(mappedBy = "review") + private List reviewFavorites; +} diff --git a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java new file mode 100644 index 00000000..3f748ad4 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java @@ -0,0 +1,25 @@ +package com.funeat.review.domain; + +import com.funeat.tag.domain.Tag; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +@Entity +public class ReviewTag { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "review_id") + private Review review; + + @ManyToOne + @JoinColumn(name = "tag_id") + private Tag tag; +} diff --git a/backend/src/main/java/com/funeat/tag/domain/Tag.java b/backend/src/main/java/com/funeat/tag/domain/Tag.java new file mode 100644 index 00000000..f1f8e0d6 --- /dev/null +++ b/backend/src/main/java/com/funeat/tag/domain/Tag.java @@ -0,0 +1,22 @@ +package com.funeat.tag.domain; + +import com.funeat.review.domain.ReviewTag; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.OneToMany; + +@Entity +public class Tag { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @OneToMany(mappedBy = "tag") + private List reviewTags; +} From da57de0d5a07d8e07a94221ec38c7af603e9bac9 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Thu, 13 Jul 2023 13:04:07 +0900 Subject: [PATCH 002/185] =?UTF-8?q?[FE]=20chore:=20storybook=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98=20=EB=B0=8F=20=EC=9B=B9=ED=8C=A9=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leejin Yang Co-authored-by: sᴏʟʙɪ ☔️ --- frontend/.babelrc.json | 16 + frontend/.eslintrc.js | 59 +- frontend/.storybook/main.ts | 43 + frontend/.storybook/preview.ts | 15 + frontend/package.json | 16 +- frontend/yarn.lock | 8935 ++++++++++++++++++++++++++++++++ 6 files changed, 9048 insertions(+), 36 deletions(-) create mode 100644 frontend/.babelrc.json create mode 100644 frontend/.storybook/main.ts create mode 100644 frontend/.storybook/preview.ts create mode 100644 frontend/yarn.lock diff --git a/frontend/.babelrc.json b/frontend/.babelrc.json new file mode 100644 index 00000000..b5cf683b --- /dev/null +++ b/frontend/.babelrc.json @@ -0,0 +1,16 @@ +{ + "sourceType": "unambiguous", + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "chrome": 100 + } + } + ], + "@babel/preset-typescript", + "@babel/preset-react" + ], + "plugins": [] +} \ No newline at end of file diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index c192e697..f4307332 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,51 +1,40 @@ module.exports = { env: { browser: true, - es2021: true, + es2021: true }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended', 'plugin:storybook/recommended'], ignorePatterns: ['*.js'], - overrides: [ - { - env: { - node: true, - }, - files: ['.eslintrc.{js,cjs}'], - parserOptions: { - sourceType: 'script', - }, + overrides: [{ + env: { + node: true }, - ], + files: ['.eslintrc.{js,cjs}'], + parserOptions: { + sourceType: 'script' + } + }], parser: '@typescript-eslint/parser', parserOptions: { ecmaVersion: 'latest', sourceType: 'module', project: './tsconfig.json', - tsconfigRootDir: __dirname, + tsconfigRootDir: __dirname }, plugins: ['@typescript-eslint', 'react'], rules: { 'react/react-in-jsx-scope': 'off', '@typescript-eslint/no-var-requires': 0, - '@typescript-eslint/consistent-type-imports': [ - 'error', - { - prefer: 'type-imports', - disallowTypeAnnotations: false, - }, - ], - 'react/jsx-key': [ - 'error', - { - warnOnDuplicates: true, - }, - ], - 'react/self-closing-comp': [ - 'error', - { - component: true, - html: true, - }, - ], - }, -}; + '@typescript-eslint/consistent-type-imports': ['error', { + prefer: 'type-imports', + disallowTypeAnnotations: false + }], + 'react/jsx-key': ['error', { + warnOnDuplicates: true + }], + 'react/self-closing-comp': ['error', { + component: true, + html: true + }] + } +}; \ No newline at end of file diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts new file mode 100644 index 00000000..01d201a5 --- /dev/null +++ b/frontend/.storybook/main.ts @@ -0,0 +1,43 @@ +import type { StorybookConfig } from '@storybook/react-webpack5'; +import path from 'path'; + +const config: StorybookConfig = { + stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], + addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'], + framework: { + name: '@storybook/react-webpack5', + options: {}, + }, + core: { + builder: { + name: '@storybook/builder-webpack5', + options: { + fsCache: true, + lazyCompilation: true, + }, + }, + }, + webpackFinal: async (config) => { + if (config.resolve) { + config.resolve.alias = { + ...config.resolve.alias, + '@': path.resolve(__dirname, '../src'), + '@apis': path.resolve(__dirname, '../src/apis'), + '@assets': path.resolve(__dirname, '../src/assets'), + '@components': path.resolve(__dirname, '../src/components'), + '@constants': path.resolve(__dirname, '../src/constants'), + '@hooks': path.resolve(__dirname, '../src/hooks'), + '@mocks': path.resolve(__dirname, '../src/mocks'), + '@pages': path.resolve(__dirname, '../src/pages'), + '@router': path.resolve(__dirname, '../src/router'), + '@styles': path.resolve(__dirname, '../src/styles'), + '@utils': path.resolve(__dirname, '../src/utils'), + }; + } + return config; + }, + docs: { + autodocs: true, + }, +}; +export default config; diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts new file mode 100644 index 00000000..8c2a141c --- /dev/null +++ b/frontend/.storybook/preview.ts @@ -0,0 +1,15 @@ +import type { Preview } from '@storybook/react'; + +const preview: Preview = { + parameters: { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + }, +}; + +export default preview; diff --git a/frontend/package.json b/frontend/package.json index 73b4dbb5..25bfbb1c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,9 @@ "scripts": { "start": "webpack serve --open --config webpack.dev.js", "build": "webpack --config webpack.prod.js", - "build-dev": "webpack --config webpack.dev.js" + "build-dev": "webpack --config webpack.dev.js", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build" }, "dependencies": { "react": "^18.2.0", @@ -14,6 +16,16 @@ "styled-components": "^6.0.2" }, "devDependencies": { + "@babel/preset-env": "^7.22.9", + "@babel/preset-react": "^7.22.5", + "@babel/preset-typescript": "^7.22.5", + "@storybook/addon-essentials": "^7.0.27", + "@storybook/addon-interactions": "^7.0.27", + "@storybook/addon-links": "^7.0.27", + "@storybook/blocks": "^7.0.27", + "@storybook/react": "^7.0.27", + "@storybook/react-webpack5": "^7.0.27", + "@storybook/testing-library": "^0.0.14-next.2", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "@types/styled-components": "^5.1.26", @@ -21,8 +33,10 @@ "@typescript-eslint/parser": "^5.60.1", "eslint": "^8.44.0", "eslint-plugin-react": "^7.32.2", + "eslint-plugin-storybook": "^0.6.12", "html-webpack-plugin": "^5.5.3", "prettier": "^2.8.8", + "storybook": "^7.0.27", "ts-loader": "^9.4.4", "typescript": "^5.1.6", "webpack": "^5.88.1", diff --git a/frontend/yarn.lock b/frontend/yarn.lock new file mode 100644 index 00000000..b99110a2 --- /dev/null +++ b/frontend/yarn.lock @@ -0,0 +1,8935 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@aw-web-design/x-default-browser@1.4.88": + version "1.4.88" + resolved "https://registry.yarnpkg.com/@aw-web-design/x-default-browser/-/x-default-browser-1.4.88.tgz#33d869cb2a537cd6d2a8369d4dc8ea4988d4be89" + integrity sha512-AkEmF0wcwYC2QkhK703Y83fxWARttIWXDmQN8+cof8FmFZ5BRhnNXGymeb1S73bOCLfWjYELxtujL56idCN/XA== + dependencies: + default-browser-id "3.0.0" + +"@babel/cli@^7.21.0": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.9.tgz#501b3614aeda7399371f6d5991404f069b059986" + integrity sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + commander "^4.0.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.1.0" + glob "^7.2.0" + make-dir "^2.1.0" + slash "^2.0.0" + optionalDependencies: + "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" + chokidar "^3.4.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5", "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" + integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== + +"@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.2", "@babel/core@^7.21.0", "@babel/core@^7.7.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" + integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-module-transforms" "^7.22.9" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.1" + +"@babel/core@~7.21.0": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" + integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helpers" "^7.21.5" + "@babel/parser" "^7.21.8" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.12.11", "@babel/generator@^7.21.5", "@babel/generator@^7.22.7", "@babel/generator@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" + integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/generator@~7.21.1": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.9.tgz#3a1b706e07d836e204aee0650e8ee878d3aaa241" + integrity sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg== + dependencies: + "@babel/types" "^7.21.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" + integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-validator-option" "^7.22.5" + browserslist "^4.21.9" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5", "@babel/helper-create-class-features-plugin@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" + integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" + integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + regexpu-core "^5.3.1" + semver "^6.3.1" + +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-define-polyfill-provider@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz#af1429c4a83ac316a6a8c2cc8ff45cb5d2998d3a" + integrity sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A== + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5", "@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + +"@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" + integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" + integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.9" + +"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" + integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.21.5", "@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== + +"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== + +"@babel/helper-validator-option@^7.21.0", "@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + +"@babel/helper-wrap-function@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" + integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helpers@^7.21.5", "@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.5", "@babel/parser@^7.21.8", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== + +"@babel/parser@~7.21.2": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.9.tgz#ab18ea3b85b4bc33ba98a8d4c2032c557d23cf14" + integrity sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" + +"@babel/plugin-external-helpers@^7.18.6": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-external-helpers/-/plugin-external-helpers-7.22.5.tgz#92b0705b74756123f289388320e0e12c407fdf9a" + integrity sha512-ngnNEWxmykPk82mH4ajZT0qTztr3Je6hrMuKAslZVM8G1YZTENJSYwrIGtt6KOtznug3exmAtF4so/nPqJuA4A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c" + integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" + integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-assertions@^7.20.0", "@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + integrity sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.21.5", "@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b" + integrity sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg== + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.20.7", "@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6", "@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.21.0", "@babel/plugin-transform-block-scoping@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" + integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.21.0", "@babel/plugin-transform-classes@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.21.5", "@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" + +"@babel/plugin-transform-destructuring@^7.21.3", "@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.18.9", "@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" + integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6", "@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" + integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-flow-strip-types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz#0bb17110c7bf5b35a60754b2f00c58302381dee2" + integrity sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.22.5" + +"@babel/plugin-transform-for-of@^7.21.5", "@babel/plugin-transform-for-of@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.18.9", "@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== + dependencies: + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" + integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.18.9", "@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" + integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.18.6", "@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.20.11", "@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.21.5", "@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.20.11", "@babel/plugin-transform-modules-systemjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/plugin-transform-modules-umd@^7.18.6", "@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5", "@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.18.6", "@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" + integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" + integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" + integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.5" + +"@babel/plugin-transform-object-super@^7.18.6", "@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + +"@babel/plugin-transform-optional-catch-binding@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" + integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" + integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3", "@babel/plugin-transform-parameters@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" + integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.18.6", "@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-display-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.19.0", "@babel/plugin-transform-react-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/plugin-transform-react-pure-annotations@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz#1f58363eef6626d6fa517b95ac66fe94685e32c0" + integrity sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.21.5", "@babel/plugin-transform-regenerator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.18.6", "@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-shorthand-properties@^7.18.6", "@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.20.7", "@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.18.6", "@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.18.9", "@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.18.9", "@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typescript@^7.22.5": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz#91e08ad1eb1028ecc62662a842e93ecfbf3c7234" + integrity sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.22.5" + +"@babel/plugin-transform-unicode-escapes@^7.21.5", "@babel/plugin-transform-unicode-escapes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.18.6", "@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.20.2", "@babel/preset-env@^7.22.9": + version "7.22.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.9.tgz#57f17108eb5dfd4c5c25a44c1977eba1df310ac7" + integrity sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g== + dependencies: + "@babel/compat-data" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.7" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-classes" "^7.22.6" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" + "@babel/plugin-transform-numeric-separator" "^7.22.5" + "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.6" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.22.5" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + core-js-compat "^3.31.0" + semver "^6.3.1" + +"@babel/preset-env@~7.21.0": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" + integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== + dependencies: + "@babel/compat-data" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-to-generator" "^7.20.7" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.21.5" + "@babel/plugin-transform-destructuring" "^7.21.3" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.21.5" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.21.5" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.20.7" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.21.5" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + +"@babel/preset-flow@^7.13.13", "@babel/preset-flow@^7.18.6": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.22.5.tgz#876f24ab6b38bd79703a93f32020ca2162312784" + integrity sha512-ta2qZ+LSiGCrP5pgcGt8xMnnkXQrq8Sa4Ulhy06BOlF5QbLw9q5hIx7bn5MrsvyTGAfh6kTOo07Q+Pfld/8Y5Q== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-transform-flow-strip-types" "^7.22.5" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.18.6", "@babel/preset-react@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.5.tgz#c4d6058fbf80bccad02dd8c313a9aaa67e3c3dd6" + integrity sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-transform-react-display-name" "^7.22.5" + "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.22.5" + +"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.21.0", "@babel/preset-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" + integrity sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-typescript" "^7.22.5" + +"@babel/register@^7.13.16": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939" + integrity sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.5" + source-map-support "^0.5.16" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.20.7", "@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.1.6", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.5", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@~7.21.2": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.5", "@babel/types@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + +"@babel/types@~7.21.2": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@base2/pretty-print-object@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" + integrity sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA== + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0", "@discoveryjs/json-ext@^0.5.3": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@emotion/is-prop-valid@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" + integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== + dependencies: + "@emotion/memoize" "^0.8.1" + +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17" + integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA== + +"@emotion/unitless@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" + integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz#08de79f54eb3406f9daaf77c76e35313da963963" + integrity sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw== + +"@esbuild/android-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" + integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA== + +"@esbuild/android-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d" + integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A== + +"@esbuild/android-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" + integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww== + +"@esbuild/darwin-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" + integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg== + +"@esbuild/darwin-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" + integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw== + +"@esbuild/freebsd-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" + integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ== + +"@esbuild/freebsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" + integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ== + +"@esbuild/linux-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" + integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg== + +"@esbuild/linux-arm@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" + integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA== + +"@esbuild/linux-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" + integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ== + +"@esbuild/linux-loong64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72" + integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ== + +"@esbuild/linux-mips64el@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" + integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A== + +"@esbuild/linux-ppc64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" + integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg== + +"@esbuild/linux-riscv64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" + integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA== + +"@esbuild/linux-s390x@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" + integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q== + +"@esbuild/linux-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" + integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw== + +"@esbuild/netbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" + integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q== + +"@esbuild/openbsd-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" + integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g== + +"@esbuild/sunos-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" + integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg== + +"@esbuild/win32-arm64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" + integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag== + +"@esbuild/win32-ia32@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" + integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw== + +"@esbuild/win32-x64@0.17.19": + version "0.17.19" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" + integrity sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA== + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== + +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== + +"@fal-works/esbuild-plugin-global-externals@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" + integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== + +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/transform@^29.3.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.6.1.tgz#acb5606019a197cb99beda3c05404b851f441c92" + integrity sha512-URnTneIU3ZjRSaf906cvf6Hpox3hIeJXRnz3VDSw5/X93gR8ycdfSIEy19FlVx8NFmpN7fe3Gb1xF+NjXaQLWg== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.6.1" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== + dependencies: + "@jest/schemas" "^29.6.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@juggle/resize-observer@^3.3.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" + integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/react@^2.1.5": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.3.0.tgz#4208bd6d70f0d0831def28ef28c26149b03180b3" + integrity sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g== + dependencies: + "@types/mdx" "^2.0.0" + "@types/react" ">=16" + +"@ndelangen/get-tarball@^3.0.7": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz#727ff4454e65f34707e742a59e5e6b1f525d8964" + integrity sha512-9JKTEik4vq+yGosHYhZ1tiH/3WpUS0Nh0kej4Agndhox8pAdWhEx5knFVRcb/ya9knCRCs1rPxNrSXTDdfVqpA== + dependencies: + gunzip-maybe "^1.4.2" + pump "^3.0.0" + tar-fs "^2.1.1" + +"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": + version "2.1.8-no-fsevents.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" + integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== + +"@nicolo-ribaudo/semver-v6@^6.3.3": + version "6.3.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" + integrity sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.5": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" + integrity sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA== + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^3.0.0" + source-map "^0.7.3" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@storybook/addon-actions@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-7.0.27.tgz#d130c8a64fd6e52803cad20b82c3b9cbbc05eca1" + integrity sha512-bDN7rxdEBfcgV+LJWpmd26RdblODIPFaR+UMLVIITLP2ZxSjJ5yCcDenKDvSZJCPLhDnDcyiUmNcyvRtdmWf0w== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + dequal "^2.0.2" + lodash "^4.17.21" + polished "^4.2.2" + prop-types "^15.7.2" + react-inspector "^6.0.0" + telejson "^7.0.3" + ts-dedent "^2.0.0" + uuid "^9.0.0" + +"@storybook/addon-backgrounds@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-7.0.27.tgz#3229bbb9d0cf5dfcd7a342298d69e90663e6a996" + integrity sha512-ujhlekvYirsEmRgLhKM8MtRHnG3ZBwkHKV7bj+BNl6YP39MB3SWlDqS9igRaoZhXvL1yIIbvtLkebaYBAL01dw== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + memoizerific "^1.11.3" + ts-dedent "^2.0.0" + +"@storybook/addon-controls@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-7.0.27.tgz#af6f3445eaaf20a6b96738536d4f46f51a40eab0" + integrity sha512-wONLfJ4x6gbuSGxkK54QDGFI2/pd3K32ukpp2rXV6DyyRzrjal3RQdLZYzSppEfDqxrmPTFuGiw7J7w0BLJ5TQ== + dependencies: + "@storybook/blocks" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/manager-api" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + lodash "^4.17.21" + ts-dedent "^2.0.0" + +"@storybook/addon-docs@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-7.0.27.tgz#c9c5bf27fa8d43de606841f42850d2869902ba07" + integrity sha512-Q7JbvpejyDVHl/ZS7uHBmgdX+GFznZ042ohPL6a8+vInET2L0u6iXKRz8ZUkvaGPs8NniN9fNkf62Xmw7x2EMQ== + dependencies: + "@babel/core" "^7.20.2" + "@babel/plugin-transform-react-jsx" "^7.19.0" + "@jest/transform" "^29.3.1" + "@mdx-js/react" "^2.1.5" + "@storybook/blocks" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/csf-plugin" "7.0.27" + "@storybook/csf-tools" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/mdx2-csf" "^1.0.0" + "@storybook/node-logger" "7.0.27" + "@storybook/postinstall" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/react-dom-shim" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + fs-extra "^11.1.0" + remark-external-links "^8.0.0" + remark-slug "^6.0.0" + ts-dedent "^2.0.0" + +"@storybook/addon-essentials@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-7.0.27.tgz#9478bc69489634cccc37e34e72ba67ca78b3f848" + integrity sha512-3A5XHrxO+B7oNb/vZCV784Sb1a89OjQZGT5+LdW3vvwcuHMoQy0hXie7g0CVZEbG0qqfUMVmGuDlRCLuexsWog== + dependencies: + "@storybook/addon-actions" "7.0.27" + "@storybook/addon-backgrounds" "7.0.27" + "@storybook/addon-controls" "7.0.27" + "@storybook/addon-docs" "7.0.27" + "@storybook/addon-highlight" "7.0.27" + "@storybook/addon-measure" "7.0.27" + "@storybook/addon-outline" "7.0.27" + "@storybook/addon-toolbars" "7.0.27" + "@storybook/addon-viewport" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/manager-api" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + ts-dedent "^2.0.0" + +"@storybook/addon-highlight@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-7.0.27.tgz#af9da441ef0c1bb6e8ce9f37b3fc66d520657932" + integrity sha512-Lfiv0yeETF0pPyyN9lg4YXwLbEZXOOEzSkrXtBPgtrfhK/pfEBE5SUK4hmKy1droq1dEZhO52dxNUhg6y8GdWg== + dependencies: + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/preview-api" "7.0.27" + +"@storybook/addon-interactions@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-7.0.27.tgz#528aec33e40d4dacd07702c086f06b7effa6c9f2" + integrity sha512-0pt9tWqAqMQpHDS7hglcz0kpyu5KsP/51squflZkY5GhItXEY9IFxoBZ4Ttounp//2z/pj2iQW0dPE5WJpwTrw== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/instrumenter" "7.0.27" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + jest-mock "^27.0.6" + polished "^4.2.2" + ts-dedent "^2.2.0" + +"@storybook/addon-links@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-7.0.27.tgz#a7db79b0b9fba97cd4e3dd3ca89fadc580427e7a" + integrity sha512-htnnP4VMvtuDAebd+fDDTrsZ6C6q8etag9+5rGhd/8I9NNHn6OZpAZONCk2uwqOOIS2PKQd/qmUwDz/yT2kcmQ== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/router" "7.0.27" + "@storybook/types" "7.0.27" + prop-types "^15.7.2" + ts-dedent "^2.0.0" + +"@storybook/addon-measure@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-7.0.27.tgz#e414671fdc9be10174e87fe938264c5c772116b8" + integrity sha512-ffwVgENUwoiG4vLniTxNV6Uw2dfLz7TkbIivAb+Z+OpkSfwu+2EXCt0shhoVAGfdrGSoaIij2TWabegd0jpUeQ== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/types" "7.0.27" + +"@storybook/addon-outline@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-7.0.27.tgz#5b59c1c4cdfa2f697d4f7280c2b76d865aa6956d" + integrity sha512-t4uSaeUN8M4LIx7pevub8MZBPzpTfXyjzpdkEhTNqFRccGPqhtL56i++lbRviRbNWAHmBP3pswudxSl97/1dBA== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/types" "7.0.27" + ts-dedent "^2.0.0" + +"@storybook/addon-toolbars@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-7.0.27.tgz#377d4ee8214561f6e174e2d68207d589f8e9f0bc" + integrity sha512-Zz7B/T9l+Eyvh7jYO+t4Fwdq2N8mVHkklztCSWz5gk/VE3cFttku3+PjPithdOXVbpqbux8HC8lDDS5KnQuurA== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + +"@storybook/addon-viewport@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-7.0.27.tgz#a4fa6ead643293d0a684bbd3b061f7f588e14144" + integrity sha512-evU1b7DT8yUR47ZhfLC255NPlxgupEVOcAtwL+8aQEp3uhff+nYXOEN8u/fd3ZTKs0i37FRyNdk5FOMk18RykQ== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + memoizerific "^1.11.3" + prop-types "^15.7.2" + +"@storybook/addons@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-7.0.27.tgz#b7aa5ab0e8de621187ca8fc9ee14a35d9d8355c9" + integrity sha512-LGfd8OAwS+zl7qQyLSAg/JjkfDDyf2uhwZIMYHomv3Oow/KT8kPqAdLqmsuAYBrTFBEqX3duemdHgjG7lVv9qQ== + dependencies: + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/types" "7.0.27" + +"@storybook/api@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-7.0.27.tgz#7916533fab68dc997a972b3dc5727054ebae4bb8" + integrity sha512-kvqtnahIdyp+c7qwG/IhY6e1ynet/G9k92J6n3UEpMqy0b+jKMpGE45uGdiMg5EDVGjvlDqN8Ed7v/ZDJFjlOw== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/manager-api" "7.0.27" + +"@storybook/blocks@7.0.27", "@storybook/blocks@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-7.0.27.tgz#cf83b3304fd387e7a009c96fffb038ca2c9eff0c" + integrity sha512-6EXUWS1DjI68HXHFilaav9/sAqLKZKNBaVdhIHoRfB3lJ29MzxQe1k5BN+JRnUQE9cKC/F5XuP9y2pg7P1Y6CQ== + dependencies: + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/docs-tools" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + "@types/lodash" "^4.14.167" + color-convert "^2.0.1" + dequal "^2.0.2" + lodash "^4.17.21" + markdown-to-jsx "^7.1.8" + memoizerific "^1.11.3" + polished "^4.2.2" + react-colorful "^5.1.2" + telejson "^7.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/builder-manager@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/builder-manager/-/builder-manager-7.0.27.tgz#a46b6d5b3539671d910978695f86cb2b7b7583ac" + integrity sha512-KDhBAx8Ib1nnAoB3Lm9kGo2QwBbxwFbonbB0otfT0hGhLSTKllHRYx3WL24bqibI9a87Jt1RT913PZusQ5up4w== + dependencies: + "@fal-works/esbuild-plugin-global-externals" "^2.1.2" + "@storybook/core-common" "7.0.27" + "@storybook/manager" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@types/ejs" "^3.1.1" + "@types/find-cache-dir" "^3.2.1" + "@yarnpkg/esbuild-plugin-pnp" "^3.0.0-rc.10" + browser-assert "^1.2.1" + ejs "^3.1.8" + esbuild "^0.17.0" + esbuild-plugin-alias "^0.2.1" + express "^4.17.3" + find-cache-dir "^3.0.0" + fs-extra "^11.1.0" + process "^0.11.10" + util "^0.12.4" + +"@storybook/builder-webpack5@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-7.0.27.tgz#405585d90d79eb60006b739ddf9a78e07303186f" + integrity sha512-N6CZflMuUCR23ZXzz8oNRFJLL/SGguQxwlibcw0jpdUZnXREz+6Dr3ADIrTqZGeNVxM4MkLxHfRjhi4Bkmc3wQ== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/addons" "7.0.27" + "@storybook/api" "7.0.27" + "@storybook/channel-postmessage" "7.0.27" + "@storybook/channel-websocket" "7.0.27" + "@storybook/channels" "7.0.27" + "@storybook/client-api" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/components" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/core-webpack" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/manager-api" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/preview" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/router" "7.0.27" + "@storybook/store" "7.0.27" + "@storybook/theming" "7.0.27" + "@types/node" "^16.0.0" + "@types/semver" "^7.3.4" + babel-loader "^9.0.0" + babel-plugin-named-exports-order "^0.0.2" + browser-assert "^1.2.1" + case-sensitive-paths-webpack-plugin "^2.4.0" + css-loader "^6.7.1" + express "^4.17.3" + fork-ts-checker-webpack-plugin "^7.2.8" + fs-extra "^11.1.0" + html-webpack-plugin "^5.5.0" + path-browserify "^1.0.1" + process "^0.11.10" + semver "^7.3.7" + style-loader "^3.3.1" + terser-webpack-plugin "^5.3.1" + ts-dedent "^2.0.0" + util "^0.12.4" + util-deprecate "^1.0.2" + webpack "5" + webpack-dev-middleware "^5.3.1" + webpack-hot-middleware "^2.25.1" + webpack-virtual-modules "^0.4.3" + +"@storybook/channel-postmessage@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-7.0.27.tgz#b73b2273e19568d2a7e9c0859a23d128d41279c2" + integrity sha512-ScpiStUHvtgy9RrCFNyzzH9l+zHF80lSwW/BZ1MRETJ9ZaOVPrm03U0Ju01wJC57DYPROwPU/wKMetNqKKEhdA== + dependencies: + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + qs "^6.10.0" + telejson "^7.0.3" + +"@storybook/channel-websocket@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-7.0.27.tgz#e2967409c54014d3132f3f3f41999a24e0ac5b4f" + integrity sha512-5WZmd5cd54HYa1WMWN694o266HpvWvGj9XC17DD+DwVARnWRxBmFnZs+X2FE68rGzccjD2cAJXyDTFHrcS+U1g== + dependencies: + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/global" "^5.0.0" + telejson "^7.0.3" + +"@storybook/channels@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-7.0.27.tgz#3dc577c45d57406546ec02d92c22ebfa6acc5d6b" + integrity sha512-YppvPa1qMyC+oCQJ3tf7Quzpf2NnBlvIRLPJiGAMssUwX5qE0iKe9lTtkNwMaNxEvzz6rDxewSlz+f/MWr4gPw== + +"@storybook/cli@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/cli/-/cli-7.0.27.tgz#71f069a1ea523dd43e706be026f09d9caa130b81" + integrity sha512-iHugKuE3Rw/QdFSJBCJQYaZJsnEAQtFLf9vYNRjEqmkif5AR0leZj4yQ5kV1OfQ8MRuh+FGQ/u1cz6fRsFiWEA== + dependencies: + "@babel/core" "^7.20.2" + "@babel/preset-env" "^7.20.2" + "@ndelangen/get-tarball" "^3.0.7" + "@storybook/codemod" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/core-server" "7.0.27" + "@storybook/csf-tools" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/telemetry" "7.0.27" + "@storybook/types" "7.0.27" + "@types/semver" "^7.3.4" + chalk "^4.1.0" + commander "^6.2.1" + cross-spawn "^7.0.3" + detect-indent "^6.1.0" + envinfo "^7.7.3" + execa "^5.0.0" + express "^4.17.3" + find-up "^5.0.0" + fs-extra "^11.1.0" + get-npm-tarball-url "^2.0.3" + get-port "^5.1.1" + giget "^1.0.0" + globby "^11.0.2" + jscodeshift "^0.14.0" + leven "^3.1.0" + ora "^5.4.1" + prettier "^2.8.0" + prompts "^2.4.0" + puppeteer-core "^2.1.1" + read-pkg-up "^7.0.1" + semver "^7.3.7" + shelljs "^0.8.5" + simple-update-notifier "^1.0.0" + strip-json-comments "^3.0.1" + tempy "^1.0.1" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/client-api@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-7.0.27.tgz#59a5f56d1a71197da9f8b56bac2bc4c73dc7810a" + integrity sha512-TS+w8hV2wZTbPkWgG6O1RXjkcYbqYflmZk/ju15d90AssPIZaSdd6se4HJrzJJ/9SMif/yyfG0NjFvNayl5Xcw== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + +"@storybook/client-logger@7.0.27", "@storybook/client-logger@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-7.0.27.tgz#22675527cca26e5f275157b8bbc72fd102856177" + integrity sha512-t4F0ByHP4MNiyVI5sgqtxSccr4RmPAqTr/h6CeGLJKWzUYobBV5hwKUd/qlfwdjev2u9C7AdLFPBKVcHX5PteA== + dependencies: + "@storybook/global" "^5.0.0" + +"@storybook/codemod@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/codemod/-/codemod-7.0.27.tgz#f7b0b6c77b58e5a02ee47ebd98db5efa83e9a124" + integrity sha512-kJyJkxEkbm4tnKKcDgVOqN9PG+Pf3ibsl6Skrm1m3wrbOql3DAVfZzLec/QeFOXrGmmSuvl7JdBQrkJj22Bu1Q== + dependencies: + "@babel/core" "~7.21.0" + "@babel/preset-env" "~7.21.0" + "@babel/types" "~7.21.2" + "@storybook/csf" "^0.1.0" + "@storybook/csf-tools" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/types" "7.0.27" + cross-spawn "^7.0.3" + globby "^11.0.2" + jscodeshift "^0.14.0" + lodash "^4.17.21" + prettier "^2.8.0" + recast "^0.23.1" + +"@storybook/components@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-7.0.27.tgz#5d33aa742ce4685eafb5deac9f943fd40c32809a" + integrity sha512-utt4fA1td7QHpvuD/9dWm9UEoO5xTU3EsXk/U2fPUQzN9NEsbWKV/QubUYIpVy5iwwgUyMvqzWHM0veAriJW5A== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/global" "^5.0.0" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + memoizerific "^1.11.3" + use-resize-observer "^9.1.0" + util-deprecate "^1.0.2" + +"@storybook/core-client@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-7.0.27.tgz#66cdd97a0c76f4662c97fbfa81c6a43c54ff39cd" + integrity sha512-5cyAdOLqMUJfGW2c31U4/Q5TF+8DQnuQ6jKeX3W8ZQVhDn/Kox4qYNxRR0aRUUHTzxRVojQfmDHXy8IxZqYBNA== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + +"@storybook/core-common@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-7.0.27.tgz#ffacc00fa2343df98b2c93e8697efd54acc228b7" + integrity sha512-nlHXpn3CghCwkeIffZ7/PzcraCDXNZz+cnR4L8vtgJn1n6W7y92mxfF8gkRHuiYHWHbPWRVP9M5vAmVoiNMxjw== + dependencies: + "@storybook/node-logger" "7.0.27" + "@storybook/types" "7.0.27" + "@types/node" "^16.0.0" + "@types/node-fetch" "^2.6.4" + "@types/pretty-hrtime" "^1.0.0" + chalk "^4.1.0" + esbuild "^0.17.0" + esbuild-register "^3.4.0" + file-system-cache "2.3.0" + find-up "^5.0.0" + fs-extra "^11.1.0" + glob "^8.1.0" + glob-promise "^6.0.2" + handlebars "^4.7.7" + lazy-universal-dotenv "^4.0.0" + node-fetch "^2.0.0" + picomatch "^2.3.0" + pkg-dir "^5.0.0" + pretty-hrtime "^1.0.3" + resolve-from "^5.0.0" + ts-dedent "^2.0.0" + +"@storybook/core-events@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-7.0.27.tgz#94c7f8877998a25e8d14bafa51839fedfa442769" + integrity sha512-sNnqgO5i5DUIqeQfNbr987KWvAciMN9FmMBuYdKjVFMqWFyr44HTgnhfKwZZKl+VMDYkHA9Do7UGSYZIKy0P4g== + +"@storybook/core-server@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-7.0.27.tgz#e9644d52169131cd18a12bdbeca7bee20a903a60" + integrity sha512-9OBDtJ57qJYAgj5UNK8ip4XVSQEVAZxAXWv3QKkQi/QHGixOpxNG4piOF5TdQHv4kc/OX6I0j25ZIrO8jl+VnA== + dependencies: + "@aw-web-design/x-default-browser" "1.4.88" + "@discoveryjs/json-ext" "^0.5.3" + "@storybook/builder-manager" "7.0.27" + "@storybook/core-common" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/csf-tools" "7.0.27" + "@storybook/docs-mdx" "^0.1.0" + "@storybook/global" "^5.0.0" + "@storybook/manager" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/telemetry" "7.0.27" + "@storybook/types" "7.0.27" + "@types/detect-port" "^1.3.0" + "@types/node" "^16.0.0" + "@types/node-fetch" "^2.5.7" + "@types/pretty-hrtime" "^1.0.0" + "@types/semver" "^7.3.4" + better-opn "^2.1.1" + chalk "^4.1.0" + cli-table3 "^0.6.1" + compression "^1.7.4" + detect-port "^1.3.0" + express "^4.17.3" + fs-extra "^11.1.0" + globby "^11.0.2" + ip "^2.0.0" + lodash "^4.17.21" + node-fetch "^2.6.7" + open "^8.4.0" + pretty-hrtime "^1.0.3" + prompts "^2.4.0" + read-pkg-up "^7.0.1" + semver "^7.3.7" + serve-favicon "^2.5.0" + telejson "^7.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + watchpack "^2.2.0" + ws "^8.2.3" + +"@storybook/core-webpack@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-7.0.27.tgz#56d0487249ae4274c97ed9a28dff383e68ce5c7b" + integrity sha512-qCyiS8hkcejxIr5ARbdhYFxTMjxdBh5ddRPrVnmRk4zlA4SwkS+a6Mt5mzfy6CQY0MQvIeNuKidHZ2pEr2oHNQ== + dependencies: + "@storybook/core-common" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/types" "7.0.27" + "@types/node" "^16.0.0" + ts-dedent "^2.0.0" + +"@storybook/csf-plugin@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-7.0.27.tgz#0447b3c0d56ae8e95876535e89d2a0616693a748" + integrity sha512-9GqsRNrLMH9+P/57TfGZMZOYgnai1klI0hnBAHwPUaBvCwXx/pjOBy4VW30OslT1JLHzu2ZIvZxZiy+yNZM03w== + dependencies: + "@storybook/csf-tools" "7.0.27" + unplugin "^0.10.2" + +"@storybook/csf-tools@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-7.0.27.tgz#e4bb05bb5712b28fc9422b303e39f6c966f43670" + integrity sha512-JrSP628b1VVQa2lLefEX1u3DRng4Czrl+NBFy5Mgy9JjXFs1dGJM9m0k1/r2qNO4Km9HeTcR4NAcTMfatqzw2Q== + dependencies: + "@babel/generator" "~7.21.1" + "@babel/parser" "~7.21.2" + "@babel/traverse" "~7.21.2" + "@babel/types" "~7.21.2" + "@storybook/csf" "^0.1.0" + "@storybook/types" "7.0.27" + fs-extra "^11.1.0" + recast "^0.23.1" + ts-dedent "^2.0.0" + +"@storybook/csf@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.1.tgz#95901507dc02f0bc6f9ac8ee1983e2fc5bb98ce6" + integrity sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw== + dependencies: + lodash "^4.17.15" + +"@storybook/csf@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.1.1.tgz#abccc8c3e49aed0a6a7e87beb0d1c262b1921c06" + integrity sha512-4hE3AlNVxR60Wc5KSC68ASYzUobjPqtSKyhV6G+ge0FIXU55N5nTY7dXGRZHQGDBPq+XqchMkIdlkHPRs8nTHg== + dependencies: + type-fest "^2.19.0" + +"@storybook/docs-mdx@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@storybook/docs-mdx/-/docs-mdx-0.1.0.tgz#33ba0e39d1461caf048b57db354b2cc410705316" + integrity sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg== + +"@storybook/docs-tools@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-7.0.27.tgz#ab3c31d90920b5e93d29a2879f2bed87ba4b82df" + integrity sha512-vXlFbwnlJV1ihYbwoP7uJ8JhYXkhaH3WL1yzIJx0kL1Fl1KLQc+x4flBM3pWO2MkrRa2hFLy5GrDwD6GxbMfEQ== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/core-common" "7.0.27" + "@storybook/preview-api" "7.0.27" + "@storybook/types" "7.0.27" + "@types/doctrine" "^0.0.3" + doctrine "^3.0.0" + lodash "^4.17.21" + +"@storybook/global@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@storybook/global/-/global-5.0.0.tgz#b793d34b94f572c1d7d9e0f44fac4e0dbc9572ed" + integrity sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ== + +"@storybook/instrumenter@7.0.27", "@storybook/instrumenter@^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-7.0.27.tgz#81376198efb764bf618c4d9d292a1187281f0be1" + integrity sha512-LR1Dm90lC5nurZQ5ZIbNa/8b+0AsBOQkEgnK/BQkheGMdlmrsh/i3dDqscOPZBPTLxZoYhhYWh27fDKHnT8bhw== + dependencies: + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/preview-api" "7.0.27" + +"@storybook/manager-api@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-7.0.27.tgz#270e560e791f4275882e844af228ba78450b492e" + integrity sha512-CVgy4ti8h0Xc4nxiPujTzhMANl9wmfLGvSA9ZX6YUBbKFV4UOL4oj105iHPW7Ngse6Qoqj0rnhkOSmLczXT03w== + dependencies: + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/global" "^5.0.0" + "@storybook/router" "7.0.27" + "@storybook/theming" "7.0.27" + "@storybook/types" "7.0.27" + dequal "^2.0.2" + lodash "^4.17.21" + memoizerific "^1.11.3" + semver "^7.3.7" + store2 "^2.14.2" + telejson "^7.0.3" + ts-dedent "^2.0.0" + +"@storybook/manager@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/manager/-/manager-7.0.27.tgz#f692dbb30e717456ce725355c65c6c964e998ef5" + integrity sha512-Kxryp9Bp3EEr1axZdq7iOU5epmUvd65j/uT9FxFFHp5ffag6ULfRYVmrXsSIfR6UkwAbx2XYX/W+ScWRel4pDA== + +"@storybook/mdx2-csf@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@storybook/mdx2-csf/-/mdx2-csf-1.1.0.tgz#97f6df04d0bf616991cc1005a073ac004a7281e5" + integrity sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw== + +"@storybook/node-logger@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-7.0.27.tgz#e77f4ae532f212818cd39293493edc53987d8e31" + integrity sha512-idoK+sDaTTPuxHcKhxn+l27Omhxvr1TQ0ALw1h8ehyMbW8TZBdWvYLYfmiWeI3+NQtmeudzxhKSVYTmAY4qDJw== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + npmlog "^5.0.1" + pretty-hrtime "^1.0.3" + +"@storybook/postinstall@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-7.0.27.tgz#c6a40b0ef18ff9a741aeffb0dc0c5ffe76336bc3" + integrity sha512-VehWuUQxTlqSfTEl3rnufA9+aBbFIv802c8HMJ6SsnwRSb93vlc2ZDGxx3hzryQhbBuI8oNDQx0VdFVwn+MkEg== + +"@storybook/preset-react-webpack@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-7.0.27.tgz#e4cb0aa2fbd53ae55bdb17751d66dae7a2451d83" + integrity sha512-6sqTbaqm3eKTk8RWhlGFQJl+3mwCZpue/4XXHAuuYKFt+0orIsqJtq7ulTk7G1Ism8YT/taU8pUknjGnJIeqiA== + dependencies: + "@babel/preset-flow" "^7.18.6" + "@babel/preset-react" "^7.18.6" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.5" + "@storybook/core-webpack" "7.0.27" + "@storybook/docs-tools" "7.0.27" + "@storybook/node-logger" "7.0.27" + "@storybook/react" "7.0.27" + "@storybook/react-docgen-typescript-plugin" "1.0.6--canary.9.0c3f3b7.0" + "@types/node" "^16.0.0" + "@types/semver" "^7.3.4" + babel-plugin-add-react-displayname "^0.0.5" + babel-plugin-react-docgen "^4.2.1" + fs-extra "^11.1.0" + react-refresh "^0.11.0" + semver "^7.3.7" + webpack "5" + +"@storybook/preview-api@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-7.0.27.tgz#6e414a95ecca61fe817d67433588d06301d5c5e2" + integrity sha512-FhauTuLzRsaIaEORQP5lxYrzwRgZPMnfYEPnzduyGgPiY6VZkS6wIiO6pKzat83V1L4J7m5aZhTB3HtvTwPhvg== + dependencies: + "@storybook/channel-postmessage" "7.0.27" + "@storybook/channels" "7.0.27" + "@storybook/client-logger" "7.0.27" + "@storybook/core-events" "7.0.27" + "@storybook/csf" "^0.1.0" + "@storybook/global" "^5.0.0" + "@storybook/types" "7.0.27" + "@types/qs" "^6.9.5" + dequal "^2.0.2" + lodash "^4.17.21" + memoizerific "^1.11.3" + qs "^6.10.0" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/preview@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/preview/-/preview-7.0.27.tgz#3dab5e79e499eba05c2a96b2f1128056dcbd39d3" + integrity sha512-yHUlMX6wUlIlOYIzfUtqkuXOgRPJJLqGfeniMxLWjNpcePgZ6iSx0fF91ubKfPF1uUbA5vGSVX6KI+AF/RLM1Q== + +"@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0": + version "1.0.6--canary.9.0c3f3b7.0" + resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.6--canary.9.0c3f3b7.0.tgz#7f10f3c641f32e4513a8b6ffb5036933e7059534" + integrity sha512-KUqXC3oa9JuQ0kZJLBhVdS4lOneKTOopnNBK4tUAgoxWQ3u/IjzdueZjFr7gyBrXMoU6duutk3RQR9u8ZpYJ4Q== + dependencies: + debug "^4.1.1" + endent "^2.0.1" + find-cache-dir "^3.3.1" + flat-cache "^3.0.4" + micromatch "^4.0.2" + react-docgen-typescript "^2.2.2" + tslib "^2.0.0" + +"@storybook/react-dom-shim@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-7.0.27.tgz#146457b872f7e9a58c6b7196b7e3752b8f9b8160" + integrity sha512-KnyBrs9S8BIWIhNdT6cIpqmSE9CAxL8uGH/ev60OutKeM+rf3SC3AylIBSvMdjy4cykMasg16QiShK+MMbKl9g== + +"@storybook/react-webpack5@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/react-webpack5/-/react-webpack5-7.0.27.tgz#2e63501c55e7026ecd99ac9775d838156e53535e" + integrity sha512-pTddt227ubDi8fjCe56F90fKgGPHlMaE7DDXYRYUDU8bgZUYu9hlmN0oVH8jXd3DputASH6hWC/Dll8aqrJ/6Q== + dependencies: + "@storybook/builder-webpack5" "7.0.27" + "@storybook/preset-react-webpack" "7.0.27" + "@storybook/react" "7.0.27" + "@types/node" "^16.0.0" + +"@storybook/react@7.0.27", "@storybook/react@^7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-7.0.27.tgz#0b2849ea05efa9f914a41e4a986f5e961444e01c" + integrity sha512-NPD6J5okkxiBx8k8TWvn03qG6ThD2rp1+2nFGgo3cInCEmvDgoa3wjq/Gl/2QV4W8XrQ8GiItj0Lzca+CBrkOw== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/core-client" "7.0.27" + "@storybook/docs-tools" "7.0.27" + "@storybook/global" "^5.0.0" + "@storybook/preview-api" "7.0.27" + "@storybook/react-dom-shim" "7.0.27" + "@storybook/types" "7.0.27" + "@types/escodegen" "^0.0.6" + "@types/estree" "^0.0.51" + "@types/node" "^16.0.0" + acorn "^7.4.1" + acorn-jsx "^5.3.1" + acorn-walk "^7.2.0" + escodegen "^2.0.0" + html-tags "^3.1.0" + lodash "^4.17.21" + prop-types "^15.7.2" + react-element-to-jsx-string "^15.0.0" + ts-dedent "^2.0.0" + type-fest "^2.19.0" + util-deprecate "^1.0.2" + +"@storybook/router@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-7.0.27.tgz#c7beffb1eb40fb525d9fbeabcac2ca25e8259f2a" + integrity sha512-Onflm2mERipuYB3SR+0CFAZKPbDiLsJdgX09BP8bGrg7dVYwiGkL5dc9H/CP0KPxtC7kXT8x1Zc+yx0Y0kWiJw== + dependencies: + "@storybook/client-logger" "7.0.27" + memoizerific "^1.11.3" + qs "^6.10.0" + +"@storybook/store@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-7.0.27.tgz#8bf949dbe3ba18662dee59693c2f373105f7ef8f" + integrity sha512-uk4/92psjfYv8llxnGG3LRy4gpt0ofzbfP3Q3x+vXKRCriKhJFSUGuYp5fSIRwVXJ2UL2o2PqzVSlVgBWZexuA== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/preview-api" "7.0.27" + +"@storybook/telemetry@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-7.0.27.tgz#0feba3c180eaf71844b0b44d945e94cdbea6354d" + integrity sha512-dKPxR7BpIZU/6WmKXnPRHR1b7mlpLcEPoBxOXZKfEmTV6Qb+OIwr2N7pEQA1Jzlktkfw2CoM2O9s1JOMWrVnvQ== + dependencies: + "@storybook/client-logger" "7.0.27" + "@storybook/core-common" "7.0.27" + chalk "^4.1.0" + detect-package-manager "^2.0.1" + fetch-retry "^5.0.2" + fs-extra "^11.1.0" + isomorphic-unfetch "^3.1.0" + nanoid "^3.3.1" + read-pkg-up "^7.0.1" + +"@storybook/testing-library@^0.0.14-next.2": + version "0.0.14-next.2" + resolved "https://registry.yarnpkg.com/@storybook/testing-library/-/testing-library-0.0.14-next.2.tgz#458e6c7623118e24826ba73b80db0a887f3f57e8" + integrity sha512-i/SLSGm0o978ELok/SB4Qg1sZ3zr+KuuCkzyFqcCD0r/yf+bG35aQGkFqqxfSAdDxuQom0NO02FE+qys5Eapdg== + dependencies: + "@storybook/client-logger" "^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0" + "@storybook/instrumenter" "^7.0.0-beta.0 || ^7.0.0-rc.0 || ^7.0.0" + "@testing-library/dom" "^8.3.0" + "@testing-library/user-event" "^13.2.1" + ts-dedent "^2.2.0" + +"@storybook/theming@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-7.0.27.tgz#62d6f4af8e8e2089c0b84e4d47a1a4ec3ba06fb3" + integrity sha512-l2Lc8xX8QXQO8c9gpzdUUJ+0YqLoh8w74I7lzxiife0TzEQrhWD9aRJAVimm8Vzfq5x3CNeJNFHc5PcG8ypQig== + dependencies: + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.0" + "@storybook/client-logger" "7.0.27" + "@storybook/global" "^5.0.0" + memoizerific "^1.11.3" + +"@storybook/types@7.0.27": + version "7.0.27" + resolved "https://registry.yarnpkg.com/@storybook/types/-/types-7.0.27.tgz#5e8a3bc8d973a519f1ca48fca5b34d53de9683e0" + integrity sha512-pmJuIm+kGaZiDMyl2i5KFS9iGWrpW1jVcp9OMtHeK20LBzY5Hxq/JMc3E+fbVNkAX2hVlVGbbVUNPTvd9AjbrA== + dependencies: + "@storybook/channels" "7.0.27" + "@types/babel__core" "^7.0.0" + "@types/express" "^4.7.0" + file-system-cache "2.3.0" + +"@testing-library/dom@^8.3.0": + version "8.20.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" + integrity sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/user-event@^13.2.1": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== + dependencies: + "@babel/runtime" "^7.12.5" + +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" + integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== + +"@types/babel__core@^7.0.0": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + integrity sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg== + dependencies: + "@babel/types" "^7.20.7" + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#9fd20b3974bdc2bcd4ac6567e2e0f6885cb2cf41" + integrity sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/detect-port@^1.3.0": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/detect-port/-/detect-port-1.3.3.tgz#124c5d4c283f48a21f80826bcf39433b3e64aa81" + integrity sha512-bV/jQlAJ/nPY3XqSatkGpu+nGzou+uSwrH1cROhn+jBFg47yaNH+blW4C7p9KhopC7QxCv/6M86s37k8dMk0Yg== + +"@types/doctrine@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/doctrine/-/doctrine-0.0.3.tgz#e892d293c92c9c1d3f9af72c15a554fbc7e0895a" + integrity sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA== + +"@types/ejs@^3.1.1": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.2.tgz#75d277b030bc11b3be38c807e10071f45ebc78d9" + integrity sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g== + +"@types/escodegen@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@types/escodegen/-/escodegen-0.0.6.tgz#5230a9ce796e042cda6f086dbf19f22ea330659c" + integrity sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig== + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.0.tgz#55818eabb376e2272f77fbf5c96c43137c3c1e53" + integrity sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.35" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" + integrity sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.13", "@types/express@^4.7.0": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/find-cache-dir@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz#7b959a4b9643a1e6a1a5fe49032693cc36773501" + integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw== + +"@types/glob@^8.0.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + +"@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + dependencies: + "@types/node" "*" + +"@types/hoist-non-react-statics@*": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@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" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-errors@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" + integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== + +"@types/http-proxy@^1.17.8": + version "1.17.11" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.11.tgz#0ca21949a5588d55ac2b659b69035c84bd5da293" + integrity sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA== + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + +"@types/lodash@^4.14.167": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== + +"@types/mdx@^2.0.0": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.5.tgz#9a85a8f70c7c4d9e695a21d5ae5c93645eda64b1" + integrity sha512-76CqzuD6Q7LC+AtbPqrvD9AqsN0k8bsYo2bM2J8pmNldP1aIPAbzUQ7QbobyXL4eLr1wK5x8FZFe8eF/ubRuBg== + +"@types/mime-types@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.1.tgz#d9ba43490fa3a3df958759adf69396c3532cf2c1" + integrity sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw== + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + +"@types/node-fetch@^2.5.7", "@types/node-fetch@^2.6.4": + version "2.6.4" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" + integrity sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "20.4.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9" + integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw== + +"@types/node@^16.0.0": + version "16.18.38" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.38.tgz#1dcdb6c54d02b323f621213745f2e44af30c73e6" + integrity sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/npmlog@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6" + integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/pretty-hrtime@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" + integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/qs@*", "@types/qs@^6.9.5": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-dom@^18.2.6": + version "18.2.7" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63" + integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@>=16", "@types/react@^18.2.14": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" + integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + +"@types/semver@^7.3.12", "@types/semver@^7.3.4": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== + +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/styled-components@^5.1.26": + version "5.1.26" + resolved "https://registry.yarnpkg.com/@types/styled-components/-/styled-components-5.1.26.tgz#5627e6812ee96d755028a98dae61d28e57c233af" + integrity sha512-KuKJ9Z6xb93uJiIyxo/+ksS7yLjS1KzG6iv5i78dhVg/X3u5t1H7juRWqVmodIdz6wGVaIApo1u01kmFRdJHVw== + dependencies: + "@types/hoist-non-react-statics" "*" + "@types/react" "*" + csstype "^3.0.2" + +"@types/stylis@^4.0.2": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.0.tgz#199a3f473f0c3a6f6e4e1b17cdbc967f274bdc6b" + integrity sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw== + +"@types/unist@^2.0.0": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.7.tgz#5b06ad6894b236a1d2bd6b2f07850ca5c59cf4d6" + integrity sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g== + +"@types/ws@^8.5.5": + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^16.0.0": + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.60.1": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" + integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/type-utils" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.60.1": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7" + integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== + dependencies: + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c" + integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + +"@typescript-eslint/type-utils@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a" + integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew== + dependencies: + "@typescript-eslint/typescript-estree" "5.62.0" + "@typescript-eslint/utils" "5.62.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" + integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== + +"@typescript-eslint/typescript-estree@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" + integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== + dependencies: + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/visitor-keys" "5.62.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.45.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86" + integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.62.0" + "@typescript-eslint/types" "5.62.0" + "@typescript-eslint/typescript-estree" "5.62.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.62.0": + version "5.62.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" + integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== + dependencies: + "@typescript-eslint/types" "5.62.0" + eslint-visitor-keys "^3.3.0" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@yarnpkg/esbuild-plugin-pnp@^3.0.0-rc.10": + version "3.0.0-rc.15" + resolved "https://registry.yarnpkg.com/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz#4e40e7d2eb28825c9a35ab9d04c363931d7c0e67" + integrity sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA== + dependencies: + tslib "^2.4.0" + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-jsx@^5.3.1, acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +address@^1.0.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" + integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA== + +agent-base@5: + version "5.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" + integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-html-community@0.0.8, ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +app-root-dir@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" + integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + dependencies: + deep-equal "^2.0.5" + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +assert@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" + integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== + dependencies: + es6-object-assign "^1.1.0" + is-nan "^1.2.1" + object-is "^1.0.1" + util "^0.12.0" + +ast-types@0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d" + integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg== + dependencies: + tslib "^2.0.1" + +ast-types@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + +ast-types@^0.16.1: + version "0.16.1" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.16.1.tgz#7a9da1617c9081bc121faafe91711b4c8bb81da2" + integrity sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg== + dependencies: + tslib "^2.0.1" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^3.2.3: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +babel-core@^7.0.0-bridge.0: + version "7.0.0-bridge.0" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" + integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== + +babel-loader@^9.0.0: + version "9.1.3" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.3.tgz#3d0e01b4e69760cc694ee306fe16d358aa1c6f9a" + integrity sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw== + dependencies: + find-cache-dir "^4.0.0" + schema-utils "^4.0.0" + +babel-plugin-add-react-displayname@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" + integrity sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw== + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-named-exports-order@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-named-exports-order/-/babel-plugin-named-exports-order-0.0.2.tgz#ae14909521cf9606094a2048239d69847540cb09" + integrity sha512-OgOYHOLoRK+/mvXU9imKHlG6GkPLYrUCvFXG/CM93R/aNNO8pOOF4aS+S8CCHMDQoNSeiOYEZb/G6RwL95Jktw== + +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + +babel-plugin-polyfill-corejs2@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz#9f9a0e1cd9d645cc246a5e094db5c3aa913ccd2b" + integrity sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA== + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.1" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + +babel-plugin-polyfill-corejs3@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz#d406c5738d298cd9c66f64a94cf8d5904ce4cc5e" + integrity sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.1" + core-js-compat "^3.31.0" + +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + +babel-plugin-polyfill-regenerator@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz#ace7a5eced6dff7d5060c335c52064778216afd3" + integrity sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.1" + +babel-plugin-react-docgen@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b" + integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ== + dependencies: + ast-types "^0.14.2" + lodash "^4.17.15" + react-docgen "^5.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +better-opn@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" + integrity sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA== + dependencies: + open "^7.0.3" + +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bl@^4.0.3, bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.1.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.1.tgz#960948fa0e0153f5d26743ab15baf8e33752c135" + integrity sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-assert@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" + integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== + +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ== + dependencies: + pako "~0.2.0" + +browserslist@^4.14.5, browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== + dependencies: + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +c8@^7.6.0: + version "7.14.0" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.14.0.tgz#f368184c73b125a80565e9ab2396ff0be4d732f3" + integrity sha512-i04rtkkcNcCf7zsQcSv/T9EbUn4RXQ6mropeMcjFOsQXQ0iGLAr/xT6TImQg4+U9hmNpN9XdvPkjUL1IzbgxJw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.3" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" + test-exclude "^6.0.0" + v8-to-istanbul "^9.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.9" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelize@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" + integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== + +caniuse-lite@^1.0.30001503: + version "1.0.30001515" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz#418aefeed9d024cd3129bfae0ccc782d4cb8f12b" + integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== + +case-sensitive-paths-webpack-plugin@^2.4.0: + version "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== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^3.4.0, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +clean-css@^5.2.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" + integrity sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== + +cli-table3@^0.6.1: + version "0.6.3" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" + integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^2.0.10, colorette@^2.0.14, colorette@^2.0.19: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +convert-source-map@^1.1.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +core-js-compat@^3.25.1, core-js-compat@^3.31.0: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" + integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== + dependencies: + browserslist "^4.21.9" + +core-js-pure@^3.23.3: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.31.1.tgz#73d154958881873bc19381df80bddb20c8d0cdb5" + integrity sha512-w+C62kvWti0EPs4KPMCMVv9DriHSXfQOCQ94bGGBiEW5rrbtt/Rz8n5Krhfw9cpFyzXBjf3DB3QnPdEzGDY4Fw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" + integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== + +css-color-keywords@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" + integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== + +css-loader@^6.7.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" + integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.21" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.3" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.8" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-to-react-native@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" + integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ== + dependencies: + camelize "^1.0.0" + css-color-keywords "^1.0.0" + postcss-value-parser "^4.0.2" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +csstype@^3.0.2, csstype@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + +debug@2.6.9, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-equal@^2.0.5: + version "2.2.2" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.2.tgz#9b2635da569a13ba8e1cc159c2f744071b115daa" + integrity sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.1" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + +default-browser-id@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +defu@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.2.tgz#1217cba167410a1765ba93893c6dbac9ed9d9e5c" + integrity sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ== + +del@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" + integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +dequal@^2.0.2: + 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" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detect-indent@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-package-manager@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" + integrity sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A== + dependencies: + execa "^5.1.1" + +detect-port@^1.3.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.5.1.tgz#451ca9b6eaf20451acb0799b8ab40dff7718727b" + integrity sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ== + dependencies: + address "^1.0.1" + debug "4" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.6.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.0.tgz#2202c947845c7a63c23ece58f2f70ff6ab4c2f7d" + integrity sha512-rza3UH1LwdHh9qyPXp8lkwpjSNk/AMD3dPytUoRoqnypDUhY0xvbdmVhWOfxO68frEfV9BU8V12Ez7ZsHGZpCQ== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-expand@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" + integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== + +dotenv@^16.0.0: + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + +duplexify@^3.5.0, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ee-first@1.1.1: + version "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.8: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.431: + version "1.4.459" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.459.tgz#25a23370f4ae8aaa8f77aaf00133aa4994f4148e" + integrity sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +endent@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/endent/-/endent-2.1.0.tgz#5aaba698fb569e5e18e69e1ff7a28ff35373cd88" + integrity sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.5" + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +envinfo@^7.7.3: + version "7.10.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" + integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.3.tgz#8aaa0ffc080e8a6fef6ace72631dc1ec5d47bf94" + integrity sha512-ZU4miiY1j3sGPFLJ34VJXEqhpmL+HGByCinGHv4HC+Fxl2fI2Z4yR6tl0mORnDr6PA8eihWo4LmSWDbvhALckg== + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.10" + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-module-lexer@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" + integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-object-assign@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw== + +esbuild-plugin-alias@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/esbuild-plugin-alias/-/esbuild-plugin-alias-0.2.1.tgz#45a86cb941e20e7c2bc68a2bea53562172494fcb" + integrity sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ== + +esbuild-register@^3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/esbuild-register/-/esbuild-register-3.4.2.tgz#1e39ee0a77e8f320a9790e68c64c3559620b9175" + integrity sha512-kG/XyTDyz6+YDuyfB9ZoSIOOmgyFCH+xPRtsCa8W85HLRV5Csp+o3jWVbOSHgSLfyLc5DmP+KFDNwty4mEjC+Q== + dependencies: + debug "^4.3.4" + +esbuild@^0.17.0: + version "0.17.19" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.19.tgz#087a727e98299f0462a3d0bcdd9cd7ff100bd955" + integrity sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw== + optionalDependencies: + "@esbuild/android-arm" "0.17.19" + "@esbuild/android-arm64" "0.17.19" + "@esbuild/android-x64" "0.17.19" + "@esbuild/darwin-arm64" "0.17.19" + "@esbuild/darwin-x64" "0.17.19" + "@esbuild/freebsd-arm64" "0.17.19" + "@esbuild/freebsd-x64" "0.17.19" + "@esbuild/linux-arm" "0.17.19" + "@esbuild/linux-arm64" "0.17.19" + "@esbuild/linux-ia32" "0.17.19" + "@esbuild/linux-loong64" "0.17.19" + "@esbuild/linux-mips64el" "0.17.19" + "@esbuild/linux-ppc64" "0.17.19" + "@esbuild/linux-riscv64" "0.17.19" + "@esbuild/linux-s390x" "0.17.19" + "@esbuild/linux-x64" "0.17.19" + "@esbuild/netbsd-x64" "0.17.19" + "@esbuild/openbsd-x64" "0.17.19" + "@esbuild/sunos-x64" "0.17.19" + "@esbuild/win32-arm64" "0.17.19" + "@esbuild/win32-ia32" "0.17.19" + "@esbuild/win32-x64" "0.17.19" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +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== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +eslint-plugin-react@^7.32.2: + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-plugin-storybook@^0.6.12: + version "0.6.12" + resolved "https://registry.yarnpkg.com/eslint-plugin-storybook/-/eslint-plugin-storybook-0.6.12.tgz#7bdb3392bb03bebde40ed19accfd61246e9d6301" + integrity sha512-XbIvrq6hNVG6rpdBr+eBw63QhOMLpZneQVSooEDow8aQCWGCk/5vqtap1yxpVydNfSxi3S/3mBBRLQqKUqQRww== + dependencies: + "@storybook/csf" "^0.0.1" + "@typescript-eslint/utils" "^5.45.0" + requireindex "^1.1.0" + ts-dedent "^2.2.0" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== + +eslint@^8.44.0: + version "8.44.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" + integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.6.0" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" + integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-to-babel@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/estree-to-babel/-/estree-to-babel-3.2.1.tgz#82e78315275c3ca74475fdc8ac1a5103c8a75bf5" + integrity sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg== + dependencies: + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.2.0" + c8 "^7.6.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +execa@^5.0.0, execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + 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== + +extract-zip@^1.6.6: + version "1.7.0" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" + integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== + dependencies: + concat-stream "^1.6.2" + debug "^2.6.9" + mkdirp "^0.5.4" + yauzl "^2.10.0" + +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" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.9: + version "3.3.0" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" + integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== + dependencies: + bser "2.1.1" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== + dependencies: + pend "~1.2.0" + +fetch-retry@^5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.6.tgz#17d0bc90423405b7a88b74355bf364acd2a7fa56" + integrity sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ== + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-system-cache@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/file-system-cache/-/file-system-cache-2.3.0.tgz#201feaf4c8cd97b9d0d608e96861bb6005f46fe6" + integrity sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ== + dependencies: + fs-extra "11.1.1" + ramda "0.29.0" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-cache-dir@^3.0.0, find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-cache-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-4.0.0.tgz#a30ee0448f81a3990708f6453633c733e2f6eec2" + integrity sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg== + dependencies: + common-path-prefix "^3.0.0" + pkg-dir "^7.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" + integrity sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw== + dependencies: + locate-path "^7.1.0" + path-exists "^5.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +flow-parser@0.*: + version "0.212.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.212.0.tgz#2b15a32bf0cc15fc81818fe849752dd70cb87871" + integrity sha512-45eNySEs7n692jLN+eHQ6zvC9e1cqu9Dq1PpDHTcWRri2HFEs8is8Anmp1RcIhYxA5TZYD6RuESG2jdj6nkDJQ== + +follow-redirects@^1.0.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +fork-ts-checker-webpack-plugin@^7.2.8: + version "7.3.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.3.0.tgz#a9c984a018493962360d7c7e77a67b44a2d5f3aa" + integrity sha512-IN+XTzusCjR5VgntYFgxbxVx3WraPRnKehBFrf00cMSrtUuW9MsG9dhL6MWpY6MkjC3wVwoujfCDgZZCQwbswA== + dependencies: + "@babel/code-frame" "^7.16.7" + chalk "^4.1.2" + chokidar "^3.5.3" + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + fs-extra "^10.0.0" + memfs "^3.4.1" + minimatch "^3.0.4" + node-abort-controller "^3.0.1" + schema-utils "^3.1.1" + semver "^7.3.5" + tapable "^2.2.1" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@11.1.1, fs-extra@^11.1.0: + version "11.1.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-monkey@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.4.tgz#ee8c1b53d3fe8bb7e5d2c5c5dfc0168afdd2f747" + integrity sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ== + +fs-readdir-recursive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-npm-tarball-url@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/get-npm-tarball-url/-/get-npm-tarball-url-2.0.3.tgz#67dff908d699e9e2182530ae6e939a93e5f8dfdb" + integrity sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw== + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +giget@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/giget/-/giget-1.1.2.tgz#f99a49cb0ff85479c8c3612cdc7ca27f2066e818" + integrity sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A== + dependencies: + colorette "^2.0.19" + defu "^6.1.2" + https-proxy-agent "^5.0.1" + mri "^1.2.0" + node-fetch-native "^1.0.2" + pathe "^1.1.0" + tar "^6.1.13" + +github-slugger@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" + integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-promise@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-6.0.3.tgz#e6b3ab02d350b3f4b3e15b57e4485986e41ba2fe" + integrity sha512-m+kxywR5j/2Z2V9zvHKfwwL5Gp7gIFEBX+deTB9w2lJB+wSuw9kcS43VfvTAMk8TXL5JCl/cCjsR+tgNVspGyA== + dependencies: + "@types/glob" "^8.0.0" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +gunzip-maybe@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" + integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw== + dependencies: + browserify-zlib "^0.1.4" + is-deflate "^1.0.0" + is-gzip "^1.0.0" + peek-stream "^1.1.0" + pumpify "^1.3.3" + through2 "^2.0.3" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +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== + +hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.1.0, html-entities@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" + integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" + integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== + +html-webpack-plugin@^5.5.0, html-webpack-plugin@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz#72270f4a78e222b5825b296e5e3e1328ad525a3e" + integrity sha512-6YrDKTuqaP/TquFH7h4srYWsZx+x6k6+FbsTm0ziCwGHDP78Unr1r9F/H4+sGmMbX08GQcJ+K64x55b+7VM/jg== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" + integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== + dependencies: + agent-base "5" + debug "4" + +https-proxy-agent@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" + integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + +is-absolute-url@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-arguments@^1.0.4, is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-deflate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" + integrity sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ== + +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" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-gzip@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" + integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-nan@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-cwd@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-inside@^3.0.2, is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^3.0.0: + version "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-object@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +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== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +isomorphic-unfetch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" + integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4: + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-reports@^3.1.4: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-haste-map@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.1.tgz#62655c7a1c1b349a3206441330fb2dbdb4b63803" + integrity sha512-0m7f9PZXxOCk1gRACiVgX85knUKPKLPg4oRCjLoqIm9brTHXaorMA0JpmtmVkQiT8nmXyIVoZd/nnH1cfC33ig== + dependencies: + "@jest/types" "^29.6.1" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.4.3" + jest-util "^29.6.1" + jest-worker "^29.6.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-mock@^27.0.6: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== + +jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + integrity sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg== + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.1.tgz#64b015f0e985ef3a8ad049b61fe92b3db74a5319" + integrity sha512-U+Wrbca7S8ZAxAe9L6nb6g8kPdia5hj32Puu5iOqBCMTMWFHXuK6dOV2IFrpedbTV8fjMFLdWNttQTBL6u2MRA== + dependencies: + "@types/node" "*" + jest-util "^29.6.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jscodeshift@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.14.0.tgz#7542e6715d6d2e8bde0b4e883f0ccea358b46881" + integrity sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA== + dependencies: + "@babel/core" "^7.13.16" + "@babel/parser" "^7.13.16" + "@babel/plugin-proposal-class-properties" "^7.13.0" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8" + "@babel/plugin-proposal-optional-chaining" "^7.13.12" + "@babel/plugin-transform-modules-commonjs" "^7.13.8" + "@babel/preset-flow" "^7.13.13" + "@babel/preset-typescript" "^7.13.0" + "@babel/register" "^7.13.16" + babel-core "^7.0.0-bridge.0" + chalk "^4.1.2" + flow-parser "0.*" + graceful-fs "^4.2.4" + micromatch "^4.0.4" + neo-async "^2.5.0" + node-dir "^0.1.17" + recast "^0.21.0" + temp "^0.8.4" + write-file-atomic "^2.3.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "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== + +json5@^2.1.2, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.4" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz#b896535fed5b867650acce5a9bd4135ffc7b3bf9" + integrity sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +launch-editor@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7" + integrity sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.7.3" + +lazy-universal-dotenv@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-4.0.0.tgz#0b220c264e89a042a37181a4928cdd298af73422" + integrity sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg== + dependencies: + app-root-dir "^1.0.2" + dotenv "^16.0.0" + dotenv-expand "^10.0.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +locate-path@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-7.2.0.tgz#69cb1779bd90b35ab1e771e1f2f89a202c2a8a8a" + integrity sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA== + dependencies: + p-locate "^6.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.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" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== + +markdown-to-jsx@^7.1.8: + version "7.2.1" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.2.1.tgz#87061fd3176ad926ef3d99493e5c57f6335e0c51" + integrity sha512-9HrdzBAo0+sFz9ZYAGT5fB8ilzTW+q6lPocRxrIesMO+aB40V9MgFfbfMXxlGjf22OpRy+IXlvVaQenicdpgbg== + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +memfs@^3.4.1, memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== + dependencies: + fs-monkey "^1.0.4" + +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + integrity sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog== + dependencies: + map-or-similar "^1.5.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +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== + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.0.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.5, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mri@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +nanoid@^3.3.1, nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-abort-controller@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + +node-dir@^0.1.10, node-dir@^0.1.17: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + +node-fetch-native@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.2.0.tgz#13ec6df98f33168958dbfb6945f10aedf42e7ea8" + integrity sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ== + +node-fetch@^2.0.0, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.12" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" + integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-releases@^2.0.12: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.0.1, object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +objectorarray@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" + integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^7.0.3: + 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" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-limit@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-4.0.0.tgz#914af6544ed32bfa54670b061cafcbd04984b644" + integrity sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ== + dependencies: + yocto-queue "^1.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-locate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-6.0.0.tgz#3da9a49d4934b901089dca3302fa65dc5a05c04f" + integrity sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw== + dependencies: + p-limit "^4.0.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-exists@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-5.0.0.tgz#a6aad9489200b21fab31e49cf09277e5116fb9e7" + integrity sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathe@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" + integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q== + +peek-stream@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" + integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA== + dependencies: + buffer-from "^1.0.0" + duplexify "^3.5.0" + through2 "^2.0.3" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pirates@^4.0.4, pirates@^4.0.5: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +pkg-dir@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-7.0.0.tgz#8f0c08d6df4476756c5ff29b3282d0bab7517d11" + integrity sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA== + dependencies: + find-up "^6.3.0" + +polished@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" + integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== + dependencies: + "@babel/runtime" "^7.17.8" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" + integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.21, postcss@^8.4.23: + version "8.4.25" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" + integrity sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw== + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^2.8.0, prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== + +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" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +progress@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +prompts@^2.4.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +puppeteer-core@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-2.1.1.tgz#e9b3fbc1237b4f66e25999832229e9db3e0b90ed" + integrity sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w== + dependencies: + "@types/mime-types" "^2.1.0" + debug "^4.1.0" + extract-zip "^1.6.6" + https-proxy-agent "^4.0.0" + mime "^2.0.3" + mime-types "^2.1.25" + progress "^2.0.1" + proxy-from-env "^1.0.0" + rimraf "^2.6.1" + ws "^6.1.0" + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +qs@^6.10.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +ramda@0.29.0: + version "0.29.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.29.0.tgz#fbbb67a740a754c8a4cbb41e2a6e0eb8507f55fb" + integrity sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-colorful@^5.1.2: + version "5.6.1" + resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.6.1.tgz#7dc2aed2d7c72fac89694e834d179e32f3da563b" + integrity sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw== + +react-docgen-typescript@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c" + integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg== + +react-docgen@^5.0.0: + version "5.4.3" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.4.3.tgz#7d297f73b977d0c7611402e5fc2a168acf332b26" + integrity sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA== + dependencies: + "@babel/core" "^7.7.5" + "@babel/generator" "^7.12.11" + "@babel/runtime" "^7.7.6" + ast-types "^0.14.2" + commander "^2.19.0" + doctrine "^3.0.0" + estree-to-babel "^3.1.0" + neo-async "^2.6.1" + node-dir "^0.1.10" + strip-indent "^3.0.0" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-element-to-jsx-string@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz#1cafd5b6ad41946ffc8755e254da3fc752a01ac6" + integrity sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ== + dependencies: + "@base2/pretty-print-object" "1.0.1" + is-plain-object "5.0.0" + react-is "18.1.0" + +react-inspector@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-6.0.2.tgz#aa3028803550cb6dbd7344816d5c80bf39d07e9d" + integrity sha512-x+b7LxhmHXjHoU/VrFAzw5iutsILRoYyDq97EDYdFpPLcvqtEzk4ZSZSQjnFPbr5T57tLXnHcqFYoN1pI6u8uQ== + +react-is@18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" + integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-refresh@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" + integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.2.2, readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +recast@^0.21.0: + version "0.21.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.5.tgz#e8cd22bb51bcd6130e54f87955d33a2b2e57b495" + integrity sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg== + dependencies: + ast-types "0.15.2" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + +recast@^0.23.1: + version "0.23.3" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.23.3.tgz#f205d1f46b2c6f730de413ab18f96c166263d85f" + integrity sha512-HbCVFh2ANP6a09nzD4lx7XthsxMOJWKX5pIcUwtLrmeEIl3I0DwjCoVXDE0Aobk+7k/mS3H50FK4iuYArpcT6Q== + dependencies: + assert "^2.0.0" + ast-types "^0.16.1" + esprima "~4.0.0" + source-map "~0.6.1" + tslib "^2.0.1" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw== + dependencies: + resolve "^1.1.6" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + dependencies: + "@babel/runtime" "^7.8.4" + +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-external-links@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-8.0.0.tgz#308de69482958b5d1cd3692bc9b725ce0240f345" + integrity sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA== + dependencies: + extend "^3.0.0" + is-absolute-url "^3.0.0" + mdast-util-definitions "^4.0.0" + space-separated-tokens "^1.0.0" + unist-util-visit "^2.0.0" + +remark-slug@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" + integrity sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ== + dependencies: + github-slugger "^1.0.0" + mdast-util-to-string "^1.0.0" + unist-util-visit "^2.0.0" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requireindex@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.6.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + 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" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +"semver@2 || 3 || 4 || 5", semver@^5.6.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +semver@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + dependencies: + randombytes "^2.1.0" + +serve-favicon@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" + integrity sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA== + dependencies: + etag "~1.8.1" + fresh "0.5.2" + ms "2.1.1" + parseurl "~1.3.2" + safe-buffer "5.1.1" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" + integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +shelljs@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +simple-update-notifier@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" + integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== + dependencies: + semver "~7.0.0" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@^0.5.16, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.13" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz#7189a474c46f8d47c7b0da4b987bb45e908bd2d5" + integrity sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +store2@^2.14.2: + version "2.14.2" + resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068" + integrity sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w== + +storybook@^7.0.27: + version "7.0.27" + resolved "https://registry.yarnpkg.com/storybook/-/storybook-7.0.27.tgz#a1e979bc542c2c9bff8cfe8df38e8f44c56e0084" + integrity sha512-hp6lBETyC9uHFH0/RYU7v9Ga+e00VlaOA6/hKOFCoO1AH4/3J5/+Ey/uYslyAjCMIFsrqz7jyJjBzcUG/Ps+6g== + dependencies: + "@storybook/cli" "7.0.27" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" + integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== + +styled-components@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.0.3.tgz#03863133b8340912f52be623e3262ba9eb87ccc0" + integrity sha512-qEyWvDK4CYCyDckNIruRJIcQSvcUR3dVEw/fwxu1v0LFzUMPr2uf5PhXHp17FkGK+S4TkglOS+XIealo1MssQA== + dependencies: + "@babel/cli" "^7.21.0" + "@babel/core" "^7.21.0" + "@babel/helper-module-imports" "^7.18.6" + "@babel/plugin-external-helpers" "^7.18.6" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.21.0" + "@babel/traverse" "^7.21.2" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/unitless" "^0.8.0" + "@types/stylis" "^4.0.2" + css-to-react-native "^3.2.0" + csstype "^3.1.2" + postcss "^8.4.23" + shallowequal "^1.1.0" + stylis "^4.3.0" + tslib "^2.5.0" + +stylis@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.0.tgz#abe305a669fc3d8777e10eefcfc73ad861c5588c" + integrity sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +synchronous-promise@^2.0.15: + version "2.0.17" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" + integrity sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-fs@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^6.1.13: + version "6.1.15" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" + integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +telejson@^7.0.3: + version "7.1.0" + resolved "https://registry.yarnpkg.com/telejson/-/telejson-7.1.0.tgz#1ef7a0dd57eeb52cde933126f61bcc296c170f52" + integrity sha512-jFJO4P5gPebZAERPkJsqMAQ0IMA1Hi0AoSfxpnUaV6j6R2SZqlpkbS20U6dEUtA3RUYt2Ak/mTlkQzHH9Rv/hA== + dependencies: + memoizerific "^1.11.3" + +temp-dir@^2.0.0: + version "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.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2" + integrity sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg== + dependencies: + rimraf "~2.6.2" + +tempy@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.1.tgz#30fe901fd869cfb36ee2bd999805aa72fbb035de" + integrity sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w== + dependencies: + del "^6.0.0" + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + +terser-webpack-plugin@^5.3.1, terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.10.0, terser@^5.16.8: + version "5.19.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.19.0.tgz#7b3137b01226bdd179978207b9c8148754a6da9c" + integrity sha512-JpcpGOQLOXm2jsomozdMDpd5f8ZHh1rR48OFgWUH3QsyZcfPgv2qDCYbcDEAYNd4OZRj2bWYKpwdll/udZCk/Q== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through2@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-dedent@^2.0.0, ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +ts-loader@^9.4.4: + version "9.4.4" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.4.tgz#6ceaf4d58dcc6979f84125335904920884b7cee4" + integrity sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.4.0, tslib@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-fest@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typescript@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" + integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== + +uglify-js@^3.1.4: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.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== + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== + dependencies: + crypto-random-string "^2.0.0" + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unplugin@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.10.2.tgz#0f7089c3666f592cc448d746e39e7f41e9afb01a" + integrity sha512-6rk7GUa4ICYjae5PrAllvcDeuT8pA9+j5J5EkxbMFaV+SalHhxZ7X2dohMzu6C3XzsMT+6jwR/+pwPNR3uK9MA== + dependencies: + acorn "^8.8.0" + chokidar "^3.5.3" + webpack-sources "^3.2.3" + webpack-virtual-modules "^0.4.5" + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +use-resize-observer@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c" + integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow== + dependencies: + "@juggle/resize-observer" "^3.3.1" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.0, util@^0.12.4: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +v8-to-istanbul@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +walker@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +watchpack@^2.2.0, watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webpack-cli@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.4.tgz#c8e046ba7eaae4911d7e71e2b25b776fcc35759b" + integrity sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.1" + "@webpack-cli/info" "^2.0.2" + "@webpack-cli/serve" "^2.0.5" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.15.1: + version "4.15.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" + integrity sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + +webpack-hot-middleware@^2.25.1: + version "2.25.4" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.4.tgz#d8bc9e9cb664fc3105c8e83d2b9ed436bee4e193" + integrity sha512-IRmTspuHM06aZh98OhBJtqLpeWFM8FXJS5UYpKYxCJzyFoyWj1w6VGFfomZU7OPA55dMLrQK0pRT1eQ3PACr4w== + dependencies: + ansi-html-community "0.0.8" + html-entities "^2.1.0" + strip-ansi "^6.0.0" + +webpack-merge@^5.7.3: + version "5.9.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" + integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack-virtual-modules@^0.4.3, webpack-virtual-modules@^0.4.5: + version "0.4.6" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.6.tgz#3e4008230731f1db078d9cb6f68baf8571182b45" + integrity sha512-5tyDlKLqPfMqjT3Q9TAqf2YqjwmnUleZwzJi1A5qXnlBCdj2AtOJ6wAWdglTIDOPgOiOrXeBeFcsQ8+aGQ6QbA== + +webpack@5, webpack@^5.88.1: + version "5.88.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.1.tgz#21eba01e81bd5edff1968aea726e2fbfd557d3f8" + integrity sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.10, which-typed-array@^1.1.2, which-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.10.tgz#74baa2789991905c2076abb317103b866c64e69e" + integrity sha512-uxoA5vLUfRPdjCuJ1h5LlYdmTLbYfums398v3WLkM+i/Wltl2/XyZpQWKbN++ck5L64SR/grOHqtXCUKmlZPNA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^2.3.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== + dependencies: + async-limiter "~1.0.0" + +ws@^8.13.0, ws@^8.2.3: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^20.2.2, yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +yocto-queue@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" + integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== From f2f7098b03d368cb9c5ecfed22bf6b2d86dd628e Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Thu, 13 Jul 2023 13:32:21 +0900 Subject: [PATCH 003/185] =?UTF-8?q?[FE]=20chore:=20msw=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: msw 설치 Co-authored-by: Leejin Yang Co-authored-by: sᴏʟʙɪ ☔️ * feat: msw 적용 Co-authored-by: Leejin Yang Co-authored-by: sᴏʟʙɪ ☔️ * feat: storybook에 msw 적용 Co-authored-by: Leejin Yang Co-authored-by: sᴏʟʙɪ ☔️ --------- Co-authored-by: Leejin Yang Co-authored-by: sᴏʟʙɪ ☔️ --- frontend/.storybook/preview.ts | 5 + frontend/package.json | 5 + frontend/public/mockServiceWorker.js | 303 +++++++++++++++++++++++++++ frontend/src/index.tsx | 8 + frontend/src/mocks/.gitkeep | 0 frontend/src/mocks/browser.ts | 4 + frontend/src/mocks/handlers.ts | 1 + frontend/tsconfig.json | 2 +- frontend/yarn.lock | 301 +++++++++++++++++++++++++- 9 files changed, 621 insertions(+), 8 deletions(-) create mode 100644 frontend/public/mockServiceWorker.js delete mode 100644 frontend/src/mocks/.gitkeep create mode 100644 frontend/src/mocks/browser.ts create mode 100644 frontend/src/mocks/handlers.ts diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts index 8c2a141c..cd8ea156 100644 --- a/frontend/.storybook/preview.ts +++ b/frontend/.storybook/preview.ts @@ -1,4 +1,8 @@ import type { Preview } from '@storybook/react'; +import mswDecorator from 'msw-storybook-addon'; +import { handlers } from '../src/mocks/handlers'; + +export const decorators = [mswDecorator]; const preview: Preview = { parameters: { @@ -9,6 +13,7 @@ const preview: Preview = { date: /Date$/, }, }, + msw: handlers, }, }; diff --git a/frontend/package.json b/frontend/package.json index 25bfbb1c..fee3f970 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "msw-storybook-addon": "^1.8.0", "react": "^18.2.0", "react-dom": "^18.2.0", "styled-components": "^6.0.2" @@ -35,6 +36,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-storybook": "^0.6.12", "html-webpack-plugin": "^5.5.3", + "msw": "^1.2.2", "prettier": "^2.8.8", "storybook": "^7.0.27", "ts-loader": "^9.4.4", @@ -42,5 +44,8 @@ "webpack": "^5.88.1", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" + }, + "msw": { + "workerDirectory": "public" } } diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js new file mode 100644 index 00000000..8ee70b3e --- /dev/null +++ b/frontend/public/mockServiceWorker.js @@ -0,0 +1,303 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (1.2.2). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() + +self.addEventListener('install', function () { + self.skipWaiting() +}) + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +self.addEventListener('message', async function (event) { + const clientId = event.source.id + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } + + return response +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +async function getResponse(event, client, requestId) { + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'MOCK_NOT_FOUND': { + return passthrough() + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError + } + } + + return passthrough() +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [channel.port2]) + }) +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) +} + +async function respondWithMock(response) { + await sleep(response.delay) + return new Response(response.body, response) +} diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 086c86bc..df1ef641 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -2,6 +2,14 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; +const main = async () => { + if (process.env.NODE_ENV === 'development') { + const { worker } = await import('./mocks/browser'); + await worker.start(); + } +}; +await main(); + const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/frontend/src/mocks/.gitkeep b/frontend/src/mocks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/mocks/browser.ts b/frontend/src/mocks/browser.ts new file mode 100644 index 00000000..750e031c --- /dev/null +++ b/frontend/src/mocks/browser.ts @@ -0,0 +1,4 @@ +import { setupWorker } from 'msw'; +import { handlers } from './handlers'; + +export const worker = setupWorker(...handlers); diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts new file mode 100644 index 00000000..0f3710c6 --- /dev/null +++ b/frontend/src/mocks/handlers.ts @@ -0,0 +1 @@ +export const handlers = []; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index a058c0dd..5ad1ad5f 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es6", + "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "esModuleInterop": true, "module": "esnext", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b99110a2..d3ce549a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1707,6 +1707,28 @@ "@types/mdx" "^2.0.0" "@types/react" ">=16" +"@mswjs/cookies@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.2.tgz#b4e207bf6989e5d5427539c2443380a33ebb922b" + integrity sha512-mlN83YSrcFgk7Dm1Mys40DLssI1KdJji2CMKN8eOlBqsTADYzj2+jWzsANsUTFbxDMWPD5e9bfA1RGqBpS3O1g== + dependencies: + "@types/set-cookie-parser" "^2.4.0" + set-cookie-parser "^2.4.6" + +"@mswjs/interceptors@^0.17.5": + version "0.17.9" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.17.9.tgz#0096fc88fea63ee42e36836acae8f4ae33651c04" + integrity sha512-4LVGt03RobMH/7ZrbHqRxQrS9cc2uh+iNKSj8UWr8M26A2i793ju+csaB5zaqYltqJmA2jUq4VeYfKmVqvsXQg== + dependencies: + "@open-draft/until" "^1.0.3" + "@types/debug" "^4.1.7" + "@xmldom/xmldom" "^0.8.3" + debug "^4.3.3" + headers-polyfill "^3.1.0" + outvariant "^1.2.1" + strict-event-emitter "^0.2.4" + web-encoding "^1.1.5" + "@ndelangen/get-tarball@^3.0.7": version "3.0.9" resolved "https://registry.yarnpkg.com/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz#727ff4454e65f34707e742a59e5e6b1f525d8964" @@ -1747,6 +1769,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@open-draft/until@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" + integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== + "@pmmmwh/react-refresh-webpack-plugin@^0.5.5": version "0.5.10" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" @@ -2671,6 +2698,18 @@ dependencies: "@types/node" "*" +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/debug@^4.1.7": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.8.tgz#cef723a5d0a90990313faec2d1e22aee5eecb317" + integrity sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ== + dependencies: + "@types/ms" "*" + "@types/detect-port@^1.3.0": version "1.3.3" resolved "https://registry.yarnpkg.com/@types/detect-port/-/detect-port-1.3.3.tgz#124c5d4c283f48a21f80826bcf39433b3e64aa81" @@ -2801,6 +2840,11 @@ dependencies: "@types/istanbul-lib-report" "*" +"@types/js-levenshtein@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5" + integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== + "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -2836,6 +2880,11 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== +"@types/ms@*": + version "0.7.31" + resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" + integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== + "@types/node-fetch@^2.5.7", "@types/node-fetch@^2.6.4": version "2.6.4" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.4.tgz#1bc3a26de814f6bf466b25aeb1473fa1afe6a660" @@ -2944,6 +2993,13 @@ "@types/mime" "*" "@types/node" "*" +"@types/set-cookie-parser@^2.4.0": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz#b6a955219b54151bfebd4521170723df5e13caad" + integrity sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w== + dependencies: + "@types/node" "*" + "@types/sockjs@^0.3.33": version "0.3.33" resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" @@ -3216,6 +3272,11 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== +"@xmldom/xmldom@^0.8.3": + version "0.8.9" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.9.tgz#b6ef7457e826be8049667ae673eda7876eb049be" + integrity sha512-4VSbbcMoxc4KLjb1gs96SRmi7w4h1SF+fCoiK0XaQX62buCc1G5d0DC5bJ9xJBNPDSVCmIrcl8BiYxzjrqaaJA== + "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -3233,6 +3294,11 @@ dependencies: tslib "^2.4.0" +"@zxing/text-encoding@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" + integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -3330,6 +3396,13 @@ ajv@^8.0.0, ajv@^8.9.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-html-community@0.0.8, ansi-html-community@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" @@ -3835,6 +3908,14 @@ 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== +chalk@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -3844,7 +3925,7 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3852,7 +3933,12 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@^3.4.0, chokidar@^3.5.3: +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^3.4.0, chokidar@^3.4.2, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3920,6 +4006,11 @@ cli-table3@^0.6.1: optionalDependencies: "@colors/colors" "1.5.0" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -3929,6 +4020,15 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -4096,6 +4196,11 @@ cookie@0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookie@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + core-js-compat@^3.25.1, core-js-compat@^3.31.0: version "3.31.1" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" @@ -4199,7 +4304,7 @@ debug@2.6.9, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4858,7 +4963,7 @@ eventemitter3@^4.0.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^3.2.0: +events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -4920,6 +5025,15 @@ extend@^3.0.0: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extract-zip@^1.6.6: version "1.7.0" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" @@ -4999,6 +5113,13 @@ fetch-retry@^5.0.2: resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.6.tgz#17d0bc90423405b7a88b74355bf364acd2a7fa56" integrity sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ== +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -5421,6 +5542,11 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== +"graphql@^15.0.0 || ^16.0.0": + version "16.7.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642" + integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg== + gunzip-maybe@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" @@ -5506,6 +5632,11 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +headers-polyfill@^3.1.0, headers-polyfill@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.1.2.tgz#9a4dcb545c5b95d9569592ef7ec0708aab763fbe" + integrity sha512-tWCK4biJ6hcLqTviLXVR9DTRfYGQMXEIUj3gwJ2rZ5wO/at3XtkI4g8mCvFdUF9l1KMBNCfmNAdnahm1cgavQA== + hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -5649,7 +5780,7 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5715,6 +5846,27 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +inquirer@^8.2.0: + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -5879,6 +6031,11 @@ is-negative-zero@^2.0.2: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-node-process@^1.0.1, is-node-process@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" + integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== + is-number-object@^1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" @@ -6134,6 +6291,11 @@ jest-worker@^29.6.1: merge-stream "^2.0.0" supports-color "^8.0.0" +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6586,6 +6748,38 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msw-storybook-addon@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/msw-storybook-addon/-/msw-storybook-addon-1.8.0.tgz#090b55b9a586f3e1620782dc156e8d5ce951ab7a" + integrity sha512-dw3vZwqjixmiur0vouRSOax7wPSu9Og2Hspy9JZFHf49bZRjwDiLF0Pfn2NXEkGviYJOJiGxS1ejoTiUwoSg4A== + dependencies: + is-node-process "^1.0.1" + +msw@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.2.tgz#126c3150c07f651e97b24fbd405821f3aeaf9397" + integrity sha512-GsW3PE/Es/a1tYThXcM8YHOZ1S1MtivcS3He/LQbbTCx3rbWJYCtWD5XXyJ53KlNPT7O1VI9sCW3xMtgFe8XpQ== + dependencies: + "@mswjs/cookies" "^0.2.2" + "@mswjs/interceptors" "^0.17.5" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + chalk "4.1.1" + chokidar "^3.4.2" + cookie "^0.4.2" + graphql "^15.0.0 || ^16.0.0" + headers-polyfill "^3.1.2" + inquirer "^8.2.0" + is-node-process "^1.2.0" + js-levenshtein "^1.1.6" + node-fetch "^2.6.7" + outvariant "^1.4.0" + path-to-regexp "^6.2.0" + strict-event-emitter "^0.4.3" + type-fest "^2.19.0" + yargs "^17.3.1" + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" @@ -6594,6 +6788,11 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nanoid@^3.3.1, nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" @@ -6853,6 +7052,16 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +outvariant@^1.2.1, outvariant@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.0.tgz#e742e4bda77692da3eca698ef5bfac62d9fba06e" + integrity sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw== + p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -7005,6 +7214,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" + integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -7643,6 +7857,11 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -7650,6 +7869,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.5: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -7804,6 +8030,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.4.6: + version "2.6.0" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" + integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" @@ -8018,6 +8249,18 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +strict-event-emitter@^0.2.4: + version "0.2.8" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz#b4e768927c67273c14c13d20e19d5e6c934b47ca" + integrity sha512-KDf/ujU8Zud3YaLtMCcTI4xkZlZVIYxTLr+XIULexP+77EEVWixeXroLUXQXiVtH4XH2W7jr/3PT1v3zBuvc3A== + dependencies: + events "^3.3.0" + +strict-event-emitter@^0.4.3: + version "0.4.6" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.4.6.tgz#ff347c8162b3e931e3ff5f02cfce6772c3b07eb3" + integrity sha512-12KWeb+wixJohmnwNFerbyiBrAlq5qJLwIt38etRtKtmmHyDSoGlIqFE9wx+4IwG0aDjI7GV8tc8ZccjWZZtTg== + "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -8283,11 +8526,23 @@ through2@^2.0.3: readable-stream "~2.3.6" xtend "~4.0.1" +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + thunky@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -8335,7 +8590,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.4.0, tslib@^2.5.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== @@ -8364,6 +8619,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" @@ -8541,7 +8801,7 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util@^0.12.0, util@^0.12.4: +util@^0.12.0, util@^0.12.3, util@^0.12.4: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== @@ -8623,6 +8883,15 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +web-encoding@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/web-encoding/-/web-encoding-1.1.5.tgz#fc810cf7667364a6335c939913f5051d3e0c4864" + integrity sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA== + dependencies: + util "^0.12.3" + optionalDependencies: + "@zxing/text-encoding" "0.9.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -8903,6 +9172,11 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.9: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + yargs@^16.2.0: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" @@ -8916,6 +9190,19 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yauzl@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From dbcdc3961e641a60bc4eb1ef6a2a469603e7edf0 Mon Sep 17 00:00:00 2001 From: JFe <33208246+Go-Jaecheol@users.noreply.github.com> Date: Fri, 14 Jul 2023 14:33:17 +0900 Subject: [PATCH 004/185] =?UTF-8?q?[BE]=20test:=20=EC=9D=B8=EC=88=98?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF=20=EC=9E=91=EC=84=B1=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: rest assured 관련 라이브러리 추가 * feat: 상품 관련 엔티티들의 생성자 및 getter 추가 * test: 인수 테스트용 추상 클래스 추가 * test: 인수 테스트 공통 steps 추가 * feat: 카테고리 목록 정렬 관련 enum 추가(SortType, SortOrderType) * feat: 상품 관련 API response 생성 * test: 상품 관련 API에 대한 인수 테스트 작성 * test: ProductAcceptanceTest 에서 카테고리 목록 조회 관련 테스트의 위치 이동 * chore: category 패키지를 product 패키지로 이동 * test: ProductAcceptanceTest 에서 접근 제어자 수정 --- backend/build.gradle | 23 +-- .../domain/Category.java | 22 ++- .../domain/CategoryType.java | 2 +- .../com/funeat/product/domain/Product.java | 41 ++++- .../funeat/product/domain/SortOrderType.java | 5 + .../com/funeat/product/domain/SortType.java | 5 + .../persistence/CategoryRepository.java | 9 ++ .../persistence/ProductRepository.java | 9 ++ .../presentation/CategoryProductResponse.java | 52 ++++++ .../presentation/CategoryResponse.java | 26 +++ .../product/presentation/ProductResponse.java | 60 +++++++ .../main/java/com/funeat/tag/domain/Tag.java | 15 ++ .../java/com/funeat/tag/domain/TagDto.java | 24 +++ .../acceptance/common/AcceptanceTest.java | 23 +++ .../funeat/acceptance/common/CommonSteps.java | 26 +++ .../product/ProductAcceptanceTest.java | 151 ++++++++++++++++++ .../acceptance/product/ProductSteps.java | 50 ++++++ .../src/test/resources/application.properties | 6 + 18 files changed, 535 insertions(+), 14 deletions(-) rename backend/src/main/java/com/funeat/{category => product}/domain/Category.java (53%) rename backend/src/main/java/com/funeat/{category => product}/domain/CategoryType.java (56%) create mode 100644 backend/src/main/java/com/funeat/product/domain/SortOrderType.java create mode 100644 backend/src/main/java/com/funeat/product/domain/SortType.java create mode 100644 backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java create mode 100644 backend/src/main/java/com/funeat/product/persistence/ProductRepository.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/ProductResponse.java create mode 100644 backend/src/main/java/com/funeat/tag/domain/TagDto.java create mode 100644 backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java create mode 100644 backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java create mode 100644 backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java create mode 100644 backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java create mode 100644 backend/src/test/resources/application.properties diff --git a/backend/build.gradle b/backend/build.gradle index 421fffc8..8b6233ca 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,28 +1,29 @@ plugins { - id 'java' - id 'org.springframework.boot' version '2.7.13' - id 'io.spring.dependency-management' version '1.0.15.RELEASE' + id 'java' + id 'org.springframework.boot' version '2.7.13' + id 'io.spring.dependency-management' version '1.0.15.RELEASE' } group = 'com.funeat' version = '0.0.1-SNAPSHOT' java { - sourceCompatibility = '11' + sourceCompatibility = '11' } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-web' - runtimeOnly 'com.mysql:mysql-connector-j' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'com.h2database:h2' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + runtimeOnly 'com.mysql:mysql-connector-j' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + testRuntimeOnly 'com.h2database:h2' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/backend/src/main/java/com/funeat/category/domain/Category.java b/backend/src/main/java/com/funeat/product/domain/Category.java similarity index 53% rename from backend/src/main/java/com/funeat/category/domain/Category.java rename to backend/src/main/java/com/funeat/product/domain/Category.java index 90f079e3..03d1d4f5 100644 --- a/backend/src/main/java/com/funeat/category/domain/Category.java +++ b/backend/src/main/java/com/funeat/product/domain/Category.java @@ -1,4 +1,4 @@ -package com.funeat.category.domain; +package com.funeat.product.domain; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -18,4 +18,24 @@ public class Category { @Enumerated(EnumType.STRING) private CategoryType type; + + protected Category() { + } + + public Category(final String name, final CategoryType type) { + this.name = name; + this.type = type; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public CategoryType getType() { + return type; + } } diff --git a/backend/src/main/java/com/funeat/category/domain/CategoryType.java b/backend/src/main/java/com/funeat/product/domain/CategoryType.java similarity index 56% rename from backend/src/main/java/com/funeat/category/domain/CategoryType.java rename to backend/src/main/java/com/funeat/product/domain/CategoryType.java index ecd06d7d..ee55c80b 100644 --- a/backend/src/main/java/com/funeat/category/domain/CategoryType.java +++ b/backend/src/main/java/com/funeat/product/domain/CategoryType.java @@ -1,4 +1,4 @@ -package com.funeat.category.domain; +package com.funeat.product.domain; public enum CategoryType { FOOD, STORE diff --git a/backend/src/main/java/com/funeat/product/domain/Product.java b/backend/src/main/java/com/funeat/product/domain/Product.java index 1a17856d..16d19ef3 100644 --- a/backend/src/main/java/com/funeat/product/domain/Product.java +++ b/backend/src/main/java/com/funeat/product/domain/Product.java @@ -1,6 +1,5 @@ package com.funeat.product.domain; -import com.funeat.category.domain.Category; import com.funeat.member.domain.bookmark.ProductBookmark; import java.util.List; import javax.persistence.Entity; @@ -37,4 +36,44 @@ public class Product { @OneToMany(mappedBy = "product") private List productBookmarks; + + protected Product() { + } + + public Product(final String name, final Long price, final String image, final String content, + final Category category) { + this.name = name; + this.price = price; + this.image = image; + this.content = content; + this.category = category; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getPrice() { + return price; + } + + public String getImage() { + return image; + } + + public String getContent() { + return content; + } + + public Double getAverageRating() { + return averageRating; + } + + public Category getCategory() { + return category; + } } diff --git a/backend/src/main/java/com/funeat/product/domain/SortOrderType.java b/backend/src/main/java/com/funeat/product/domain/SortOrderType.java new file mode 100644 index 00000000..339b7402 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/domain/SortOrderType.java @@ -0,0 +1,5 @@ +package com.funeat.product.domain; + +public enum SortOrderType { + ASC, DESC +} diff --git a/backend/src/main/java/com/funeat/product/domain/SortType.java b/backend/src/main/java/com/funeat/product/domain/SortType.java new file mode 100644 index 00000000..d17a0be2 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/domain/SortType.java @@ -0,0 +1,5 @@ +package com.funeat.product.domain; + +public enum SortType { + PRICE +} diff --git a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java new file mode 100644 index 00000000..d52c5def --- /dev/null +++ b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java @@ -0,0 +1,9 @@ +package com.funeat.product.persistence; + +import com.funeat.product.domain.Category; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CategoryRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java new file mode 100644 index 00000000..3953cc03 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java @@ -0,0 +1,9 @@ +package com.funeat.product.persistence; + +import com.funeat.product.domain.Product; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java b/backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java new file mode 100644 index 00000000..8dc95173 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java @@ -0,0 +1,52 @@ +package com.funeat.product.presentation; + +import com.funeat.product.domain.Product; + +public class CategoryProductResponse { + + private final Long id; + private final String name; + private final Long price; + private final String image; + private final Double averageRating; + private final Long reviewCount; + + public CategoryProductResponse(final Long id, final String name, final Long price, final String image, + final Double averageRating, final Long reviewCount) { + this.id = id; + this.name = name; + this.price = price; + this.image = image; + this.averageRating = averageRating; + this.reviewCount = reviewCount; + } + + public static CategoryProductResponse toResponse(final Product product, final Long reviewCount) { + return new CategoryProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImage(), + product.getAverageRating(), reviewCount); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getPrice() { + return price; + } + + public String getImage() { + return image; + } + + public Double getAverageRating() { + return averageRating; + } + + public Long getReviewCount() { + return reviewCount; + } +} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java b/backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java new file mode 100644 index 00000000..b359aa92 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java @@ -0,0 +1,26 @@ +package com.funeat.product.presentation; + +import com.funeat.product.domain.Category; + +public class CategoryResponse { + + private final Long id; + private final String name; + + public CategoryResponse(final Long id, final String name) { + this.id = id; + this.name = name; + } + + public static CategoryResponse toResponse(final Category category) { + return new CategoryResponse(category.getId(), category.getName()); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductResponse.java b/backend/src/main/java/com/funeat/product/presentation/ProductResponse.java new file mode 100644 index 00000000..0645d9a2 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/ProductResponse.java @@ -0,0 +1,60 @@ +package com.funeat.product.presentation; + +import com.funeat.product.domain.Product; +import com.funeat.tag.domain.Tag; +import com.funeat.tag.domain.TagDto; +import java.util.ArrayList; +import java.util.List; + +public class ProductResponse { + + private final Long id; + private final String name; + private final Long price; + private final String image; + private final Double averageRating; + private final List tags; + + public ProductResponse(final Long id, final String name, final Long price, final String image, + final Double averageRating, final List tags) { + this.id = id; + this.name = name; + this.price = price; + this.image = image; + this.averageRating = averageRating; + this.tags = tags; + } + + public static ProductResponse toResponse(final Product product, List tags) { + List tagDtos = new ArrayList<>(); + for (Tag tag : tags) { + tagDtos.add(TagDto.toDto(tag)); + } + return new ProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImage(), + product.getAverageRating(), tagDtos); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Long getPrice() { + return price; + } + + public String getImage() { + return image; + } + + public Double getAverageRating() { + return averageRating; + } + + public List getTags() { + return tags; + } +} diff --git a/backend/src/main/java/com/funeat/tag/domain/Tag.java b/backend/src/main/java/com/funeat/tag/domain/Tag.java index f1f8e0d6..f59f9e3c 100644 --- a/backend/src/main/java/com/funeat/tag/domain/Tag.java +++ b/backend/src/main/java/com/funeat/tag/domain/Tag.java @@ -19,4 +19,19 @@ public class Tag { @OneToMany(mappedBy = "tag") private List reviewTags; + + protected Tag() { + } + + public Tag(final String name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } } diff --git a/backend/src/main/java/com/funeat/tag/domain/TagDto.java b/backend/src/main/java/com/funeat/tag/domain/TagDto.java new file mode 100644 index 00000000..16770f5e --- /dev/null +++ b/backend/src/main/java/com/funeat/tag/domain/TagDto.java @@ -0,0 +1,24 @@ +package com.funeat.tag.domain; + +public class TagDto { + + private final Long id; + private final String name; + + public TagDto(final Long id, final String name) { + this.id = id; + this.name = name; + } + + public static TagDto toDto(final Tag tag) { + return new TagDto(tag.getId(), tag.getName()); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java new file mode 100644 index 00000000..0d8ceb91 --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java @@ -0,0 +1,23 @@ +package com.funeat.acceptance.common; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public abstract class AcceptanceTest { + + @LocalServerPort + private int port; + + @BeforeEach + void setUp() { + RestAssured.port = port; + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java new file mode 100644 index 00000000..428eb94f --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java @@ -0,0 +1,26 @@ +package com.funeat.acceptance.common; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; + +@SuppressWarnings("NonAsciiCharacters") +public class CommonSteps { + + public static final HttpStatus 정상_처리 = HttpStatus.OK; + public static final HttpStatus 정상_생성 = HttpStatus.CREATED; + + public static Long LOCATION_헤더에서_ID_추출(final ExtractableResponse response) { + return Long.parseLong(response.header("Location").split("/")[2]); + } + + public static void LOCATION_헤더를_검증한다(final ExtractableResponse response) { + assertThat(response.header("Location")).isNotBlank(); + } + + public static void STATUS_CODE를_검증한다(final ExtractableResponse response, HttpStatus httpStatus) { + assertThat(response.statusCode()).isEqualTo(httpStatus.value()); + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java new file mode 100644 index 00000000..5f27391b --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java @@ -0,0 +1,151 @@ +package com.funeat.acceptance.product; + +import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; +import static com.funeat.acceptance.common.CommonSteps.정상_처리; +import static com.funeat.acceptance.product.ProductSteps.간편식사; +import static com.funeat.acceptance.product.ProductSteps.과자류; +import static com.funeat.acceptance.product.ProductSteps.상품_상세_조회_요청; +import static com.funeat.acceptance.product.ProductSteps.즉석조리; +import static com.funeat.acceptance.product.ProductSteps.카테고리_목록_조회_요청; +import static com.funeat.acceptance.product.ProductSteps.카테고리별_상품_목록_조회_요청; +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.acceptance.common.AcceptanceTest; +import com.funeat.product.domain.Category; +import com.funeat.product.domain.Product; +import com.funeat.product.domain.SortOrderType; +import com.funeat.product.domain.SortType; +import com.funeat.product.persistence.CategoryRepository; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.product.presentation.CategoryProductResponse; +import com.funeat.product.presentation.CategoryResponse; +import com.funeat.product.presentation.ProductResponse; +import io.restassured.common.mapper.TypeRef; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +class ProductAcceptanceTest extends AcceptanceTest { + + @Autowired + private ProductRepository productRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Test + void 카테고리별_상품_목록을_가격낮은순으로_조회한다() { + // given + final Long categoryId = 카테고리_추가_요청(간편식사); + final Product product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); + final Product product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", 간편식사); + final Product product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", 간편식사); + final List products = List.of(product1, product2, product3); + 복수_상품_추가_요청(products); + + // when + final var response = 카테고리별_상품_목록_조회_요청(categoryId, SortType.PRICE, SortOrderType.ASC, 1); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product1, product3, product2)); + } + + @Test + void 카테고리별_상품_목록을_가격높은순으로_조회한다() { + // given + final Long categoryId = 카테고리_추가_요청(간편식사); + final Product product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); + final Product product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", 간편식사); + final Product product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", 간편식사); + final List products = List.of(product1, product2, product3); + 복수_상품_추가_요청(products); + + // when + final var response = 카테고리별_상품_목록_조회_요청(categoryId, SortType.PRICE, SortOrderType.DESC, 1); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product2, product3, product1)); + } + + @Test + void 상품_상세_정보를_조회한다() { + // given + 카테고리_추가_요청(간편식사); + final Product product = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); + final Long productId = 상품_추가_요청(product); + + // when + final var response = 상품_상세_조회_요청(productId); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 상품_상세_정보_조회_결과를_검증한다(response, product); + } + + @Test + void 카테고리의_목록을_조회한다() { + // given + 카테고리_추가_요청(간편식사); + 카테고리_추가_요청(즉석조리); + 카테고리_추가_요청(과자류); + + // when + final var response = 카테고리_목록_조회_요청(); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 카테고리_목록_조회_결과를_검증한다(response, List.of(간편식사, 즉석조리, 과자류)); + } + + private Long 카테고리_추가_요청(final Category category) { + return categoryRepository.save(category).getId(); + } + + private Long 상품_추가_요청(final Product product) { + return productRepository.save(product).getId(); + } + + private void 복수_상품_추가_요청(final List products) { + productRepository.saveAll(products); + } + + private void 카테고리별_상품_목록_조회_결과를_검증한다(final ExtractableResponse response, final List products) { + final List expected = new ArrayList<>(); + for (Product product : products) { + expected.add(CategoryProductResponse.toResponse(product, 0L)); + } + + final List actual = response.jsonPath().getList("products"); + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "reviewCount") + .isEqualTo(expected); + } + + private void 상품_상세_정보_조회_결과를_검증한다(final ExtractableResponse response, final Product product) { + final ProductResponse expected = ProductResponse.toResponse(product, Collections.emptyList()); + + final ProductResponse actual = response.as(ProductResponse.class); + assertThat(actual).usingRecursiveComparison() + .ignoringFields("id", "averageRating") + .isEqualTo(expected); + } + + private void 카테고리_목록_조회_결과를_검증한다(final ExtractableResponse response, final List categories) { + final List expected = new ArrayList<>(); + for (Category category : categories) { + expected.add(CategoryResponse.toResponse(category)); + } + + final List actualResponses = response.as(new TypeRef<>() { + }); + assertThat(actualResponses).usingRecursiveComparison() + .ignoringFields("id") + .isEqualTo(expected); + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java new file mode 100644 index 00000000..45c172cd --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java @@ -0,0 +1,50 @@ +package com.funeat.acceptance.product; + +import static io.restassured.RestAssured.given; + +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; +import com.funeat.product.domain.SortOrderType; +import com.funeat.product.domain.SortType; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; + +@SuppressWarnings("NonAsciiCharacters") +public class ProductSteps { + + public static final Category 간편식사 = new Category("간편식사", CategoryType.FOOD); + public static final Category 즉석조리 = new Category("즉석조리", CategoryType.FOOD); + public static final Category 과자류 = new Category("과자류", CategoryType.FOOD); + public static final Category 아이스크림 = new Category("아이스크림", CategoryType.FOOD); + public static final Category 식품 = new Category("식품", CategoryType.FOOD); + public static final Category 음료 = new Category("음료", CategoryType.FOOD); + + public static ExtractableResponse 카테고리_목록_조회_요청() { + return given() + .when() + .get("/api/categories") + .then() + .extract(); + } + + public static ExtractableResponse 카테고리별_상품_목록_조회_요청(final Long categoryId, final SortType sortType, + final SortOrderType sortOrderType, + final Integer page) { + return given() + .queryParam("sort", sortType.name().toLowerCase()) + .queryParam("order", sortOrderType.name().toLowerCase()) + .queryParam("page", page) + .when() + .get("/api/categories/{category_id}/products", categoryId) + .then() + .extract(); + } + + public static ExtractableResponse 상품_상세_조회_요청(final Long productId) { + return given() + .when() + .get("/api/products/{product_id}", productId) + .then() + .extract(); + } +} diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties new file mode 100644 index 00000000..d79c430a --- /dev/null +++ b/backend/src/test/resources/application.properties @@ -0,0 +1,6 @@ +spring.datasource.url=jdbc:h2:mem:test;MODE=MySQL +spring.datasource.username=sa +spring.jpa.hibernate.ddl-auto=create +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.show-sql=true +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE From fed8c1d4d72166ec3c2b049ddf8b0af1d54f9e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Fri, 14 Jul 2023 22:36:06 +0900 Subject: [PATCH 005/185] =?UTF-8?q?chore:=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EC=84=A4=EC=B9=98=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/package.json | 1 + frontend/src/index.tsx | 5 ++++- frontend/yarn.lock | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index fee3f970..d194698d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { + "@fun-eat/design-system": "^0.1.0", "msw-storybook-addon": "^1.8.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index df1ef641..ebe83651 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; +import { FunEatProvider } from '@fun-eat/design-system'; const main = async () => { if (process.env.NODE_ENV === 'development') { @@ -13,6 +14,8 @@ await main(); const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); root.render( - + + + ); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d3ce549a..1ed1e491 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1558,6 +1558,11 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== +"@fun-eat/design-system@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.1.0.tgz#d2bc9a0f7bbff4092ed263cac4d907f2e031920d" + integrity sha512-PXwQFip9qlR/ILCk59Llth3BcEXegeNLcqKkzUYFEYjvlSLAfV1Lg0HSrOdGe7EyE+d1fWeWWviRDLKLnxWMXA== + "@humanwhocodes/config-array@^0.11.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" From 73c3467eaf3b292bb771128f45997c399ed4d465 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Fri, 14 Jul 2023 22:36:47 +0900 Subject: [PATCH 006/185] =?UTF-8?q?[FE]=20chore:=20import=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=20lint=20=EC=A0=81=EC=9A=A9=20(#35)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.babelrc.json | 2 +- frontend/.eslintrc.js | 95 ++++++++--- frontend/package.json | 3 + frontend/src/index.tsx | 1 + frontend/src/mocks/browser.ts | 1 + frontend/src/types/index.ts | 1 + frontend/yarn.lock | 313 ++++++++++++++++++++++++++++++++-- 7 files changed, 378 insertions(+), 38 deletions(-) create mode 100644 frontend/src/types/index.ts diff --git a/frontend/.babelrc.json b/frontend/.babelrc.json index b5cf683b..00ca841a 100644 --- a/frontend/.babelrc.json +++ b/frontend/.babelrc.json @@ -13,4 +13,4 @@ "@babel/preset-react" ], "plugins": [] -} \ No newline at end of file +} diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index f4307332..42d5cafd 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,40 +1,85 @@ module.exports = { env: { browser: true, - es2021: true + es2021: true, }, - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react/recommended', 'plugin:storybook/recommended'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react/recommended', + 'plugin:storybook/recommended', + 'plugin:import/recommended', + ], ignorePatterns: ['*.js'], - overrides: [{ - env: { - node: true + overrides: [ + { + env: { + node: true, + }, + files: ['.eslintrc.{js,cjs}'], + parserOptions: { + sourceType: 'script', + }, }, - files: ['.eslintrc.{js,cjs}'], - parserOptions: { - sourceType: 'script' - } - }], + ], parser: '@typescript-eslint/parser', parserOptions: { ecmaVersion: 'latest', sourceType: 'module', project: './tsconfig.json', - tsconfigRootDir: __dirname + tsconfigRootDir: __dirname, }, - plugins: ['@typescript-eslint', 'react'], + plugins: ['@typescript-eslint', 'react', 'import'], rules: { 'react/react-in-jsx-scope': 'off', '@typescript-eslint/no-var-requires': 0, - '@typescript-eslint/consistent-type-imports': ['error', { - prefer: 'type-imports', - disallowTypeAnnotations: false - }], - 'react/jsx-key': ['error', { - warnOnDuplicates: true - }], - 'react/self-closing-comp': ['error', { - component: true, - html: true - }] - } -}; \ No newline at end of file + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + disallowTypeAnnotations: false, + }, + ], + 'react/jsx-key': [ + 'error', + { + warnOnDuplicates: true, + }, + ], + 'react/self-closing-comp': [ + 'error', + { + component: true, + html: true, + }, + ], + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', ['parent', 'sibling', 'index'], 'object', 'unknown'], + pathGroups: [ + { + pattern: '@storybook/**', + group: 'external', + }, + { + pattern: '@*', + group: 'unknown', + }, + ], + pathGroupsExcludedImportTypes: ['unknown'], + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + 'newlines-between': 'always', + }, + ], + }, + settings: { + 'import/resolver': { + typescript: {}, + webpack: {}, + }, + }, +}; diff --git a/frontend/package.json b/frontend/package.json index d194698d..bd7bb355 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,6 +34,9 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "eslint": "^8.44.0", + "eslint-import-resolver-typescript": "^3.5.5", + "eslint-import-resolver-webpack": "^0.13.2", + "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", "eslint-plugin-storybook": "^0.6.12", "html-webpack-plugin": "^5.5.3", diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index ebe83651..cb7a5adc 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; + import App from './App'; import { FunEatProvider } from '@fun-eat/design-system'; diff --git a/frontend/src/mocks/browser.ts b/frontend/src/mocks/browser.ts index 750e031c..9c10cad9 100644 --- a/frontend/src/mocks/browser.ts +++ b/frontend/src/mocks/browser.ts @@ -1,4 +1,5 @@ import { setupWorker } from 'msw'; + import { handlers } from './handlers'; export const worker = setupWorker(...handlers); diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts new file mode 100644 index 00000000..0bb0c866 --- /dev/null +++ b/frontend/src/types/index.ts @@ -0,0 +1 @@ +export type BBBB = ''; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1ed1e491..4c5dd619 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1779,6 +1779,18 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== +"@pkgr/utils@^2.3.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" + integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== + dependencies: + cross-spawn "^7.0.3" + fast-glob "^3.3.0" + is-glob "^4.0.3" + open "^9.1.0" + picocolors "^1.0.0" + tslib "^2.6.0" + "@pmmmwh/react-refresh-webpack-plugin@^0.5.5": version "0.5.10" resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" @@ -2855,6 +2867,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/lodash@^4.14.167": version "4.14.195" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" @@ -3490,6 +3507,11 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" +array-find@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" + integrity sha512-kO/vVCacW9mnpn3WPWbTVlEnOabK2L7LWi2HViURtCM46y1zb6I8UMjx4LgbiqadTgHnLInUronwn3ampNTJtQ== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3844,6 +3866,13 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -4316,6 +4345,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: dependencies: ms "2.1.2" +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" @@ -4355,7 +4391,7 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-browser-id@3.0.0: +default-browser-id@3.0.0, default-browser-id@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== @@ -4363,6 +4399,16 @@ default-browser-id@3.0.0: bplist-parser "^0.2.0" untildify "^4.0.0" +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" @@ -4382,6 +4428,11 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -4615,7 +4666,16 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.5" -enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: +enhanced-resolve@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + integrity sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.12.0, enhanced-resolve@^5.15.0: version "5.15.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -4809,6 +4869,74 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + +eslint-import-resolver-typescript@^3.5.5: + version "3.5.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" + integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + get-tsconfig "^4.5.0" + globby "^13.1.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + synckit "^0.8.5" + +eslint-import-resolver-webpack@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.2.tgz#fc813df0d08b9265cc7072d22393bda5198bdc1e" + integrity sha512-XodIPyg1OgE2h5BDErz3WJoK7lawxKTJNhgPNafRST6csC/MZC+L5P6kKqsZGRInpbgc02s/WZMrb4uGJzcuRg== + dependencies: + array-find "^1.0.0" + debug "^3.2.7" + enhanced-resolve "^0.9.1" + find-root "^1.1.0" + has "^1.0.3" + interpret "^1.4.0" + is-core-module "^2.7.0" + is-regex "^1.1.4" + lodash "^4.17.21" + resolve "^1.20.0" + semver "^5.7.1" + +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@^2.27.5: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + eslint-plugin-react@^7.32.2: version "7.32.2" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" @@ -4988,6 +5116,21 @@ execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + express@^4.17.3: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -5054,7 +5197,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.9: +fast-glob@^3.2.9, fast-glob@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== @@ -5193,6 +5336,11 @@ find-cache-dir@^4.0.0: common-path-prefix "^3.0.0" pkg-dir "^7.0.0" +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -5419,7 +5567,7 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5432,6 +5580,13 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.6.2.tgz#831879a5e6c2aa24fe79b60340e2233a1e0f472e" + integrity sha512-E5XrT4CbbXcXWy+1jChlZmrmCwd5KGx502kDCXJJ7y898TtWW9FwoG5HfOLVRKmlmDGkWN2HM9Ho+/Y8F0sJDg== + dependencies: + resolve-pkg-maps "^1.0.0" + giget@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/giget/-/giget-1.1.2.tgz#f99a49cb0ff85479c8c3612cdc7ca27f2066e818" @@ -5530,6 +5685,17 @@ globby@^11.0.1, globby@^11.0.2, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +globby@^13.1.3: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -5785,6 +5951,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5802,7 +5973,7 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.2.4: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -5881,7 +6052,7 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" -interpret@^1.0.0: +interpret@^1.0.0, interpret@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== @@ -5960,7 +6131,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: +is-core-module@^2.11.0, is-core-module@^2.7.0, is-core-module@^2.9.0: version "2.12.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== @@ -5984,6 +6155,11 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -6013,6 +6189,13 @@ is-gzip@^1.0.0: resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -6105,6 +6288,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -6376,6 +6564,13 @@ 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== +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + json5@^2.1.2, json5@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" @@ -6607,6 +6802,11 @@ memoizerific@^1.11.3: dependencies: map-or-similar "^1.5.0" +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + integrity sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng== + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -6662,6 +6862,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -6686,7 +6891,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -6748,7 +6953,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3: +ms@2.1.3, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -6892,6 +7097,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + npmlog@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" @@ -7013,6 +7225,13 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + open@^7.0.3: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" @@ -7030,6 +7249,16 @@ open@^8.0.9, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -7209,6 +7438,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -7805,7 +8039,12 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0: +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.1: version "1.22.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== @@ -7862,6 +8101,13 @@ rimraf@~2.6.2: dependencies: glob "^7.1.3" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -7948,7 +8194,7 @@ selfsigned@^2.1.1: dependencies: node-forge "^1" -"semver@2 || 3 || 4 || 5", semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -8124,6 +8370,11 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" @@ -8337,11 +8588,21 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-final-newline@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -8420,6 +8681,19 @@ synchronous-promise@^2.0.15: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" integrity sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g== +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + +tapable@^0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + integrity sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ== + tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -8541,6 +8815,11 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -8590,12 +8869,22 @@ ts-loader@^9.4.4: micromatch "^4.0.0" semver "^7.3.4" +tsconfig-paths@^3.14.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0: +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== From d77f01f27b0aafcdd1b1e58315b0e2641af3def5 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Sun, 16 Jul 2023 23:07:03 +0900 Subject: [PATCH 007/185] =?UTF-8?q?[FE]=20SvgSprite,=20SvgIcon=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#39)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: mswDecorator가 잘못 import 되어있는 오류 해결 * chore: import order lint가 안되는 오류 해결 * chore: design system 버전 업그레이드 * feat: SvgSprite 컴포넌트 추가 * feat: SvgIcon 컴포넌트 추가 * refactor: svg icon variant 배열 네이밍 수정 * feat: index.ts에 svg 컴포넌트 import문 추가 * style: 불필요한 코드 제거 --- frontend/.eslintrc.js | 9 +++ frontend/.storybook/preview-body.html | 62 +++++++++++++++++++ frontend/.storybook/preview.ts | 2 +- frontend/package.json | 4 +- frontend/src/components/.gitkeep | 0 .../components/Common/Svg/SvgIcon.stories.tsx | 37 +++++++++++ .../src/components/Common/Svg/SvgIcon.tsx | 44 +++++++++++++ .../src/components/Common/Svg/SvgSprite.tsx | 53 ++++++++++++++++ frontend/src/components/Common/index.ts | 2 + frontend/src/index.tsx | 5 +- frontend/src/types/index.ts | 1 - frontend/yarn.lock | 8 +-- 12 files changed, 218 insertions(+), 9 deletions(-) create mode 100644 frontend/.storybook/preview-body.html delete mode 100644 frontend/src/components/.gitkeep create mode 100644 frontend/src/components/Common/Svg/SvgIcon.stories.tsx create mode 100644 frontend/src/components/Common/Svg/SvgIcon.tsx create mode 100644 frontend/src/components/Common/Svg/SvgSprite.tsx create mode 100644 frontend/src/components/Common/index.ts delete mode 100644 frontend/src/types/index.ts diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 42d5cafd..a5df31c5 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -62,6 +62,14 @@ module.exports = { pattern: '@storybook/**', group: 'external', }, + { + pattern: '@fun-eat/**', + group: 'external', + }, + { + pattern: '@*/**', + group: 'unknown', + }, { pattern: '@*', group: 'unknown', @@ -75,6 +83,7 @@ module.exports = { 'newlines-between': 'always', }, ], + 'import/no-unresolved': 'off', }, settings: { 'import/resolver': { diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html new file mode 100644 index 00000000..e6026e84 --- /dev/null +++ b/frontend/.storybook/preview-body.html @@ -0,0 +1,62 @@ + diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.ts index cd8ea156..02c7fda4 100644 --- a/frontend/.storybook/preview.ts +++ b/frontend/.storybook/preview.ts @@ -1,5 +1,5 @@ import type { Preview } from '@storybook/react'; -import mswDecorator from 'msw-storybook-addon'; +import { mswDecorator } from 'msw-storybook-addon'; import { handlers } from '../src/mocks/handlers'; export const decorators = [mswDecorator]; diff --git a/frontend/package.json b/frontend/package.json index bd7bb355..2f691e00 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,8 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { - "@fun-eat/design-system": "^0.1.0", - "msw-storybook-addon": "^1.8.0", + "@fun-eat/design-system": "^0.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", "styled-components": "^6.0.2" @@ -41,6 +40,7 @@ "eslint-plugin-storybook": "^0.6.12", "html-webpack-plugin": "^5.5.3", "msw": "^1.2.2", + "msw-storybook-addon": "^1.8.0", "prettier": "^2.8.8", "storybook": "^7.0.27", "ts-loader": "^9.4.4", diff --git a/frontend/src/components/.gitkeep b/frontend/src/components/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/components/Common/Svg/SvgIcon.stories.tsx b/frontend/src/components/Common/Svg/SvgIcon.stories.tsx new file mode 100644 index 00000000..87c7f7e0 --- /dev/null +++ b/frontend/src/components/Common/Svg/SvgIcon.stories.tsx @@ -0,0 +1,37 @@ +import { theme } from '@fun-eat/design-system'; +import type { Meta, StoryObj } from '@storybook/react'; + +import SvgIcon, { SVG_ICON_VARIANTS } from './SvgIcon'; + +const meta: Meta = { + title: 'common/SvgIcon', + component: SvgIcon, + argTypes: { + color: { + control: { + type: 'color', + }, + }, + }, + args: { + variant: 'recipe', + color: theme.colors.gray4, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Playground: Story = {}; + +export const SvgIcons: Story = { + render: () => { + return ( + <> + {SVG_ICON_VARIANTS.map((variant) => ( + + ))} + + ); + }, +}; diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx new file mode 100644 index 00000000..1d275dec --- /dev/null +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -0,0 +1,44 @@ +import { theme } from '@fun-eat/design-system'; +import type { ComponentPropsWithoutRef, CSSProperties } from 'react'; + +export const SVG_ICON_VARIANTS = [ + 'recipe', + 'list', + 'profile', + 'search', + 'arrow', + 'bookmark', + 'review', + 'star', + 'favorite', +] as const; +type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number]; + +interface SvgIconProps extends ComponentPropsWithoutRef<'svg'> { + /** + * SvgSprite 컴포넌트의 symbol id입니다. + */ + variant: SvgIconVariant; + /** + * SvgIcon의 색상입니다. (기본값 gray4) + */ + color?: CSSProperties['color']; + /** + * SvgIcon의 너비입니다. (기본값 24) + */ + width?: number; + /** + * SvgIcon의 높이입니다. (기본값 24) + */ + height?: number; +} + +const SvgIcon = ({ variant, width = 24, height = 24, color = theme.colors.gray4, ...props }: SvgIconProps) => { + return ( + + + + ); +}; + +export default SvgIcon; diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx new file mode 100644 index 00000000..db81f3fc --- /dev/null +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -0,0 +1,53 @@ +const SvgSprite = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default SvgSprite; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts new file mode 100644 index 00000000..8c89a684 --- /dev/null +++ b/frontend/src/components/Common/index.ts @@ -0,0 +1,2 @@ +export { default as SvgSprite } from './Svg/SvgSprite'; +export { default as SvgIcon } from './Svg/SvgIcon'; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index cb7a5adc..16c5bd98 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,8 +1,10 @@ +import { FunEatProvider } from '@fun-eat/design-system'; import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; -import { FunEatProvider } from '@fun-eat/design-system'; + +import { SvgSprite } from '@components/Common'; const main = async () => { if (process.env.NODE_ENV === 'development') { @@ -16,6 +18,7 @@ const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( + diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts deleted file mode 100644 index 0bb0c866..00000000 --- a/frontend/src/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export type BBBB = ''; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4c5dd619..529d9e2c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1558,10 +1558,10 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== -"@fun-eat/design-system@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.1.0.tgz#d2bc9a0f7bbff4092ed263cac4d907f2e031920d" - integrity sha512-PXwQFip9qlR/ILCk59Llth3BcEXegeNLcqKkzUYFEYjvlSLAfV1Lg0HSrOdGe7EyE+d1fWeWWviRDLKLnxWMXA== +"@fun-eat/design-system@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.1.1.tgz#31334395cee74e7a57e511e5a39d9653825f3709" + integrity sha512-9u23qDvq5TahQ+B/IGJuRjmfoPNIjXh2GxGid3IInbxDkFeQqH2yjOSamkPP+lxS44vKx1Tk5RfeLBKaoxwkuA== "@humanwhocodes/config-array@^0.11.10": version "0.11.10" From 2eaaddcb899adbd2be8c5e25ee9cfecdfab7d2aa Mon Sep 17 00:00:00 2001 From: Hanuel Lee <91522259+hanueleee@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:46:31 +0900 Subject: [PATCH 008/185] =?UTF-8?q?[BE]=20feat:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: repository Autowired 하는 부분을 AcceptanceTest 로 이동 * test: STORE 타입 카테고리 상수 추가 * feat: 공통 상품에 대한 카테고리 목록 조회 기능 추가 * feat: string으로 들어온 CategoryType을 enum으로 변환해주는 Converter 추가 * refactor: 카테고리 목록 조회 api 명세 수정 (기존) /api/categories (수정) /api/categories?type=food * feat: CategoryService에 Transactional(readOnly) 적용 * test: 카테고리 목록 조회 기능에 대한 repository 테스트 추가 * refactor: Service용 DTO 제거 --------- Co-authored-by: Go-Jaecheol --- .../common/StringToCategoryTypeConverter.java | 12 ++++ .../java/com/funeat/common/WebConfig.java | 14 ++++ .../product/application/CategoryService.java | 23 +++++++ .../persistence/CategoryRepository.java | 4 ++ .../presentation/CategoryController.java | 30 +++++++++ .../acceptance/common/AcceptanceTest.java | 9 +++ .../product/ProductAcceptanceTest.java | 22 +++---- .../acceptance/product/ProductSteps.java | 6 +- .../persistence/CategoryRepositoryTest.java | 64 +++++++++++++++++++ 9 files changed, 169 insertions(+), 15 deletions(-) create mode 100644 backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java create mode 100644 backend/src/main/java/com/funeat/common/WebConfig.java create mode 100644 backend/src/main/java/com/funeat/product/application/CategoryService.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/CategoryController.java create mode 100644 backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java diff --git a/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java b/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java new file mode 100644 index 00000000..642c82a8 --- /dev/null +++ b/backend/src/main/java/com/funeat/common/StringToCategoryTypeConverter.java @@ -0,0 +1,12 @@ +package com.funeat.common; + +import com.funeat.product.domain.CategoryType; +import org.springframework.core.convert.converter.Converter; + +public class StringToCategoryTypeConverter implements Converter { + + @Override + public CategoryType convert(final String source) { + return CategoryType.valueOf(source.toUpperCase()); + } +} diff --git a/backend/src/main/java/com/funeat/common/WebConfig.java b/backend/src/main/java/com/funeat/common/WebConfig.java new file mode 100644 index 00000000..752d744e --- /dev/null +++ b/backend/src/main/java/com/funeat/common/WebConfig.java @@ -0,0 +1,14 @@ +package com.funeat.common; + +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addFormatters(final FormatterRegistry registry) { + registry.addConverter(new StringToCategoryTypeConverter()); + } +} diff --git a/backend/src/main/java/com/funeat/product/application/CategoryService.java b/backend/src/main/java/com/funeat/product/application/CategoryService.java new file mode 100644 index 00000000..7b99b6f3 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/application/CategoryService.java @@ -0,0 +1,23 @@ +package com.funeat.product.application; + +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; +import com.funeat.product.persistence.CategoryRepository; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +public class CategoryService { + + private final CategoryRepository categoryRepository; + + public CategoryService(final CategoryRepository categoryRepository) { + this.categoryRepository = categoryRepository; + } + + public List findAllCategoriesByType(final CategoryType type) { + return categoryRepository.findAllByType(type); + } +} diff --git a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java index d52c5def..dba13e4b 100644 --- a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java +++ b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java @@ -1,9 +1,13 @@ package com.funeat.product.persistence; import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface CategoryRepository extends JpaRepository { + + List findAllByType(final CategoryType type); } diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java new file mode 100644 index 00000000..f514032c --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java @@ -0,0 +1,30 @@ +package com.funeat.product.presentation; + +import com.funeat.product.application.CategoryService; +import com.funeat.product.domain.CategoryType; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/categories") +public class CategoryController { + + private final CategoryService categoryService; + + public CategoryController(final CategoryService categoryService) { + this.categoryService = categoryService; + } + + @GetMapping + public ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type) { + final List responses = categoryService.findAllCategoriesByType(type).stream() + .map(CategoryResponse::toResponse) + .collect(Collectors.toList()); + return ResponseEntity.ok(responses); + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java index 0d8ceb91..cd7f583b 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java @@ -1,9 +1,12 @@ package com.funeat.acceptance.common; +import com.funeat.product.persistence.CategoryRepository; +import com.funeat.product.persistence.ProductRepository; import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; @@ -16,6 +19,12 @@ public abstract class AcceptanceTest { @LocalServerPort private int port; + @Autowired + public ProductRepository productRepository; + + @Autowired + public CategoryRepository categoryRepository; + @BeforeEach void setUp() { RestAssured.port = port; diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java index 5f27391b..4d5e8a23 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java @@ -2,11 +2,12 @@ import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; import static com.funeat.acceptance.common.CommonSteps.정상_처리; +import static com.funeat.acceptance.product.ProductSteps.CU; import static com.funeat.acceptance.product.ProductSteps.간편식사; +import static com.funeat.acceptance.product.ProductSteps.공통_상품_카테고리_목록_조회_요청; import static com.funeat.acceptance.product.ProductSteps.과자류; import static com.funeat.acceptance.product.ProductSteps.상품_상세_조회_요청; import static com.funeat.acceptance.product.ProductSteps.즉석조리; -import static com.funeat.acceptance.product.ProductSteps.카테고리_목록_조회_요청; import static com.funeat.acceptance.product.ProductSteps.카테고리별_상품_목록_조회_요청; import static org.assertj.core.api.Assertions.assertThat; @@ -15,8 +16,6 @@ import com.funeat.product.domain.Product; import com.funeat.product.domain.SortOrderType; import com.funeat.product.domain.SortType; -import com.funeat.product.persistence.CategoryRepository; -import com.funeat.product.persistence.ProductRepository; import com.funeat.product.presentation.CategoryProductResponse; import com.funeat.product.presentation.CategoryResponse; import com.funeat.product.presentation.ProductResponse; @@ -27,17 +26,10 @@ import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; @SuppressWarnings("NonAsciiCharacters") class ProductAcceptanceTest extends AcceptanceTest { - @Autowired - private ProductRepository productRepository; - - @Autowired - private CategoryRepository categoryRepository; - @Test void 카테고리별_상품_목록을_가격낮은순으로_조회한다() { // given @@ -90,18 +82,19 @@ class ProductAcceptanceTest extends AcceptanceTest { } @Test - void 카테고리의_목록을_조회한다() { + void 공통_상품_카테고리의_목록을_조회한다() { // given 카테고리_추가_요청(간편식사); 카테고리_추가_요청(즉석조리); 카테고리_추가_요청(과자류); + 카테고리_추가_요청(CU); // when - final var response = 카테고리_목록_조회_요청(); + final var response = 공통_상품_카테고리_목록_조회_요청(); // then STATUS_CODE를_검증한다(response, 정상_처리); - 카테고리_목록_조회_결과를_검증한다(response, List.of(간편식사, 즉석조리, 과자류)); + 공통_상품_카테고리_목록_조회_결과를_검증한다(response, List.of(간편식사, 즉석조리, 과자류)); } private Long 카테고리_추가_요청(final Category category) { @@ -136,7 +129,8 @@ class ProductAcceptanceTest extends AcceptanceTest { .isEqualTo(expected); } - private void 카테고리_목록_조회_결과를_검증한다(final ExtractableResponse response, final List categories) { + private void 공통_상품_카테고리_목록_조회_결과를_검증한다(final ExtractableResponse response, + final List categories) { final List expected = new ArrayList<>(); for (Category category : categories) { expected.add(CategoryResponse.toResponse(category)); diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java index 45c172cd..dfaf236b 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java @@ -18,9 +18,13 @@ public class ProductSteps { public static final Category 아이스크림 = new Category("아이스크림", CategoryType.FOOD); public static final Category 식품 = new Category("식품", CategoryType.FOOD); public static final Category 음료 = new Category("음료", CategoryType.FOOD); + public static final Category CU = new Category("CU", CategoryType.STORE); + public static final Category GS25 = new Category("GS25", CategoryType.STORE); + public static final Category EMART24 = new Category("EMART24", CategoryType.STORE); - public static ExtractableResponse 카테고리_목록_조회_요청() { + public static ExtractableResponse 공통_상품_카테고리_목록_조회_요청() { return given() + .queryParam("type", "food") .when() .get("/api/categories") .then() diff --git a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java new file mode 100644 index 00000000..5537da76 --- /dev/null +++ b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java @@ -0,0 +1,64 @@ +package com.funeat.product.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class CategoryRepositoryTest { + + public static final Category 간편식사 = new Category("간편식사", CategoryType.FOOD); + public static final Category 즉석조리 = new Category("즉석조리", CategoryType.FOOD); + public static final Category 과자류 = new Category("과자류", CategoryType.FOOD); + public static final Category 아이스크림 = new Category("아이스크림", CategoryType.FOOD); + public static final Category 식품 = new Category("식품", CategoryType.FOOD); + public static final Category 음료 = new Category("음료", CategoryType.FOOD); + public static final Category CU = new Category("CU", CategoryType.STORE); + public static final Category GS25 = new Category("GS25", CategoryType.STORE); + public static final Category EMART24 = new Category("EMART24", CategoryType.STORE); + + @Autowired + private CategoryRepository categoryRepository; + + @BeforeEach + void setUp() { + categoryRepository.deleteAll(); + categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); + } + + @Test + void 카테고리_타입이_FOOD인_모든_카테고리를_조회한다() { + // given + final CategoryType foodType = CategoryType.FOOD; + + // when + final List actual = categoryRepository.findAllByType(foodType); + + // then + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields() + .containsOnly(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료); + } + + @Test + void 카테고리_타입이_STORE인_모든_카테고리를_조회한다() { + // given + final CategoryType storeType = CategoryType.STORE; + + // when + final List actual = categoryRepository.findAllByType(storeType); + + // then + assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields() + .containsOnly(CU, GS25, EMART24); + } +} From 3615fe01561c1505cf8b71ac8bd36890ece92c92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Mon, 17 Jul 2023 14:41:53 +0900 Subject: [PATCH 009/185] =?UTF-8?q?[BE]=20feat:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 리뷰 작성 인수테스트 추가 * feat: 리뷰 서비스 작성 기능 구현 * feat: 리뷰 작성 시 필요한 entity 수정 및 repository 추가 * refactor: 사용하지 않는 메소드 삭제 및 필요한 생성자 추가 * style: 주석 제거 * refactor: 패키지 이동, 인수테스트 클래스명 수정 * refactor: 서비스 테스트 통합 테스트로 수정 * refactor: 멤버와 상품 Optional로 조회 후 검증 * test: TagRepository 커스텀 메소드 테스트 * refactor: 리뷰서비스 테스트 조건 수정 * feat: 리뷰 등록시 이미지 저장 경로 분리, 환경변수 설정 추가 * feat: 이미지 업로드 시 빈파일 생성 이슈 해결 - dev, test 분리하여 test 시에는 임시 폴더, 파일생성 후 삭제로직 구현 * style: 코드 컨벤션따라 수정 * style: 테스트 코드 컨벤션따라 수정 * refactor: beforeEach deleteAll 로 초기화 후 테스트 시작 * refactor: cascade 옵션 삭제, review 편의메소드 추가 - 정적팩토리 메소드로 가독성 높임 * refactor: Tag 에서 ReviewTag 양방향 매핑 삭제 - Tag 를 조회해 리뷰 접근 가능성 있을 때 추가 예정 * refactor: ReviewTag 조회 시, 매핑된 엔티티 fetch 전략 수정 * refactor: TestImageUploader 패키지 이동 * refactor: entity, repository 수정 * feat: Repository 추가 * refactor: @Repository 삭제 * feat: repository 추가 --- .../java/com/funeat/config/EnvProperties.java | 12 ++ .../java/com/funeat/member/domain/Member.java | 16 +++ .../member/persistence/MemberRepository.java | 7 ++ .../ProductBookmarkRepository.java | 7 ++ .../persistence/RecipeBookMarkRepository.java | 7 ++ .../persistence/RecipeFavoriteRepository.java | 7 ++ .../persistence/ReviewFavoriteRepository.java | 7 ++ .../persistence/CategoryRepository.java | 2 - .../persistence/ProductRecipeRepository.java | 7 ++ .../persistence/ProductRepository.java | 2 - .../persistence/RecipeImageRepository.java | 7 ++ .../recipe/persistence/RecipeRepository.java | 7 ++ .../review/application/ImageService.java | 8 ++ .../review/application/ImageUploader.java | 29 +++++ .../review/application/ReviewService.java | 62 ++++++++++ .../java/com/funeat/review/domain/Review.java | 69 ++++++++++- .../com/funeat/review/domain/ReviewTag.java | 19 ++- .../review/persistence/ReviewRepository.java | 15 +++ .../persistence/ReviewTagRepository.java | 7 ++ .../review/presentation/ReviewController.java | 31 +++++ .../presentation/dto/ReviewCreateRequest.java | 41 +++++++ .../main/java/com/funeat/tag/domain/Tag.java | 6 - .../funeat/tag/persistence/TagRepository.java | 10 ++ .../main/resources/properties/env.properties | 1 + .../review/ReviewAcceptanceTest.java | 109 ++++++++++++++++++ .../review/application/ReviewServiceTest.java | 102 ++++++++++++++++ .../review/application/TestImageUploader.java | 41 +++++++ .../tag/persistence/TagRepositoryTest.java | 44 +++++++ .../src/test/resources/application.properties | 1 + 29 files changed, 670 insertions(+), 13 deletions(-) create mode 100644 backend/src/main/java/com/funeat/config/EnvProperties.java create mode 100644 backend/src/main/java/com/funeat/member/persistence/MemberRepository.java create mode 100644 backend/src/main/java/com/funeat/member/persistence/ProductBookmarkRepository.java create mode 100644 backend/src/main/java/com/funeat/member/persistence/RecipeBookMarkRepository.java create mode 100644 backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java create mode 100644 backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java create mode 100644 backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java create mode 100644 backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java create mode 100644 backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java create mode 100644 backend/src/main/java/com/funeat/review/application/ImageService.java create mode 100644 backend/src/main/java/com/funeat/review/application/ImageUploader.java create mode 100644 backend/src/main/java/com/funeat/review/application/ReviewService.java create mode 100644 backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java create mode 100644 backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/ReviewController.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java create mode 100644 backend/src/main/java/com/funeat/tag/persistence/TagRepository.java create mode 100644 backend/src/main/resources/properties/env.properties create mode 100644 backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java create mode 100644 backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java create mode 100644 backend/src/test/java/com/funeat/review/application/TestImageUploader.java create mode 100644 backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java diff --git a/backend/src/main/java/com/funeat/config/EnvProperties.java b/backend/src/main/java/com/funeat/config/EnvProperties.java new file mode 100644 index 00000000..11a5cc86 --- /dev/null +++ b/backend/src/main/java/com/funeat/config/EnvProperties.java @@ -0,0 +1,12 @@ +package com.funeat.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.PropertySources; + +@Configuration +@PropertySources({ + @PropertySource("classpath:properties/env.properties") +}) +public class EnvProperties { +} diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java index 27adbc3a..05f9b5bd 100644 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -42,4 +42,20 @@ public class Member { @OneToMany(mappedBy = "member") private List recipeBookmarks; + + protected Member() { + } + + public Member(final String nickName, final String profileImage, final Integer age, final Gender gender, + final String phoneNumber) { + this.nickname = nickName; + this.profileImage = profileImage; + this.age = age; + this.gender = gender; + this.phoneNumber = phoneNumber; + } + + public Long getId() { + return id; + } } diff --git a/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java b/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java new file mode 100644 index 00000000..4bad6f3f --- /dev/null +++ b/backend/src/main/java/com/funeat/member/persistence/MemberRepository.java @@ -0,0 +1,7 @@ +package com.funeat.member.persistence; + +import com.funeat.member.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MemberRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/member/persistence/ProductBookmarkRepository.java b/backend/src/main/java/com/funeat/member/persistence/ProductBookmarkRepository.java new file mode 100644 index 00000000..c7651b59 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/persistence/ProductBookmarkRepository.java @@ -0,0 +1,7 @@ +package com.funeat.member.persistence; + +import com.funeat.member.domain.bookmark.ProductBookmark; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProductBookmarkRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/member/persistence/RecipeBookMarkRepository.java b/backend/src/main/java/com/funeat/member/persistence/RecipeBookMarkRepository.java new file mode 100644 index 00000000..4ed5cce4 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/persistence/RecipeBookMarkRepository.java @@ -0,0 +1,7 @@ +package com.funeat.member.persistence; + +import com.funeat.member.domain.bookmark.RecipeBookmark; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeBookMarkRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java b/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java new file mode 100644 index 00000000..75fdbf01 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/persistence/RecipeFavoriteRepository.java @@ -0,0 +1,7 @@ +package com.funeat.member.persistence; + +import com.funeat.member.domain.favorite.RecipeFavorite; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeFavoriteRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java b/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java new file mode 100644 index 00000000..970b56b1 --- /dev/null +++ b/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java @@ -0,0 +1,7 @@ +package com.funeat.member.persistence; + +import com.funeat.member.domain.favorite.ReviewFavorite; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewFavoriteRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java index dba13e4b..4da06451 100644 --- a/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java +++ b/backend/src/main/java/com/funeat/product/persistence/CategoryRepository.java @@ -4,9 +4,7 @@ import com.funeat.product.domain.CategoryType; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -@Repository public interface CategoryRepository extends JpaRepository { List findAllByType(final CategoryType type); diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java new file mode 100644 index 00000000..e30bb6c7 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/persistence/ProductRecipeRepository.java @@ -0,0 +1,7 @@ +package com.funeat.product.persistence; + +import com.funeat.product.domain.ProductRecipe; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ProductRecipeRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java index 3953cc03..bab5a7e1 100644 --- a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java +++ b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java @@ -2,8 +2,6 @@ import com.funeat.product.domain.Product; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -@Repository public interface ProductRepository extends JpaRepository { } diff --git a/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java b/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java new file mode 100644 index 00000000..3a1c8c14 --- /dev/null +++ b/backend/src/main/java/com/funeat/recipe/persistence/RecipeImageRepository.java @@ -0,0 +1,7 @@ +package com.funeat.recipe.persistence; + +import com.funeat.recipe.domain.RecipeImage; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeImageRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java b/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java new file mode 100644 index 00000000..83cb43d5 --- /dev/null +++ b/backend/src/main/java/com/funeat/recipe/persistence/RecipeRepository.java @@ -0,0 +1,7 @@ +package com.funeat.recipe.persistence; + +import com.funeat.recipe.domain.Recipe; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RecipeRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/review/application/ImageService.java b/backend/src/main/java/com/funeat/review/application/ImageService.java new file mode 100644 index 00000000..5b17a756 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/application/ImageService.java @@ -0,0 +1,8 @@ +package com.funeat.review.application; + +import org.springframework.web.multipart.MultipartFile; + +public interface ImageService { + + void upload(final MultipartFile image); +} diff --git a/backend/src/main/java/com/funeat/review/application/ImageUploader.java b/backend/src/main/java/com/funeat/review/application/ImageUploader.java new file mode 100644 index 00000000..6d5103e2 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/application/ImageUploader.java @@ -0,0 +1,29 @@ +package com.funeat.review.application; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +@Profile("dev") +public class ImageUploader implements ImageService { + + @Value("${review.image.path}") + private String imagePath; + + @Override + public void upload(final MultipartFile image) { + final String originalImageName = image.getOriginalFilename(); + final Path path = Paths.get(imagePath + originalImageName); + try { + Files.write(path, image.getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java new file mode 100644 index 00000000..ee1833d1 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -0,0 +1,62 @@ +package com.funeat.review.application; + +import com.funeat.member.domain.Member; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.domain.Review; +import com.funeat.review.domain.ReviewTag; +import com.funeat.review.persistence.ReviewRepository; +import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.tag.domain.Tag; +import com.funeat.tag.persistence.TagRepository; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Transactional(readOnly = true) +public class ReviewService { + + private final ReviewRepository reviewRepository; + private final TagRepository tagRepository; + private final ReviewTagRepository reviewTagRepository; + private final MemberRepository memberRepository; + private final ProductRepository productRepository; + private final ImageService imageService; + + public ReviewService(final ReviewRepository reviewRepository, final TagRepository tagRepository, + final ReviewTagRepository reviewTagRepository, final MemberRepository memberRepository, + final ProductRepository productRepository, final ImageService imageService) { + this.reviewRepository = reviewRepository; + this.tagRepository = tagRepository; + this.reviewTagRepository = reviewTagRepository; + this.memberRepository = memberRepository; + this.productRepository = productRepository; + this.imageService = imageService; + } + + @Transactional + public void create(final Long productId, final MultipartFile image, final ReviewCreateRequest reviewRequest) { + final Member findMember = memberRepository.findById(reviewRequest.getMemberId()) + .orElseThrow(IllegalArgumentException::new); + final Product findProduct = productRepository.findById(productId) + .orElseThrow(IllegalArgumentException::new); + + final Review savedReview = reviewRepository.save( + new Review(findMember, findProduct, image.getOriginalFilename(), reviewRequest.getRating(), + reviewRequest.getContent(), reviewRequest.getReBuy())); + + final List findTags = tagRepository.findTagsByIdIn(reviewRequest.getTagIds()); + + final List reviewTags = findTags.stream() + .map(findTag -> ReviewTag.createReviewTag(savedReview, findTag)) + .collect(Collectors.toList()); + + imageService.upload(image); + reviewTagRepository.saveAll(reviewTags); + } +} diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java index a355e09b..40b997ad 100644 --- a/backend/src/main/java/com/funeat/review/domain/Review.java +++ b/backend/src/main/java/com/funeat/review/domain/Review.java @@ -3,6 +3,7 @@ import com.funeat.member.domain.Member; import com.funeat.member.domain.favorite.ReviewFavorite; import com.funeat.product.domain.Product; +import java.util.ArrayList; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; @@ -36,8 +37,74 @@ public class Review { private Member member; @OneToMany(mappedBy = "review") - private List reviewTags; + private List reviewTags = new ArrayList<>(); @OneToMany(mappedBy = "review") private List reviewFavorites; + + private Long favoriteCount = 0L; + + protected Review() { + } + + public Review(final Member member, final Product findProduct, final String image, final Double rating, + final String content, final Boolean reBuy) { + this.member = member; + this.product = findProduct; + this.image = image; + this.rating = rating; + this.content = content; + this.reBuy = reBuy; + } + + public Review(final Member member, final Product findProduct, final String image, final Double rating, + final String content, final Boolean reBuy, final Long favoriteCount) { + this.member = member; + this.product = findProduct; + this.image = image; + this.rating = rating; + this.content = content; + this.reBuy = reBuy; + this.favoriteCount = favoriteCount; + } + + public Long getId() { + return id; + } + + public String getImage() { + return image; + } + + public Double getRating() { + return rating; + } + + public String getContent() { + return content; + } + + public Boolean getReBuy() { + return reBuy; + } + + public Product getProduct() { + return product; + } + + public Member getMember() { + return member; + } + + public List getReviewTags() { + return reviewTags; + } + + public List getReviewFavorites() { + return reviewFavorites; + } + + public Long getFavoriteCount() { + return favoriteCount; + } } diff --git a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java index 3f748ad4..09f0b203 100644 --- a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java +++ b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java @@ -2,6 +2,7 @@ import com.funeat.tag.domain.Tag; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @@ -15,11 +16,25 @@ public class ReviewTag { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "review_id") private Review review; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "tag_id") private Tag tag; + + protected ReviewTag() { + } + + private ReviewTag(final Review review, final Tag tag) { + this.review = review; + this.tag = tag; + } + + public static ReviewTag createReviewTag(final Review review, final Tag tag) { + final ReviewTag reviewTag = new ReviewTag(review, tag); + review.getReviewTags().add(reviewTag); + return reviewTag; + } } diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java new file mode 100644 index 00000000..3af73239 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java @@ -0,0 +1,15 @@ +package com.funeat.review.persistence; + +import com.funeat.product.domain.Product; +import com.funeat.review.domain.Review; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewRepository extends JpaRepository { + + Page findReviewsByProduct(final Pageable pageable, final Product product); + + List findReviewsByProductOrderByFavoriteCountDesc(final Pageable pageable, final Product product); +} diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java new file mode 100644 index 00000000..d69b7c97 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java @@ -0,0 +1,7 @@ +package com.funeat.review.persistence; + +import com.funeat.review.domain.ReviewTag; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewTagRepository extends JpaRepository { +} diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java new file mode 100644 index 00000000..0b0e3a47 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -0,0 +1,31 @@ +package com.funeat.review.presentation; + +import com.funeat.review.application.ReviewService; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import java.net.URI; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +public class ReviewController { + + private final ReviewService reviewService; + + public ReviewController(final ReviewService reviewService) { + this.reviewService = reviewService; + } + + @PostMapping(value = "/api/products/{productId}/reviews", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, + MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity writeReview(@PathVariable Long productId, @RequestPart MultipartFile image, + @RequestPart ReviewCreateRequest reviewRequest) { + reviewService.create(productId, image, reviewRequest); + + return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); + } +} diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java new file mode 100644 index 00000000..93a55377 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java @@ -0,0 +1,41 @@ +package com.funeat.review.presentation.dto; + +import java.util.List; + +public class ReviewCreateRequest { + + private final Double rating; + private final List tagIds; + private final String content; + private final Boolean reBuy; + private final Long memberId; + + public ReviewCreateRequest(final Double rating, final List tagIds, final String content, final Boolean reBuy, + final Long memberId) { + this.rating = rating; + this.tagIds = tagIds; + this.content = content; + this.reBuy = reBuy; + this.memberId = memberId; + } + + public Double getRating() { + return rating; + } + + public String getContent() { + return content; + } + + public Boolean getReBuy() { + return reBuy; + } + + public Long getMemberId() { + return memberId; + } + + public List getTagIds() { + return tagIds; + } +} diff --git a/backend/src/main/java/com/funeat/tag/domain/Tag.java b/backend/src/main/java/com/funeat/tag/domain/Tag.java index f59f9e3c..22edc07e 100644 --- a/backend/src/main/java/com/funeat/tag/domain/Tag.java +++ b/backend/src/main/java/com/funeat/tag/domain/Tag.java @@ -1,12 +1,9 @@ package com.funeat.tag.domain; -import com.funeat.review.domain.ReviewTag; -import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.OneToMany; @Entity public class Tag { @@ -17,9 +14,6 @@ public class Tag { private String name; - @OneToMany(mappedBy = "tag") - private List reviewTags; - protected Tag() { } diff --git a/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java b/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java new file mode 100644 index 00000000..2730bdbc --- /dev/null +++ b/backend/src/main/java/com/funeat/tag/persistence/TagRepository.java @@ -0,0 +1,10 @@ +package com.funeat.tag.persistence; + +import com.funeat.tag.domain.Tag; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TagRepository extends JpaRepository { + + List findTagsByIdIn(final List tagIds); +} diff --git a/backend/src/main/resources/properties/env.properties b/backend/src/main/resources/properties/env.properties new file mode 100644 index 00000000..ce793659 --- /dev/null +++ b/backend/src/main/resources/properties/env.properties @@ -0,0 +1 @@ +review.image.path=/Users/wugawuga/fun-eat/review/images/ diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java new file mode 100644 index 00000000..f3dcfec5 --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -0,0 +1,109 @@ +package com.funeat.acceptance.review; + +import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; +import static com.funeat.acceptance.common.CommonSteps.정상_생성; +import static io.restassured.RestAssured.given; + +import com.funeat.acceptance.common.AcceptanceTest; +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.persistence.ReviewRepository; +import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.tag.domain.Tag; +import com.funeat.tag.persistence.TagRepository; +import io.restassured.builder.MultiPartSpecBuilder; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import io.restassured.specification.MultiPartSpecification; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +@SuppressWarnings("NonAsciiCharacters") +class ReviewAcceptanceTest extends AcceptanceTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ProductRepository productRepository; + + @Autowired + private TagRepository tagRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private ReviewTagRepository reviewTagRepository; + + @BeforeEach + void init() { + reviewTagRepository.deleteAll(); + memberRepository.deleteAll(); + productRepository.deleteAll(); + tagRepository.deleteAll(); + reviewRepository.deleteAll(); + } + + @Test + void 리뷰를_작성한다() { + // given + final Long savedMemberId = 멤버_추가_요청(); + final Long savedProductId = 상품_추가_요청(); + final List savedTagIds = 태그_추가_요청(); + final MultiPartSpecification image = 리뷰_사진_명세_요청(); + + final var request = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + + // when + final var response = 리뷰_추가_요청(savedProductId, image, request); + + // then + STATUS_CODE를_검증한다(response, 정상_생성); + } + + private ExtractableResponse 리뷰_추가_요청(final Long productId, final MultiPartSpecification image, + final ReviewCreateRequest request) { + return given() + .multiPart(image) + .multiPart("reviewRequest", request, "application/json") + .when() + .post("/api/products/{productId}/reviews", productId) + .then() + .extract(); + } + + private MultiPartSpecification 리뷰_사진_명세_요청() { + return new MultiPartSpecBuilder("image".getBytes()) + .fileName("testImage.png") + .controlName("image") + .mimeType("image/png") + .build(); + } + + private List 태그_추가_요청() { + final Tag testTag1 = tagRepository.save(new Tag("testTag1")); + final Tag testTag2 = tagRepository.save(new Tag("testTag2")); + + return List.of(testTag1.getId(), testTag2.getId()); + } + + private Long 상품_추가_요청() { + final Product testProduct = productRepository.save(new Product("testName", 1000L, "test.png", "test", null)); + + return testProduct.getId(); + } + + private Long 멤버_추가_요청() { + final Member testMember = memberRepository.save( + new Member("test", "image.png", 27, Gender.FEMALE, "01036551086")); + + return testMember.getId(); + } +} diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java new file mode 100644 index 00000000..c3578885 --- /dev/null +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -0,0 +1,102 @@ +package com.funeat.review.application; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.domain.Review; +import com.funeat.review.persistence.ReviewRepository; +import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.tag.domain.Tag; +import com.funeat.tag.persistence.TagRepository; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockMultipartFile; + +@SpringBootTest +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class ReviewServiceTest { + + @Autowired + private ReviewService reviewService; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private TagRepository tagRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ProductRepository productRepository; + + @Autowired + private ReviewTagRepository reviewTagRepository; + + @BeforeEach + void init() { + reviewTagRepository.deleteAll(); + reviewRepository.deleteAll(); + memberRepository.deleteAll(); + productRepository.deleteAll(); + tagRepository.deleteAll(); + } + + @Test + void 리뷰를_추가할_수_있다() { + // given + var member = 멤버_추가_요청(); + var product = 상품_추가_요청(); + var tags = 태그_추가_요청(); + var tagIds = tags.stream() + .map(Tag::getId) + .collect(Collectors.toList()); + var image = 리뷰_페이크_사진_요청(); + var request = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + + // when + reviewService.create(product.getId(), image, request); + var result = reviewRepository.findAll(); + + // then + assertThat(result.get(0)).usingRecursiveComparison() + .ignoringExpectedNullFields() + .comparingOnlyFields("member", "product", "image", "rating", "content", "reBuy") + .isEqualTo( + new Review(member, product, image.getOriginalFilename(), 4.5, "review", true) + ); + } + + private MockMultipartFile 리뷰_페이크_사진_요청() { + return new MockMultipartFile("image", "image.jpg", "image/jpeg", new byte[]{1, 2, 3}); + } + + private List 태그_추가_요청() { + final Tag testTag1 = tagRepository.save(new Tag("testTag1")); + final Tag testTag2 = tagRepository.save(new Tag("testTag2")); + + return List.of(testTag1, testTag2); + } + + private Product 상품_추가_요청() { + return productRepository.save(new Product("testName", 1000L, "test.png", "test", null)); + } + + private Member 멤버_추가_요청() { + return memberRepository.save( + new Member("test", "image.png", 27, Gender.FEMALE, "01036551086")); + } +} diff --git a/backend/src/test/java/com/funeat/review/application/TestImageUploader.java b/backend/src/test/java/com/funeat/review/application/TestImageUploader.java new file mode 100644 index 00000000..49ca3ea6 --- /dev/null +++ b/backend/src/test/java/com/funeat/review/application/TestImageUploader.java @@ -0,0 +1,41 @@ +package com.funeat.review.application; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +@Profile("test") +public class TestImageUploader implements ImageService { + + @Override + public void upload(final MultipartFile image) { + // 실제로 IO 작업을 수행하는 대신, 임시 디렉토리로 복사하도록 수정 + try { + final String temporaryPath = String.valueOf(System.currentTimeMillis()); + final Path tempDirectoryPath = Files.createTempDirectory(temporaryPath); + final Path filePath = tempDirectoryPath.resolve(image.getOriginalFilename()); + Files.copy(image.getInputStream(), filePath); + + deleteDirectory(tempDirectoryPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void deleteDirectory(Path directory) throws IOException { + // 디렉토리 내부 파일 및 디렉토리 삭제 + try (Stream pathStream = Files.walk(directory)) { + pathStream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Files.deleteIfExists(directory); + } +} diff --git a/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java b/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java new file mode 100644 index 00000000..43bcbc5b --- /dev/null +++ b/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java @@ -0,0 +1,44 @@ +package com.funeat.tag.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.tag.domain.Tag; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class TagRepositoryTest { + + @Autowired + TagRepository tagRepository; + + @Test + void 여러_태그_아이디로_태그들을_조회_할_수_있다() { + // given + var tags = 태그_추가_요청(); + var tagIds = tags.stream() + .map(Tag::getId) + .collect(Collectors.toList()); + + // then + List result = tagRepository.findTagsByIdIn(tagIds); + + // when + assertThat(result).usingRecursiveComparison() + .isEqualTo(tags); + } + + private List 태그_추가_요청() { + final Tag testTag1 = tagRepository.save(new Tag("testTag1")); + final Tag testTag2 = tagRepository.save(new Tag("testTag2")); + + return List.of(testTag1, testTag2); + } +} diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties index d79c430a..a0bd609c 100644 --- a/backend/src/test/resources/application.properties +++ b/backend/src/test/resources/application.properties @@ -1,3 +1,4 @@ +spring.profiles.active=test spring.datasource.url=jdbc:h2:mem:test;MODE=MySQL spring.datasource.username=sa spring.jpa.hibernate.ddl-auto=create From a98008490487315154e10a21ec92974406041204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Mon, 17 Jul 2023 17:36:31 +0900 Subject: [PATCH 010/185] =?UTF-8?q?[FE]=20feat:=20RankingProductItem,=20Ra?= =?UTF-8?q?nkingProductList=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20(#44)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Ranking Product Item 구현 * chore: Ranking Products mock json 작성 * chore: gitkeep 삭제 * feat: Ranking Product List 구현 * feat: ranking type 구현 * feat: span -> 디자인 시스템 Text로 변경 * feat: 디자인 시스템에 맞게 text align 추가 * chore: 파일 이름 대문자 -> 소문자로 변경 * chore: 사용하지 않는 args 삭제 * refactor: list를 상위 컴포넌트로 빼고 div로 수정 * fix: 오류 사항 수정 --- .../.storybook/{preview.ts => preview.tsx} | 11 ++++- .../RankingProductItem.stories.tsx | 21 +++++++++ .../RankingProductItem/RankingProductItem.tsx | 44 +++++++++++++++++++ .../RankingProductList.stories.tsx | 13 ++++++ .../RankingProductList/RankingProductList.tsx | 16 +++++++ frontend/src/constants/.gitkeep | 0 frontend/src/mocks/data/rankingProducts.json | 20 +++++++++ frontend/src/types/.gitkeep | 0 frontend/src/types/Ranking.ts | 6 +++ frontend/src/types/index.ts | 3 ++ frontend/src/types/ranking.ts | 6 +++ frontend/tsconfig.json | 1 + 12 files changed, 140 insertions(+), 1 deletion(-) rename frontend/.storybook/{preview.ts => preview.tsx} (65%) create mode 100644 frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx create mode 100644 frontend/src/components/RankingProductItem/RankingProductItem.tsx create mode 100644 frontend/src/components/RankingProductList/RankingProductList.stories.tsx create mode 100644 frontend/src/components/RankingProductList/RankingProductList.tsx delete mode 100644 frontend/src/constants/.gitkeep create mode 100644 frontend/src/mocks/data/rankingProducts.json delete mode 100644 frontend/src/types/.gitkeep create mode 100644 frontend/src/types/Ranking.ts create mode 100644 frontend/src/types/index.ts create mode 100644 frontend/src/types/ranking.ts diff --git a/frontend/.storybook/preview.ts b/frontend/.storybook/preview.tsx similarity index 65% rename from frontend/.storybook/preview.ts rename to frontend/.storybook/preview.tsx index 02c7fda4..67c7b38b 100644 --- a/frontend/.storybook/preview.ts +++ b/frontend/.storybook/preview.tsx @@ -1,8 +1,17 @@ +import React from 'react'; +import { FunEatProvider } from '@fun-eat/design-system'; import type { Preview } from '@storybook/react'; import { mswDecorator } from 'msw-storybook-addon'; import { handlers } from '../src/mocks/handlers'; -export const decorators = [mswDecorator]; +export const decorators = [ + (Story) => ( + + + + ), + mswDecorator, +]; const preview: Preview = { parameters: { diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx b/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx new file mode 100644 index 00000000..c3dda407 --- /dev/null +++ b/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx @@ -0,0 +1,21 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import RankingProductItem from './RankingProductItem'; + +const meta: Meta = { + title: 'RankingProductItem', + component: RankingProductItem, + args: { + rankingProduct: { + id: 3, + rank: 1, + image: 'https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg', + name: '소금빵', + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.tsx b/frontend/src/components/RankingProductItem/RankingProductItem.tsx new file mode 100644 index 00000000..3ec57539 --- /dev/null +++ b/frontend/src/components/RankingProductItem/RankingProductItem.tsx @@ -0,0 +1,44 @@ +import { Text } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import type { RankingProduct } from '@types'; + +interface RankingProductItemProps { + rankingProduct: RankingProduct; +} + +const RankingProductItem = ({ rankingProduct }: RankingProductItemProps) => { + const { rank, image, name } = rankingProduct; + + return ( + + + {rank} + + + + {name} + + + ); +}; + +export default RankingProductItem; + +const RankingProductContainer = styled.div` + display: flex; + align-items: center; + width: 300px; + height: 50px; + margin-bottom: 15px; + padding-left: 15px; + gap: 15px; + border-radius: ${({ theme }) => theme.borderRadius.xs}; + background: ${({ theme }) => theme.colors.gray1}; +`; + +const RankingProductImage = styled.img` + width: 45px; + height: 45px; + border-radius: 50%; +`; diff --git a/frontend/src/components/RankingProductList/RankingProductList.stories.tsx b/frontend/src/components/RankingProductList/RankingProductList.stories.tsx new file mode 100644 index 00000000..40fe0cff --- /dev/null +++ b/frontend/src/components/RankingProductList/RankingProductList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import RankingProductList from './RankingProductList'; + +const meta: Meta = { + title: 'RankingProductList', + component: RankingProductList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/RankingProductList/RankingProductList.tsx b/frontend/src/components/RankingProductList/RankingProductList.tsx new file mode 100644 index 00000000..7c7ee716 --- /dev/null +++ b/frontend/src/components/RankingProductList/RankingProductList.tsx @@ -0,0 +1,16 @@ +import RankingProductItem from '@components/RankingProductItem/RankingProductItem'; +import rankingProducts from '@mocks/data/rankingProducts.json'; + +const RankingProductList = () => { + return ( +
    + {rankingProducts.map((rankingProduct) => ( +
  • + +
  • + ))} +
+ ); +}; + +export default RankingProductList; diff --git a/frontend/src/constants/.gitkeep b/frontend/src/constants/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/mocks/data/rankingProducts.json b/frontend/src/mocks/data/rankingProducts.json new file mode 100644 index 00000000..68bfbbf1 --- /dev/null +++ b/frontend/src/mocks/data/rankingProducts.json @@ -0,0 +1,20 @@ +[ + { + "id": 3, + "rank": 1, + "name": "구운감자슬림명란마요", + "image": "https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg" + }, + { + "id": 2, + "rank": 2, + "name": "삼립)보름달피치문", + "image": "https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg" + }, + { + "id": 4, + "rank": 3, + "name": "하얀짜파게티큰사발", + "image": "https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg" + } +] diff --git a/frontend/src/types/.gitkeep b/frontend/src/types/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/types/Ranking.ts b/frontend/src/types/Ranking.ts new file mode 100644 index 00000000..954d341f --- /dev/null +++ b/frontend/src/types/Ranking.ts @@ -0,0 +1,6 @@ +export interface RankingProduct { + id: number; + rank: number; + name: string; + image: string; +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts new file mode 100644 index 00000000..1855b45f --- /dev/null +++ b/frontend/src/types/index.ts @@ -0,0 +1,3 @@ +export type BBBB = ''; + +export * from './ranking'; diff --git a/frontend/src/types/ranking.ts b/frontend/src/types/ranking.ts new file mode 100644 index 00000000..954d341f --- /dev/null +++ b/frontend/src/types/ranking.ts @@ -0,0 +1,6 @@ +export interface RankingProduct { + id: number; + rank: number; + name: string; + image: string; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 5ad1ad5f..262cb08d 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -11,6 +11,7 @@ "skipLibCheck": true, "jsx": "react-jsx", "outDir": "./dist", + "typeRoots": ["node_modules/@types", "src/types"], "baseUrl": ".", "paths": { "@*": ["src/*"] From a63bcf6820ea48465f14896e7ade53684108082e Mon Sep 17 00:00:00 2001 From: Dabeen Jeong Date: Mon, 17 Jul 2023 19:01:54 +0900 Subject: [PATCH 011/185] =?UTF-8?q?[BE]=20feat:=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EC=83=81=ED=92=88=EC=9D=98=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=20(#40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 좋아요 기준 리뷰 목록 조회 인수 테스트 작성 * feat: response 관련 dto 생성 * feat: 리뷰 목록 조회 컨트롤러 추가 * feat: 리뷰와 관련된 엔티티 생성자 및 getter 추가 * feat: 리뷰에 좋아요 개수 필드 추가 * feat: 정렬 조건에 따라 리뷰 목록을 정렬할 수 있는 기능 추가 * refactor: 사용하지 않는 멤버 변수 삭제, 람다 표현식으로 변환 * refactor: dto 필드에 final 키워드 추가 * refactor: RequestParam의 sort가 아닌 Pageable의 sort를 사용하도록 수정 * refactor: Service에서 Response를 반환하도록 변경 * fix: conflict 해결 * refactor: api 필드명 변경으로 인한 코드 수정 * refactor: null 값이 들어오면 빈 리스트를 반환하는 로직 삭제 전부 기본값이 new ArrayList();로 변경됐거나, 변경될 예정으로 인해 삭제 * refactor: 사용하지 않는 import문 삭제 * refactor: 테스트 변수 클래스 이름을 var로 통일 --- .../java/com/funeat/member/domain/Member.java | 36 ++++++ .../domain/favorite/ReviewFavorite.java | 25 ++++ .../review/application/ReviewService.java | 25 +++- .../java/com/funeat/review/domain/Review.java | 5 +- .../com/funeat/review/domain/ReviewTag.java | 13 ++ .../review/persistence/ReviewRepository.java | 3 - .../review/presentation/ReviewController.java | 17 +++ .../presentation/dto/SortingReviewDto.java | 117 +++++++++++++++++ .../dto/SortingReviewsPageDto.java | 63 +++++++++ .../dto/SortingReviewsResponse.java | 28 ++++ .../acceptance/common/AcceptanceTest.java | 16 +++ .../review/ReviewAcceptanceTest.java | 122 ++++++++++++++---- .../review/application/ReviewServiceTest.java | 66 +++++++++- 13 files changed, 498 insertions(+), 38 deletions(-) create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsPageDto.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsResponse.java diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java index 05f9b5bd..1636bbc4 100644 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -58,4 +58,40 @@ public Member(final String nickName, final String profileImage, final Integer ag public Long getId() { return id; } + + public String getNickname() { + return nickname; + } + + public String getProfileImage() { + return profileImage; + } + + public Integer getAge() { + return age; + } + + public Gender getGender() { + return gender; + } + + public String getPhoneNumber() { + return phoneNumber; + } + + public List getReviewFavorites() { + return reviewFavorites; + } + + public List getRecipeFavorites() { + return recipeFavorites; + } + + public List getProductBookmarks() { + return productBookmarks; + } + + public List getRecipeBookmarks() { + return recipeBookmarks; + } } diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java index afa23cf3..d1013195 100644 --- a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java +++ b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java @@ -25,4 +25,29 @@ public class ReviewFavorite { private Review review; private Boolean checked; + + protected ReviewFavorite() { + } + + public ReviewFavorite(final Member member, final Review review, final Boolean checked) { + this.member = member; + this.review = review; + this.checked = checked; + } + + public Long getId() { + return id; + } + + public Member getMember() { + return member; + } + + public Review getReview() { + return review; + } + + public Boolean getChecked() { + return checked; + } } diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java index ee1833d1..96b5b663 100644 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -9,14 +9,20 @@ import com.funeat.review.persistence.ReviewRepository; import com.funeat.review.persistence.ReviewTagRepository; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.SortingReviewDto; +import com.funeat.review.presentation.dto.SortingReviewsPageDto; +import com.funeat.review.presentation.dto.SortingReviewsResponse; import com.funeat.tag.domain.Tag; import com.funeat.tag.persistence.TagRepository; -import java.util.List; -import java.util.stream.Collectors; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.util.List; +import java.util.stream.Collectors; + @Service @Transactional(readOnly = true) public class ReviewService { @@ -59,4 +65,19 @@ public void create(final Long productId, final MultipartFile image, final Review imageService.upload(image); reviewTagRepository.saveAll(reviewTags); } + + public SortingReviewsResponse sortingReviews(final Long productId, + final Pageable pageable) { + final Product product = productRepository.findById(productId) + .orElseThrow(IllegalArgumentException::new); + + final Page reviewPage = reviewRepository.findReviewsByProduct(pageable, product); + + final SortingReviewsPageDto pageDto = SortingReviewsPageDto.toDto(reviewPage); + final List reviewDtos = reviewPage.stream() + .map(SortingReviewDto::toDto) + .collect(Collectors.toList()); + + return SortingReviewsResponse.toResponse(pageDto, reviewDtos); + } } diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java index 40b997ad..64d9c309 100644 --- a/backend/src/main/java/com/funeat/review/domain/Review.java +++ b/backend/src/main/java/com/funeat/review/domain/Review.java @@ -3,8 +3,7 @@ import com.funeat.member.domain.Member; import com.funeat.member.domain.favorite.ReviewFavorite; import com.funeat.product.domain.Product; -import java.util.ArrayList; -import java.util.List; + import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -12,6 +11,8 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; +import java.util.ArrayList; +import java.util.List; @Entity public class Review { diff --git a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java index 09f0b203..ee4afd02 100644 --- a/backend/src/main/java/com/funeat/review/domain/ReviewTag.java +++ b/backend/src/main/java/com/funeat/review/domain/ReviewTag.java @@ -1,6 +1,7 @@ package com.funeat.review.domain; import com.funeat.tag.domain.Tag; + import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; @@ -37,4 +38,16 @@ public static ReviewTag createReviewTag(final Review review, final Tag tag) { review.getReviewTags().add(reviewTag); return reviewTag; } + + public Long getId() { + return id; + } + + public Review getReview() { + return review; + } + + public Tag getTag() { + return tag; + } } diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java index 3af73239..89c1ef36 100644 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java @@ -2,7 +2,6 @@ import com.funeat.product.domain.Product; import com.funeat.review.domain.Review; -import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -10,6 +9,4 @@ public interface ReviewRepository extends JpaRepository { Page findReviewsByProduct(final Pageable pageable, final Product product); - - List findReviewsByProductOrderByFavoriteCountDesc(final Pageable pageable, final Product product); } diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index 0b0e3a47..c31c32a7 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -2,7 +2,9 @@ import com.funeat.review.application.ReviewService; import com.funeat.review.presentation.dto.ReviewCreateRequest; + import java.net.URI; + import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; @@ -11,6 +13,13 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import com.funeat.review.presentation.dto.SortingReviewsResponse; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.web.bind.annotation.GetMapping; + +import java.util.List; + @RestController public class ReviewController { @@ -28,4 +37,12 @@ public ResponseEntity writeReview(@PathVariable Long productId, @RequestPa return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); } + + @GetMapping(value = "/api/products/{productId}/reviews") + public ResponseEntity getSortingReviews(@PathVariable Long productId, + @PageableDefault Pageable pageable) { + final SortingReviewsResponse response = reviewService.sortingReviews(productId, pageable); + + return ResponseEntity.ok(response); + } } diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java new file mode 100644 index 00000000..1d825ad2 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java @@ -0,0 +1,117 @@ +package com.funeat.review.presentation.dto; + +import com.funeat.member.domain.favorite.ReviewFavorite; +import com.funeat.review.domain.Review; +import com.funeat.review.domain.ReviewTag; +import com.funeat.tag.domain.Tag; + +import java.util.List; +import java.util.stream.Collectors; + +public class SortingReviewDto { + + private final Long id; + private final String userName; + private final String profileImage; + private final String image; + private final Double rating; + private final List tags; + private final String content; + private final boolean rebuy; + private final Long favoriteCount; + private final boolean favorite; + + public SortingReviewDto(final Long id, + final String userName, + final String profileImage, + final String image, + final Double rating, + final List tags, + final String content, + final boolean rebuy, + final Long favoriteCount, + final boolean favorite) { + this.id = id; + this.userName = userName; + this.profileImage = profileImage; + this.image = image; + this.rating = rating; + this.tags = tags; + this.content = content; + this.rebuy = rebuy; + this.favoriteCount = favoriteCount; + this.favorite = favorite; + } + + public static SortingReviewDto toDto(final Review review) { + return new SortingReviewDto( + review.getId(), + review.getMember().getNickname(), + review.getMember().getProfileImage(), + review.getImage(), + review.getRating(), + findTags(review), + review.getContent(), + review.getReBuy(), + review.getFavoriteCount(), + findReviewFavoriteChecked(review) + ); + } + + private static List findTags(final Review review) { + return review.getReviewTags() + .stream() + .map(ReviewTag::getTag) + .collect(Collectors.toList()); + } + + private static boolean findReviewFavoriteChecked(final Review review) { + return review.getReviewFavorites() + .stream() + .filter(reviewFavorite -> reviewFavorite.getReview().equals(review)) + .filter(reviewFavorite -> reviewFavorite.getMember().equals(review.getMember())) + .findFirst() + .map(ReviewFavorite::getChecked) + .orElse(false); + } + + public Long getId() { + return id; + } + + public String getUserName() { + return userName; + } + + public String getProfileImage() { + return profileImage; + } + + public String getImage() { + return image; + } + + public Double getRating() { + return rating; + } + + public List getTags() { + return tags; + } + + public String getContent() { + return content; + } + + public boolean isRebuy() { + return rebuy; + } + + public Long getFavoriteCount() { + return favoriteCount; + } + + public boolean isFavorite() { + return favorite; + } +} diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsPageDto.java b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsPageDto.java new file mode 100644 index 00000000..37bf3bfd --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsPageDto.java @@ -0,0 +1,63 @@ +package com.funeat.review.presentation.dto; + +import com.funeat.review.domain.Review; +import org.springframework.data.domain.Page; + +public class SortingReviewsPageDto { + + private final Long totalDataCount; + private final Long totalPages; + private final boolean firstPage; + private final boolean lastPage; + private final Long requestPage; + private final Long requestSize; + + public SortingReviewsPageDto(final Long totalDataCount, + final Long totalPages, + final boolean firstPage, + final boolean lastPage, + final Long requestPage, + final Long requestSize) { + this.totalDataCount = totalDataCount; + this.totalPages = totalPages; + this.firstPage = firstPage; + this.lastPage = lastPage; + this.requestPage = requestPage; + this.requestSize = requestSize; + } + + public static SortingReviewsPageDto toDto(final Page page) { + return new SortingReviewsPageDto( + page.getTotalElements(), + Long.valueOf(page.getTotalPages()), + page.isFirst(), + page.isLast(), + Long.valueOf(page.getNumber()), + Long.valueOf(page.getSize()) + ); + } + + public Long getTotalDataCount() { + return totalDataCount; + } + + public Long getTotalPages() { + return totalPages; + } + + public boolean isFirstPage() { + return firstPage; + } + + public boolean isLastPage() { + return lastPage; + } + + public Long getRequestPage() { + return requestPage; + } + + public Long getRequestSize() { + return requestSize; + } +} diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsResponse.java b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsResponse.java new file mode 100644 index 00000000..9a9178a9 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewsResponse.java @@ -0,0 +1,28 @@ +package com.funeat.review.presentation.dto; + +import java.util.List; + +public class SortingReviewsResponse { + + private final SortingReviewsPageDto page; + private final List reviews; + + public SortingReviewsResponse(final SortingReviewsPageDto page, + final List reviews) { + this.page = page; + this.reviews = reviews; + } + + public static SortingReviewsResponse toResponse(final SortingReviewsPageDto page, + final List reviews) { + return new SortingReviewsResponse(page, reviews); + } + + public SortingReviewsPageDto getPage() { + return page; + } + + public List getReviews() { + return reviews; + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java index cd7f583b..4fb920a0 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java @@ -1,7 +1,11 @@ package com.funeat.acceptance.common; +import com.funeat.member.persistence.MemberRepository; import com.funeat.product.persistence.CategoryRepository; import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.persistence.ReviewRepository; +import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.tag.persistence.TagRepository; import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; @@ -25,6 +29,18 @@ public abstract class AcceptanceTest { @Autowired public CategoryRepository categoryRepository; + @Autowired + public MemberRepository memberRepository; + + @Autowired + public ReviewRepository reviewRepository; + + @Autowired + public TagRepository tagRepository; + + @Autowired + public ReviewTagRepository reviewTagRepository; + @BeforeEach void setUp() { RestAssured.port = port; diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index f3dcfec5..58cfcf08 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -1,46 +1,34 @@ package com.funeat.acceptance.review; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.정상_생성; -import static io.restassured.RestAssured.given; - import com.funeat.acceptance.common.AcceptanceTest; import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; -import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; import com.funeat.product.domain.Product; -import com.funeat.product.persistence.ProductRepository; -import com.funeat.review.persistence.ReviewRepository; -import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.review.domain.Review; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.SortingReviewDto; +import com.funeat.review.presentation.dto.SortingReviewsPageDto; import com.funeat.tag.domain.Tag; -import com.funeat.tag.persistence.TagRepository; import io.restassured.builder.MultiPartSpecBuilder; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import io.restassured.specification.MultiPartSpecification; -import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -@SuppressWarnings("NonAsciiCharacters") -class ReviewAcceptanceTest extends AcceptanceTest { - - @Autowired - private MemberRepository memberRepository; - @Autowired - private ProductRepository productRepository; - - @Autowired - private TagRepository tagRepository; +import java.util.List; +import java.util.stream.Collectors; - @Autowired - private ReviewRepository reviewRepository; +import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; +import static com.funeat.acceptance.common.CommonSteps.정상_생성; +import static com.funeat.acceptance.common.CommonSteps.정상_처리; +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; - @Autowired - private ReviewTagRepository reviewTagRepository; +@SuppressWarnings("NonAsciiCharacters") +class ReviewAcceptanceTest extends AcceptanceTest { @BeforeEach void init() { @@ -106,4 +94,86 @@ void init() { return testMember.getId(); } + + @Test + void 좋아요_기준_내림차순된_리뷰_목록을_조회한다() { + // given + final var category = new Category("간편식사", CategoryType.FOOD); + 카테고리_추가_요청(category); + + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가_요청(members); + + final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); + final var productId = 상품_추가_요청(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var sortingReviews = List.of(review2, review3, review1); + + // when + final var response = 좋아요_기준_리뷰_목록_조회_요청(productId, "favoriteCount,desc", 0); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 좋아요_기준_리뷰_목록_조회_결과를_검증한다(response, sortingReviews); + } + + private void 카테고리_추가_요청(final Category category) { + categoryRepository.save(category); + } + + private void 복수_유저_추가_요청(final List members) { + memberRepository.saveAll(members); + } + + private Long 상품_추가_요청(final Product product) { + return productRepository.save(product).getId(); + } + + private void 복수_리뷰_추가(final List reviews) { + reviewRepository.saveAll(reviews); + } + + private ExtractableResponse 좋아요_기준_리뷰_목록_조회_요청(final Long productId, + final String sort, + final Integer page) { + return given() + .queryParam("sort", sort) + .queryParam("page", page) + .when() + .get("/api/products/{product_id}/reviews", productId) + .then() + .extract(); + } + + private void 좋아요_기준_리뷰_목록_조회_결과를_검증한다(final ExtractableResponse response, + final List reviews) { + 페이지를_검증한다(response); + 리뷰_목록을_검증한다(response, reviews); + } + + private void 페이지를_검증한다(final ExtractableResponse response) { + final var expected = new SortingReviewsPageDto(3L, 1L, true, true, 0L, 10L); + final var actual = response.jsonPath().getObject("page", SortingReviewsPageDto.class); + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + private void 리뷰_목록을_검증한다(final ExtractableResponse response, + final List reviews) { + final List expected = reviews.stream() + .map(SortingReviewDto::toDto) + .collect(Collectors.toList()); + final List actual = response.jsonPath().getList("reviews", SortingReviewDto.class); + assertThat(actual).usingRecursiveComparison() + .ignoringFields("id") + .isEqualTo(expected); + } } diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java index c3578885..ac94a1d7 100644 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -1,7 +1,5 @@ package com.funeat.review.application; -import static org.assertj.core.api.Assertions.assertThat; - import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; import com.funeat.member.persistence.MemberRepository; @@ -11,18 +9,29 @@ import com.funeat.review.persistence.ReviewRepository; import com.funeat.review.persistence.ReviewTagRepository; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.tag.domain.Tag; import com.funeat.tag.persistence.TagRepository; -import java.util.List; -import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; -import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.mock.web.MockMultipartFile; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +@Transactional @SpringBootTest @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) @@ -99,4 +108,51 @@ void init() { return memberRepository.save( new Member("test", "image.png", 27, Gender.FEMALE, "01036551086")); } + + @Nested + class sortingReviews_페이징_테스트 { + + @Test + void 좋아요_기준으로_내림차순_정렬이_되는지_확인한다() { + // given + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가(members); + + final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); + 상품_추가(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var pageable = PageRequest.of(0, 2, Sort.by("favoriteCount").descending()); + final var expected = Stream.of(review1, review3) + .map(SortingReviewDto::toDto) + .collect(Collectors.toList()); + + // when + final var actual = reviewService.sortingReviews(product.getId(), pageable) + .getReviews(); + + // then + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + } + + private void 복수_유저_추가(final List members) { + memberRepository.saveAll(members); + } + + private void 상품_추가(final Product product) { + productRepository.save(product); + } + + private void 복수_리뷰_추가(final List reviews) { + reviewRepository.saveAll(reviews); + } } From 1da49d908d252900cd583644c1663ac7048a1916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Mon, 17 Jul 2023 19:24:34 +0900 Subject: [PATCH 012/185] =?UTF-8?q?[BE]=20feat:=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EC=83=81=ED=92=88=EC=9D=98=20=EB=A6=AC=EB=B7=B0=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94,=20=EC=A2=8B=EC=95=84=EC=9A=94=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 리뷰 좋아요 인수테스트 구현 * feat: 리뷰 좋아요 서비스 구현 * feat: ReviewFavoriteRepository 테스트 추가 * feat: 리뷰 좋아요 count 기능 및 테스트 추가 * fix: test 동작 수정 * refactor: 코드 컨벤션 적용 --- .../java/com/funeat/member/domain/Member.java | 3 +- .../domain/favorite/ReviewFavorite.java | 23 ++++- .../persistence/ReviewFavoriteRepository.java | 5 + .../review/application/ReviewService.java | 27 +++++- .../java/com/funeat/review/domain/Review.java | 18 +++- .../review/presentation/ReviewController.java | 11 ++- .../dto/ReviewFavoriteRequest.java | 20 ++++ .../funeat/acceptance/common/CommonSteps.java | 1 + .../review/ReviewAcceptanceTest.java | 81 +++++++++++++++- .../ReviewFavoriteRepositoryTest.java | 79 ++++++++++++++++ .../review/application/ReviewServiceTest.java | 92 ++++++++++++++++--- 11 files changed, 338 insertions(+), 22 deletions(-) create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/ReviewFavoriteRequest.java create mode 100644 backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java index 1636bbc4..53398c2a 100644 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -4,6 +4,7 @@ import com.funeat.member.domain.bookmark.RecipeBookmark; import com.funeat.member.domain.favorite.RecipeFavorite; import com.funeat.member.domain.favorite.ReviewFavorite; +import java.util.ArrayList; import java.util.List; import javax.persistence.Entity; import javax.persistence.EnumType; @@ -32,7 +33,7 @@ public class Member { private String phoneNumber; @OneToMany(mappedBy = "member") - private List reviewFavorites; + private List reviewFavorites = new ArrayList<>(); @OneToMany(mappedBy = "member") private List recipeFavorites; diff --git a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java index d1013195..5bae5ddc 100644 --- a/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java +++ b/backend/src/main/java/com/funeat/member/domain/favorite/ReviewFavorite.java @@ -29,10 +29,31 @@ public class ReviewFavorite { protected ReviewFavorite() { } - public ReviewFavorite(final Member member, final Review review, final Boolean checked) { + public ReviewFavorite(final Boolean favorite) { + this.checked = favorite; + } + + public ReviewFavorite(final Member member, final Review review) { this.member = member; this.review = review; + } + + public static ReviewFavorite createReviewFavoriteByMemberAndReview(final Member member, final Review review, + final Boolean favorite) { + final ReviewFavorite reviewFavorite = new ReviewFavorite(member, review); + reviewFavorite.review.getReviewFavorites().add(reviewFavorite); + reviewFavorite.member.getReviewFavorites().add(reviewFavorite); + return reviewFavorite; + } + + public void updateChecked(final Boolean checked) { this.checked = checked; + if (checked) { + this.review.addFavoriteCount(); + return; + } + this.review.minusFavoriteCount(); + } public Long getId() { diff --git a/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java b/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java index 970b56b1..2e96e623 100644 --- a/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java +++ b/backend/src/main/java/com/funeat/member/persistence/ReviewFavoriteRepository.java @@ -1,7 +1,12 @@ package com.funeat.member.persistence; +import com.funeat.member.domain.Member; import com.funeat.member.domain.favorite.ReviewFavorite; +import com.funeat.review.domain.Review; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface ReviewFavoriteRepository extends JpaRepository { + + Optional findByMemberAndReview(final Member member, final Review review); } diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java index 96b5b663..e7c952ca 100644 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -1,7 +1,9 @@ package com.funeat.review.application; import com.funeat.member.domain.Member; +import com.funeat.member.domain.favorite.ReviewFavorite; import com.funeat.member.persistence.MemberRepository; +import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.domain.Product; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.domain.Review; @@ -33,16 +35,19 @@ public class ReviewService { private final MemberRepository memberRepository; private final ProductRepository productRepository; private final ImageService imageService; + private final ReviewFavoriteRepository reviewFavoriteRepository; public ReviewService(final ReviewRepository reviewRepository, final TagRepository tagRepository, final ReviewTagRepository reviewTagRepository, final MemberRepository memberRepository, - final ProductRepository productRepository, final ImageService imageService) { + final ProductRepository productRepository, final ImageService imageService, + final ReviewFavoriteRepository reviewFavoriteRepository) { this.reviewRepository = reviewRepository; this.tagRepository = tagRepository; this.reviewTagRepository = reviewTagRepository; this.memberRepository = memberRepository; this.productRepository = productRepository; this.imageService = imageService; + this.reviewFavoriteRepository = reviewFavoriteRepository; } @Transactional @@ -62,8 +67,26 @@ public void create(final Long productId, final MultipartFile image, final Review .map(findTag -> ReviewTag.createReviewTag(savedReview, findTag)) .collect(Collectors.toList()); - imageService.upload(image); reviewTagRepository.saveAll(reviewTags); + imageService.upload(image); + } + + @Transactional + public void likeReview(final Long productId, final Long reviewId, final ReviewFavoriteRequest request) { + final Member findMember = memberRepository.findById(request.getMemberId()) + .orElseThrow(IllegalArgumentException::new); + final Product findProduct = productRepository.findById(productId) + .orElseThrow(IllegalArgumentException::new); + final Review findReview = reviewRepository.findById(reviewId) + .orElseThrow(IllegalArgumentException::new); + + final ReviewFavorite reviewFavorite = ReviewFavorite.createReviewFavoriteByMemberAndReview(findMember, + findReview, request.getFavorite()); + + final ReviewFavorite findReviewFavorite = reviewFavoriteRepository.findByMemberAndReview(findMember, + findReview).orElse(reviewFavoriteRepository.save(reviewFavorite)); + + findReviewFavorite.updateChecked(request.getFavorite()); } public SortingReviewsResponse sortingReviews(final Long productId, diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java index 64d9c309..a3b5ecc0 100644 --- a/backend/src/main/java/com/funeat/review/domain/Review.java +++ b/backend/src/main/java/com/funeat/review/domain/Review.java @@ -41,7 +41,7 @@ public class Review { private List reviewTags = new ArrayList<>(); @OneToMany(mappedBy = "review") - private List reviewFavorites; + private List reviewFavorites = new ArrayList<>(); private Long favoriteCount = 0L; @@ -69,6 +69,14 @@ public Review(final Member member, final Product findProduct, final String image this.favoriteCount = favoriteCount; } + public void addFavoriteCount() { + this.favoriteCount++; + } + + public void minusFavoriteCount() { + this.favoriteCount--; + } + public Long getId() { return id; } @@ -97,10 +105,6 @@ public Member getMember() { return member; } - public List getReviewTags() { - return reviewTags; - } - public List getReviewFavorites() { return reviewFavorites; } @@ -108,4 +112,8 @@ public List getReviewFavorites() { public Long getFavoriteCount() { return favoriteCount; } + + public List getReviewTags() { + return reviewTags; + } } diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index c31c32a7..a9171d41 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -2,13 +2,15 @@ import com.funeat.review.application.ReviewService; import com.funeat.review.presentation.dto.ReviewCreateRequest; - +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import java.net.URI; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -38,6 +40,13 @@ public ResponseEntity writeReview(@PathVariable Long productId, @RequestPa return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); } + @PatchMapping("/api/products/{productId}/reviews/{reviewId}") + public ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, + @RequestBody ReviewFavoriteRequest request) { + reviewService.likeReview(productId, reviewId, request); + + return ResponseEntity.noContent().build(); + @GetMapping(value = "/api/products/{productId}/reviews") public ResponseEntity getSortingReviews(@PathVariable Long productId, @PageableDefault Pageable pageable) { diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/ReviewFavoriteRequest.java b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewFavoriteRequest.java new file mode 100644 index 00000000..6d1a974c --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewFavoriteRequest.java @@ -0,0 +1,20 @@ +package com.funeat.review.presentation.dto; + +public class ReviewFavoriteRequest { + + private final Boolean favorite; + private final Long memberId; + + public ReviewFavoriteRequest(final Boolean favorite, final Long memberId) { + this.favorite = favorite; + this.memberId = memberId; + } + + public Boolean getFavorite() { + return favorite; + } + + public Long getMemberId() { + return memberId; + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java index 428eb94f..a2f4081b 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java +++ b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java @@ -11,6 +11,7 @@ public class CommonSteps { public static final HttpStatus 정상_처리 = HttpStatus.OK; public static final HttpStatus 정상_생성 = HttpStatus.CREATED; + public static final HttpStatus 정상_처리_NO_CONTENT = HttpStatus.NO_CONTENT; public static Long LOCATION_헤더에서_ID_추출(final ExtractableResponse response) { return Long.parseLong(response.header("Location").split("/")[2]); diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index 58cfcf08..e2939fd5 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -1,13 +1,23 @@ package com.funeat.acceptance.review; +import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; +import static com.funeat.acceptance.common.CommonSteps.정상_생성; +import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + import com.funeat.acceptance.common.AcceptanceTest; import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; +import com.funeat.member.domain.favorite.ReviewFavorite; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.domain.Category; import com.funeat.product.domain.CategoryType; import com.funeat.product.domain.Product; import com.funeat.review.domain.Review; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.review.presentation.dto.SortingReviewsPageDto; import com.funeat.tag.domain.Tag; @@ -30,13 +40,17 @@ @SuppressWarnings("NonAsciiCharacters") class ReviewAcceptanceTest extends AcceptanceTest { + @Autowired + private ReviewFavoriteRepository reviewFavoriteRepository; + @BeforeEach void init() { reviewTagRepository.deleteAll(); + reviewFavoriteRepository.deleteAll(); + reviewRepository.deleteAll(); memberRepository.deleteAll(); productRepository.deleteAll(); tagRepository.deleteAll(); - reviewRepository.deleteAll(); } @Test @@ -56,6 +70,60 @@ void init() { STATUS_CODE를_검증한다(response, 정상_생성); } + @Test + void 리뷰에_좋아요를_할_수_있다() { + // given + final Long savedMemberId = 멤버_추가_요청(); + final Long savedProductId = 상품_추가_요청(); + final List savedTagIds = 태그_추가_요청(); + final MultiPartSpecification image = 리뷰_사진_명세_요청(); + final var reviewRequest = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + final var favoriteRequest = new ReviewFavoriteRequest(true, savedMemberId); + + 리뷰_추가_요청(savedProductId, image, reviewRequest); + final var savedReviewId = reviewRepository.findAll().get(0).getId(); + + // when + final var response = 리뷰_좋아요_요청(savedProductId, savedReviewId, favoriteRequest); + final var result = reviewFavoriteRepository.findAll().get(0); + + // then + STATUS_CODE를_검증한다(response, 정상_처리_NO_CONTENT); + 리뷰_좋아요_결과를_검증한다(result, savedMemberId, savedReviewId); + assertThat(result.getChecked()).isTrue(); + } + + @Test + void 리뷰에_좋아요를_취소할_수_있다() { + // given + final Long savedMemberId = 멤버_추가_요청(); + final Long savedProductId = 상품_추가_요청(); + final List savedTagIds = 태그_추가_요청(); + final MultiPartSpecification image = 리뷰_사진_명세_요청(); + final var reviewRequest = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + final var favoriteRequest = new ReviewFavoriteRequest(true, savedMemberId); + final var favoriteCancelRequest = new ReviewFavoriteRequest(false, savedMemberId); + + 리뷰_추가_요청(savedProductId, image, reviewRequest); + final var savedReview = reviewRepository.findAll().get(0); + 리뷰_좋아요_요청(savedProductId, savedReview.getId(), favoriteRequest); + + // when + final var response = 리뷰_좋아요_요청(savedProductId, savedReview.getId(), favoriteCancelRequest); + final var result = reviewFavoriteRepository.findAll().get(0); + + // then + STATUS_CODE를_검증한다(response, 정상_처리_NO_CONTENT); + 리뷰_좋아요_결과를_검증한다(result, savedMemberId, savedReview.getId()); + assertThat(result.getChecked()).isFalse(); + } + + private void 리뷰_좋아요_결과를_검증한다(final ReviewFavorite result, final Long memberId, final Long reviewId) { + assertThat(result.getId()).isNotNull(); + assertThat(result.getReview().getId()).isEqualTo(reviewId); + assertThat(result.getMember().getId()).isEqualTo(memberId); + } + private ExtractableResponse 리뷰_추가_요청(final Long productId, final MultiPartSpecification image, final ReviewCreateRequest request) { return given() @@ -67,6 +135,17 @@ void init() { .extract(); } + private ExtractableResponse 리뷰_좋아요_요청(final Long productId, final Long reviewId, + final ReviewFavoriteRequest request) { + return given() + .contentType("application/json") + .body(request) + .when() + .patch("/api/products/{productId}/reviews/{reviewId}", productId, reviewId) + .then() + .extract(); + } + private MultiPartSpecification 리뷰_사진_명세_요청() { return new MultiPartSpecBuilder("image".getBytes()) .fileName("testImage.png") diff --git a/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java b/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java new file mode 100644 index 00000000..bd048e43 --- /dev/null +++ b/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java @@ -0,0 +1,79 @@ +package com.funeat.member.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; +import com.funeat.member.domain.favorite.ReviewFavorite; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.domain.Review; +import com.funeat.review.persistence.ReviewRepository; +import io.restassured.builder.MultiPartSpecBuilder; +import io.restassured.specification.MultiPartSpecification; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; + +@DataJpaTest +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class ReviewFavoriteRepositoryTest { + + @Autowired + private ReviewFavoriteRepository reviewFavoriteRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private ProductRepository productRepository; + + @Test + void 멤버와_리뷰로_리뷰_좋아요를_조회할_수_있다() { + // given + final var member = 멤버_추가_요청(); + final var product = 상품_추가_요청(); + final var review = 리뷰_추가_요청(member, product); + final var reviewFavorite = ReviewFavorite.createReviewFavoriteByMemberAndReview(member, review, true); + reviewFavoriteRepository.save(reviewFavorite); + + // when + final var result = reviewFavoriteRepository.findByMemberAndReview(member, review).get(); + + // then + var expected = ReviewFavorite.createReviewFavoriteByMemberAndReview(member, review, true); + + assertThat(result).usingRecursiveComparison() + .ignoringExpectedNullFields() + .comparingOnlyFields("member", "review", "checked") + .isEqualTo(expected); + } + + private Member 멤버_추가_요청() { + return memberRepository.save( + new Member("test", "image.png", 27, Gender.FEMALE, "01036551086")); + } + + private Product 상품_추가_요청() { + return productRepository.save(new Product("testName", 1000L, "test.png", "test", null)); + } + + private Review 리뷰_추가_요청(final Member member, final Product product) { + final var image = 리뷰_사진_명세_요청(); + return reviewRepository.save(new Review(member, product, image.getFileName(), 4.5, "content", true)); + } + + private MultiPartSpecification 리뷰_사진_명세_요청() { + return new MultiPartSpecBuilder("image".getBytes()) + .fileName("testImage.png") + .controlName("image") + .mimeType("image/png") + .build(); + } +} diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java index ac94a1d7..ce596240 100644 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -1,14 +1,20 @@ package com.funeat.review.application; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; + import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; +import com.funeat.member.domain.favorite.ReviewFavorite; import com.funeat.member.persistence.MemberRepository; +import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.domain.Product; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.domain.Review; import com.funeat.review.persistence.ReviewRepository; import com.funeat.review.persistence.ReviewTagRepository; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.tag.domain.Tag; import com.funeat.tag.persistence.TagRepository; @@ -23,14 +29,10 @@ import org.springframework.data.domain.Sort; import org.springframework.mock.web.MockMultipartFile; import org.springframework.transaction.annotation.Transactional; - import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; - @Transactional @SpringBootTest @SuppressWarnings("NonAsciiCharacters") @@ -52,11 +54,15 @@ class ReviewServiceTest { @Autowired private ProductRepository productRepository; + @Autowired + private ReviewFavoriteRepository reviewFavoriteRepository; + @Autowired private ReviewTagRepository reviewTagRepository; @BeforeEach void init() { + reviewFavoriteRepository.deleteAll(); reviewTagRepository.deleteAll(); reviewRepository.deleteAll(); memberRepository.deleteAll(); @@ -67,18 +73,18 @@ void init() { @Test void 리뷰를_추가할_수_있다() { // given - var member = 멤버_추가_요청(); - var product = 상품_추가_요청(); - var tags = 태그_추가_요청(); - var tagIds = tags.stream() + final var member = 멤버_추가_요청(); + final var product = 상품_추가_요청(); + final var tags = 태그_추가_요청(); + final var tagIds = tags.stream() .map(Tag::getId) .collect(Collectors.toList()); - var image = 리뷰_페이크_사진_요청(); - var request = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + final var image = 리뷰_페이크_사진_요청(); + final var request = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); // when reviewService.create(product.getId(), image, request); - var result = reviewRepository.findAll(); + final var result = reviewRepository.findAll(); // then assertThat(result.get(0)).usingRecursiveComparison() @@ -89,6 +95,70 @@ void init() { ); } + @Test + void 리뷰에_좋아요를_할_수_있다() { + // given + final var member = 멤버_추가_요청(); + final var product = 상품_추가_요청(); + final var tags = 태그_추가_요청(); + final var tagIds = tags.stream() + .map(Tag::getId) + .collect(Collectors.toList()); + final var image = 리뷰_페이크_사진_요청(); + final var reviewCreaterequest = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + + reviewService.create(product.getId(), image, reviewCreaterequest); + final var savedReview = reviewRepository.findAll().get(0); + + // when + final var favoriteRequest = new ReviewFavoriteRequest(true, member.getId()); + reviewService.likeReview(product.getId(), savedReview.getId(), favoriteRequest); + final var reviewFavoriteResult = reviewFavoriteRepository.findAll().get(0); + final var reviewResult = reviewRepository.findAll().get(0); + + // then + final var expected = ReviewFavorite.createReviewFavoriteByMemberAndReview(member, savedReview, true); + assertThat(reviewResult.getFavoriteCount()).isEqualTo(1L); + assertThat(reviewFavoriteResult).usingRecursiveComparison() + .ignoringExpectedNullFields() + .comparingOnlyFields("member", "review", "checked") + .isEqualTo(expected); + } + + @Test + void 리뷰에_좋아요를_취소_할_수_있다() { + // given + final var member = 멤버_추가_요청(); + final var product = 상품_추가_요청(); + final var tags = 태그_추가_요청(); + final var tagIds = tags.stream() + .map(Tag::getId) + .collect(Collectors.toList()); + final var image = 리뷰_페이크_사진_요청(); + final var reviewCreaterequest = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + + reviewService.create(product.getId(), image, reviewCreaterequest); + final var savedReview = reviewRepository.findAll().get(0); + + final var favoriteRequest = new ReviewFavoriteRequest(true, member.getId()); + reviewService.likeReview(product.getId(), savedReview.getId(), favoriteRequest); + + // when + final var cancelFavoriteRequest = new ReviewFavoriteRequest(false, member.getId()); + reviewService.likeReview(product.getId(), savedReview.getId(), cancelFavoriteRequest); + + final var reviewFavoriteResult = reviewFavoriteRepository.findAll().get(0); + final var reviewResult = reviewRepository.findAll().get(0); + + // then + final var expected = ReviewFavorite.createReviewFavoriteByMemberAndReview(member, savedReview, false); + assertThat(reviewResult.getFavoriteCount()).isEqualTo(0L); + assertThat(reviewFavoriteResult).usingRecursiveComparison() + .ignoringExpectedNullFields() + .comparingOnlyFields("member", "review", "checked") + .isEqualTo(expected); + } + private MockMultipartFile 리뷰_페이크_사진_요청() { return new MockMultipartFile("image", "image.jpg", "image/jpeg", new byte[]{1, 2, 3}); } From 31646e48f3e086ff1077017f9263a983715ac554 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Mon, 17 Jul 2023 19:59:30 +0900 Subject: [PATCH 013/185] =?UTF-8?q?[FE]=20chore:=20=EC=A0=88=EB=8C=80?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=EB=A5=BC=20'@'=EC=97=90=EC=84=9C=20'@/'?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/index.tsx | 3 +-- frontend/tsconfig.json | 2 +- frontend/webpack.common.js | 14 ++------------ 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 16c5bd98..cfc6c28f 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -3,8 +3,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; - -import { SvgSprite } from '@components/Common'; +import { SvgSprite } from './components/Common'; const main = async () => { if (process.env.NODE_ENV === 'development') { diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 262cb08d..f8eb64f4 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -14,7 +14,7 @@ "typeRoots": ["node_modules/@types", "src/types"], "baseUrl": ".", "paths": { - "@*": ["src/*"] + "@/*": ["src/*"] } }, "include": ["src"], diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 6992b088..a259309a 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -9,19 +9,9 @@ module.exports = { publicPath: '/', }, resolve: { - extensions: ['.ts', '.tsx', '.js'], + extensions: ['.ts', '.tsx', '.js', 'json'], alias: { - '@': path.resolve(__dirname, './src'), - '@apis': path.resolve(__dirname, './src/apis'), - '@assets': path.resolve(__dirname, './src/assets'), - '@components': path.resolve(__dirname, './src/components'), - '@constants': path.resolve(__dirname, './src/constants'), - '@hooks': path.resolve(__dirname, './src/hooks'), - '@mocks': path.resolve(__dirname, './src/mocks'), - '@pages': path.resolve(__dirname, './src/pages'), - '@router': path.resolve(__dirname, './src/router'), - '@styles': path.resolve(__dirname, './src/styles'), - '@utils': path.resolve(__dirname, './src/utils'), + '@': path.resolve(__dirname, 'src'), }, }, module: { From d133b8d65287a39cb6d4beaa13f4ea8302012cc8 Mon Sep 17 00:00:00 2001 From: Dabeen Jeong Date: Tue, 18 Jul 2023 09:47:25 +0900 Subject: [PATCH 014/185] =?UTF-8?q?[BE]=20feat:=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=EC=88=98=EB=A5=BC=20=EA=B8=B0=EC=A4=80=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=EB=B7=B0=20=EB=9E=AD=ED=82=B9=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 리뷰 랭킹 조회 인수테스트 작성 * feat: response dto 생성 * feat: 리뷰 랭킹 조회 컨트롤러 구현 * feat: 좋아요를 기준으로 상위 3개 리뷰를 보여주는 기능 구현 * fix: 인수테스트 비교시 같은 종류의 객체를 반환하도록 수정 * refactor: 주석 삭제 * refactor: service에서 response를 반환하도록 수정 * refactor: final 키워드 추가 * refactor: 테스트 내부는 전부 var로 수정 * fix: conflict 해결 * test: Repository 테스트 추가 --- .../review/application/ReviewService.java | 13 +++ .../review/persistence/ReviewRepository.java | 4 + .../review/presentation/ReviewController.java | 25 +++-- .../presentation/dto/RankingReviewDto.java | 62 ++++++++++++ .../dto/RankingReviewsResponse.java | 20 ++++ .../acceptance/common/AcceptanceTest.java | 4 + .../review/ReviewAcceptanceTest.java | 71 +++++++++++--- .../persistence/ReviewRepositoryTest.java | 96 +++++++++++++++++++ 8 files changed, 276 insertions(+), 19 deletions(-) create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewsResponse.java create mode 100644 backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java index e7c952ca..ea77df22 100644 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -10,7 +10,10 @@ import com.funeat.review.domain.ReviewTag; import com.funeat.review.persistence.ReviewRepository; import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.review.presentation.dto.RankingReviewDto; +import com.funeat.review.presentation.dto.RankingReviewsResponse; import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.review.presentation.dto.SortingReviewsPageDto; import com.funeat.review.presentation.dto.SortingReviewsResponse; @@ -103,4 +106,14 @@ public SortingReviewsResponse sortingReviews(final Long productId, return SortingReviewsResponse.toResponse(pageDto, reviewDtos); } + + public RankingReviewsResponse getTopReviews() { + final List rankingReviews = reviewRepository.findTop3ByOrderByFavoriteCountDesc(); + + final List dtos = rankingReviews.stream() + .map(RankingReviewDto::toDto) + .collect(Collectors.toList()); + + return RankingReviewsResponse.toResponse(dtos); + } } diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java index 89c1ef36..caa89f0a 100644 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java @@ -6,7 +6,11 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ReviewRepository extends JpaRepository { Page findReviewsByProduct(final Pageable pageable, final Product product); + + List findTop3ByOrderByFavoriteCountDesc(); } diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index a9171d41..5eb68c8b 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -1,12 +1,17 @@ package com.funeat.review.presentation; import com.funeat.review.application.ReviewService; +import com.funeat.review.domain.Review; +import com.funeat.review.presentation.dto.RankingReviewDto; +import com.funeat.review.presentation.dto.RankingReviewsResponse; import com.funeat.review.presentation.dto.ReviewCreateRequest; import com.funeat.review.presentation.dto.ReviewFavoriteRequest; -import java.net.URI; - +import com.funeat.review.presentation.dto.SortingReviewsResponse; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -15,12 +20,9 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import com.funeat.review.presentation.dto.SortingReviewsResponse; -import org.springframework.data.domain.Pageable; -import org.springframework.data.web.PageableDefault; -import org.springframework.web.bind.annotation.GetMapping; - +import java.net.URI; import java.util.List; +import java.util.stream.Collectors; @RestController public class ReviewController { @@ -47,6 +49,8 @@ public ResponseEntity toggleLikeReview(@PathVariable Long productId, @Path return ResponseEntity.noContent().build(); + } + @GetMapping(value = "/api/products/{productId}/reviews") public ResponseEntity getSortingReviews(@PathVariable Long productId, @PageableDefault Pageable pageable) { @@ -54,4 +58,11 @@ public ResponseEntity getSortingReviews(@PathVariable Lo return ResponseEntity.ok(response); } + + @GetMapping("/api/ranks/reviews") + public ResponseEntity getRankingReviews() { + final RankingReviewsResponse response = reviewService.getTopReviews(); + + return ResponseEntity.ok(response); + } } diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java new file mode 100644 index 00000000..d3ca4620 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java @@ -0,0 +1,62 @@ +package com.funeat.review.presentation.dto; + +import com.funeat.review.domain.Review; + +public class RankingReviewDto { + + private final Long reviewId; + private final Long productId; + private final String productName; + private final String content; + private final Double rating; + private final Long favoriteCount; + + public RankingReviewDto(final Long reviewId, + final Long productId, + final String productName, + final String content, + final Double rating, + final Long favoriteCount) { + this.reviewId = reviewId; + this.productId = productId; + this.productName = productName; + this.content = content; + this.rating = rating; + this.favoriteCount = favoriteCount; + } + + public static RankingReviewDto toDto(final Review review) { + return new RankingReviewDto( + review.getId(), + review.getProduct().getId(), + review.getProduct().getName(), + review.getContent(), + review.getRating(), + review.getFavoriteCount() + ); + } + + public Long getReviewId() { + return reviewId; + } + + public Long getProductId() { + return productId; + } + + public String getProductName() { + return productName; + } + + public String getContent() { + return content; + } + + public Double getRating() { + return rating; + } + + public Long getFavoriteCount() { + return favoriteCount; + } +} diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewsResponse.java b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewsResponse.java new file mode 100644 index 00000000..c5971de0 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewsResponse.java @@ -0,0 +1,20 @@ +package com.funeat.review.presentation.dto; + +import java.util.List; + +public class RankingReviewsResponse { + + private final List reviews; + + public RankingReviewsResponse(final List reviews) { + this.reviews = reviews; + } + + public static RankingReviewsResponse toResponse(final List reviews) { + return new RankingReviewsResponse(reviews); + } + + public List getReviews() { + return reviews; + } +} diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java index 4fb920a0..e62bf46e 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java @@ -1,6 +1,7 @@ package com.funeat.acceptance.common; import com.funeat.member.persistence.MemberRepository; +import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.persistence.CategoryRepository; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.persistence.ReviewRepository; @@ -41,6 +42,9 @@ public abstract class AcceptanceTest { @Autowired public ReviewTagRepository reviewTagRepository; + @Autowired + public ReviewFavoriteRepository reviewFavoriteRepository; + @BeforeEach void setUp() { RestAssured.port = port; diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index e2939fd5..57da0fc2 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -1,21 +1,14 @@ package com.funeat.acceptance.review; -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.정상_생성; -import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; -import static io.restassured.RestAssured.given; -import static org.assertj.core.api.Assertions.assertThat; - import com.funeat.acceptance.common.AcceptanceTest; import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; import com.funeat.member.domain.favorite.ReviewFavorite; -import com.funeat.member.persistence.MemberRepository; -import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.domain.Category; import com.funeat.product.domain.CategoryType; import com.funeat.product.domain.Product; import com.funeat.review.domain.Review; +import com.funeat.review.presentation.dto.RankingReviewDto; import com.funeat.review.presentation.dto.ReviewCreateRequest; import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewDto; @@ -34,15 +27,13 @@ import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; import static com.funeat.acceptance.common.CommonSteps.정상_생성; import static com.funeat.acceptance.common.CommonSteps.정상_처리; +import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; import static io.restassured.RestAssured.given; import static org.assertj.core.api.Assertions.assertThat; @SuppressWarnings("NonAsciiCharacters") class ReviewAcceptanceTest extends AcceptanceTest { - @Autowired - private ReviewFavoriteRepository reviewFavoriteRepository; - @BeforeEach void init() { reviewTagRepository.deleteAll(); @@ -157,7 +148,6 @@ void init() { private List 태그_추가_요청() { final Tag testTag1 = tagRepository.save(new Tag("testTag1")); final Tag testTag2 = tagRepository.save(new Tag("testTag2")); - return List.of(testTag1.getId(), testTag2.getId()); } @@ -205,6 +195,41 @@ void init() { 좋아요_기준_리뷰_목록_조회_결과를_검증한다(response, sortingReviews); } + @Test + void 리뷰_랭킹을_조회하다() { + // given + final var category = new Category("간편식사", CategoryType.FOOD); + 카테고리_추가_요청(category); + + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가_요청(members); + + final var product1 = new Product("김밥", 1000L, "image.png", "김밥", category); + final var product2 = new Product("물", 500L, "water.jpg", "물", category); + final var products = List.of(product1, product2); + 복수_상품_추가_요청(products); + + final var review1 = new Review(member1, product1, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product1, "review2.jpg", 4.5, "역삼역", true, 351L); + final var review3 = new Review(member3, product1, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review4 = new Review(member2, product2, "review4.jpg", 5.0, "ㅁㅜㄹ", true, 247L); + final var review5 = new Review(member3, product2, "review5.jpg", 1.5, "ㄴㄴ", false, 83L); + final var reviews = List.of(review1, review2, review3, review4, review5); + 복수_리뷰_추가(reviews); + + final var rankingReviews = List.of(review2, review4, review3); + + // when + final var response = 리뷰_랭킹_조회_요청(); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 리뷰_랭킹_조회_결과를_검증한다(response, rankingReviews); + } + private void 카테고리_추가_요청(final Category category) { categoryRepository.save(category); } @@ -217,6 +242,10 @@ void init() { return productRepository.save(product).getId(); } + private void 복수_상품_추가_요청(final List products) { + productRepository.saveAll(products); + } + private void 복수_리뷰_추가(final List reviews) { reviewRepository.saveAll(reviews); } @@ -233,6 +262,14 @@ void init() { .extract(); } + private ExtractableResponse 리뷰_랭킹_조회_요청() { + return given() + .when() + .get("/api/ranks/reviews") + .then() + .extract(); + } + private void 좋아요_기준_리뷰_목록_조회_결과를_검증한다(final ExtractableResponse response, final List reviews) { 페이지를_검증한다(response); @@ -255,4 +292,14 @@ void init() { .ignoringFields("id") .isEqualTo(expected); } + + private void 리뷰_랭킹_조회_결과를_검증한다(final ExtractableResponse response, + final List reviews) { + final var expected = reviews.stream() + .map(RankingReviewDto::toDto) + .collect(Collectors.toList()); + final var actual = response.jsonPath() + .getList("reviews", RankingReviewDto.class); + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } } diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java new file mode 100644 index 00000000..199fdeff --- /dev/null +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java @@ -0,0 +1,96 @@ +package com.funeat.review.persistence; + +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.domain.Review; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; + +@DataJpaTest +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +public class ReviewRepositoryTest { + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ProductRepository productRepository; + + @Test + void 특정_상품에_대한_좋아요_기준_내림차순으로_정렬한다() { + // given + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + memberRepository.saveAll(members); + + final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); + productRepository.save(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + reviewRepository.saveAll(reviews); + + final var expected = List.of(review1, review3); + + final var page = PageRequest.of(0, 2, Sort.by("favoriteCount").descending()); + + // when + final List actual = reviewRepository.findReviewsByProduct(page, product) + .getContent(); + + // then + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void 전체_리뷰_목록에서_가장_좋아요가_높은_상위_3개의_리뷰를_가져온다() { + // given + + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + memberRepository.saveAll(members); + + final var product1 = new Product("김밥", 1000L, "image.png", "김밥", null); + final var product2 = new Product("물", 500L, "water.jpg", "물", null); + final var products = List.of(product1, product2); + productRepository.saveAll(products); + + final var review1 = new Review(member1, product1, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product1, "review2.jpg", 4.5, "역삼역", true, 351L); + final var review3 = new Review(member3, product1, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review4 = new Review(member2, product2, "review4.jpg", 5.0, "ㅁㅜㄹ", true, 247L); + final var review5 = new Review(member3, product2, "review5.jpg", 1.5, "ㄴㄴ", false, 83L); + final var reviews = List.of(review1, review2, review3, review4, review5); + reviewRepository.saveAll(reviews); + + final var expected = List.of(review2, review4, review3); + + // when + final var actual = reviewRepository.findTop3ByOrderByFavoriteCountDesc(); + + // then + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } +} From b88e1308b529b8f4f6c776521bab02b1ca7ec0f5 Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Tue, 18 Jul 2023 09:52:10 +0900 Subject: [PATCH 015/185] =?UTF-8?q?[FE]=20feat:=20ProductItem,=20ProductLi?= =?UTF-8?q?st=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 디자인 시스템 버전 업데이트 * chore: 스토리북 설정에 provider 추가 * feat: ProductItem 컴포넌트 추가 * feat: ProductItem 컴포넌트 스토리 작성 * refactor: ProductItem props 추가 * feat: ProductList 컴포넌트 추가 * feat: ProductList 컴포넌트 스토리 작성 * chore: image 타입 추가 * feat: ProductItem 높이 및 패딩 수정 * style: ProductList import 순서 수정 * feat: ProductItem 스토리북 args 추가 * chore: 컴포넌트 폴더 gitkeep 파일 삭제 * feat: ProductList border 색상 수정 * refactor: 상품 타입, 목데이터 분리 * style: import 경로 수정 * style: 랭킹 관련 컴포넌트 스토리 경로 추가 * chore: styled css 타입 추가 * feat: 평점, 리뷰수 svg 적용 --- frontend/.babelrc.json | 2 +- frontend/package.json | 3 +- .../ProductItem/ProductItem.stories.tsx | 18 ++++ .../Product/ProductItem/ProductItem.tsx | 83 +++++++++++++++++++ .../ProductList/ProductList.stories.tsx | 13 +++ .../Product/ProductList/ProductList.tsx | 28 +++++++ .../RankingProductItem.stories.tsx | 2 +- .../RankingProductItem/RankingProductItem.tsx | 2 +- .../RankingProductList.stories.tsx | 2 +- .../RankingProductList/RankingProductList.tsx | 4 +- frontend/src/mocks/data/products.json | 18 ++++ frontend/src/types/Ranking.ts | 6 -- frontend/src/types/images.d.ts | 14 ++++ frontend/src/types/product.ts | 8 ++ frontend/src/types/styled.d.ts | 7 ++ frontend/yarn.lock | 73 +++++++++------- 16 files changed, 238 insertions(+), 45 deletions(-) create mode 100644 frontend/src/components/Product/ProductItem/ProductItem.stories.tsx create mode 100644 frontend/src/components/Product/ProductItem/ProductItem.tsx create mode 100644 frontend/src/components/Product/ProductList/ProductList.stories.tsx create mode 100644 frontend/src/components/Product/ProductList/ProductList.tsx create mode 100644 frontend/src/mocks/data/products.json delete mode 100644 frontend/src/types/Ranking.ts create mode 100644 frontend/src/types/images.d.ts create mode 100644 frontend/src/types/product.ts create mode 100644 frontend/src/types/styled.d.ts diff --git a/frontend/.babelrc.json b/frontend/.babelrc.json index 00ca841a..2c055d07 100644 --- a/frontend/.babelrc.json +++ b/frontend/.babelrc.json @@ -12,5 +12,5 @@ "@babel/preset-typescript", "@babel/preset-react" ], - "plugins": [] + "plugins": ["babel-plugin-styled-components"] } diff --git a/frontend/package.json b/frontend/package.json index 2f691e00..ee02fcd5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { - "@fun-eat/design-system": "^0.1.1", + "@fun-eat/design-system": "^0.2.0", "react": "^18.2.0", "react-dom": "^18.2.0", "styled-components": "^6.0.2" @@ -32,6 +32,7 @@ "@types/styled-components": "^5.1.26", "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", + "babel-plugin-styled-components": "^2.1.4", "eslint": "^8.44.0", "eslint-import-resolver-typescript": "^3.5.5", "eslint-import-resolver-webpack": "^0.13.2", diff --git a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx b/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx new file mode 100644 index 00000000..4961a449 --- /dev/null +++ b/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductItem from './ProductItem'; + +import mockProducts from '@/mocks/data/products.json'; + +const meta: Meta = { + title: 'product/ProductItem', + component: ProductItem, + args: { + product: mockProducts[0], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Product/ProductItem/ProductItem.tsx b/frontend/src/components/Product/ProductItem/ProductItem.tsx new file mode 100644 index 00000000..b360a8a2 --- /dev/null +++ b/frontend/src/components/Product/ProductItem/ProductItem.tsx @@ -0,0 +1,83 @@ +import { Text, useTheme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; +import type { Product } from '@/types/product'; + +interface ProductItemProps { + product: Product; +} + +const ProductItem = ({ product }: ProductItemProps) => { + const theme = useTheme(); + const { name, price, image, averageRating, reviewCount } = product; + + return ( + + {name} + + + {name} + + {price.toLocaleString('ko-KR')}원 + + + + + {averageRating} + + + + + + {reviewCount} + + + + + + ); +}; + +export default ProductItem; + +const ProductItemContainer = styled.div` + display: flex; + align-items: center; + height: 150px; + padding: 30px; +`; + +const ProductInfoWrapper = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + margin-left: 30px; +`; + +const ProductReviewWrapper = styled.div` + display: flex; + column-gap: 20px; + margin-left: -2px; +`; + +const RatingIconWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 4px; + + & > svg { + padding-bottom: 2px; + } +`; + +const ReviewIconWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 4px; + + & > svg { + padding-top: 2px; + } +`; diff --git a/frontend/src/components/Product/ProductList/ProductList.stories.tsx b/frontend/src/components/Product/ProductList/ProductList.stories.tsx new file mode 100644 index 00000000..de89073b --- /dev/null +++ b/frontend/src/components/Product/ProductList/ProductList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductList from './ProductList'; + +const meta: Meta = { + title: 'product/ProductList', + component: ProductList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Product/ProductList/ProductList.tsx b/frontend/src/components/Product/ProductList/ProductList.tsx new file mode 100644 index 00000000..d96ac830 --- /dev/null +++ b/frontend/src/components/Product/ProductList/ProductList.tsx @@ -0,0 +1,28 @@ +import styled from 'styled-components'; + +import ProductItem from '../ProductItem/ProductItem'; + +import mockProducts from '@/mocks/data/products.json'; + +const ProductList = () => { + return ( + + {mockProducts.map((product) => ( +
  • + +
  • + ))} +
    + ); +}; + +export default ProductList; + +const ProductListContainer = styled.ul` + display: flex; + flex-direction: column; + + & > li { + border-bottom: 1px solid ${({ theme }) => theme.borderColors.disabled}; + } +`; diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx b/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx index c3dda407..28188914 100644 --- a/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx +++ b/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import RankingProductItem from './RankingProductItem'; const meta: Meta = { - title: 'RankingProductItem', + title: 'product/RankingProductItem', component: RankingProductItem, args: { rankingProduct: { diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.tsx b/frontend/src/components/RankingProductItem/RankingProductItem.tsx index 3ec57539..baa2a44a 100644 --- a/frontend/src/components/RankingProductItem/RankingProductItem.tsx +++ b/frontend/src/components/RankingProductItem/RankingProductItem.tsx @@ -1,7 +1,7 @@ import { Text } from '@fun-eat/design-system'; import styled from 'styled-components'; -import type { RankingProduct } from '@types'; +import type { RankingProduct } from '@/types'; interface RankingProductItemProps { rankingProduct: RankingProduct; diff --git a/frontend/src/components/RankingProductList/RankingProductList.stories.tsx b/frontend/src/components/RankingProductList/RankingProductList.stories.tsx index 40fe0cff..4c8d9544 100644 --- a/frontend/src/components/RankingProductList/RankingProductList.stories.tsx +++ b/frontend/src/components/RankingProductList/RankingProductList.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import RankingProductList from './RankingProductList'; const meta: Meta = { - title: 'RankingProductList', + title: 'product/RankingProductList', component: RankingProductList, }; diff --git a/frontend/src/components/RankingProductList/RankingProductList.tsx b/frontend/src/components/RankingProductList/RankingProductList.tsx index 7c7ee716..8c551114 100644 --- a/frontend/src/components/RankingProductList/RankingProductList.tsx +++ b/frontend/src/components/RankingProductList/RankingProductList.tsx @@ -1,5 +1,5 @@ -import RankingProductItem from '@components/RankingProductItem/RankingProductItem'; -import rankingProducts from '@mocks/data/rankingProducts.json'; +import RankingProductItem from '@/components/RankingProductItem/RankingProductItem'; +import rankingProducts from '@/mocks/data/rankingProducts.json'; const RankingProductList = () => { return ( diff --git a/frontend/src/mocks/data/products.json b/frontend/src/mocks/data/products.json new file mode 100644 index 00000000..542bbfd6 --- /dev/null +++ b/frontend/src/mocks/data/products.json @@ -0,0 +1,18 @@ +[ + { + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "averageRating": 4.5, + "reviewCount": 100 + }, + { + "id": 2, + "name": "새우깡", + "price": 1000, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "averageRating": 4.0, + "reviewCount": 55 + } +] diff --git a/frontend/src/types/Ranking.ts b/frontend/src/types/Ranking.ts deleted file mode 100644 index 954d341f..00000000 --- a/frontend/src/types/Ranking.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface RankingProduct { - id: number; - rank: number; - name: string; - image: string; -} diff --git a/frontend/src/types/images.d.ts b/frontend/src/types/images.d.ts new file mode 100644 index 00000000..c13a67fc --- /dev/null +++ b/frontend/src/types/images.d.ts @@ -0,0 +1,14 @@ +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} diff --git a/frontend/src/types/product.ts b/frontend/src/types/product.ts new file mode 100644 index 00000000..d3cb3ef0 --- /dev/null +++ b/frontend/src/types/product.ts @@ -0,0 +1,8 @@ +export interface Product { + id: number; + name: string; + price: number; + image: string; + averageRating: number; + reviewCount: number; +} diff --git a/frontend/src/types/styled.d.ts b/frontend/src/types/styled.d.ts new file mode 100644 index 00000000..cd4e8cd2 --- /dev/null +++ b/frontend/src/types/styled.d.ts @@ -0,0 +1,7 @@ +import type { CSSProp } from 'styled-components'; + +declare module 'react' { + interface Attributes { + css?: CSSProp; + } +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 529d9e2c..bdba20c6 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1558,10 +1558,10 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== -"@fun-eat/design-system@^0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.1.1.tgz#31334395cee74e7a57e511e5a39d9653825f3709" - integrity sha512-9u23qDvq5TahQ+B/IGJuRjmfoPNIjXh2GxGid3IInbxDkFeQqH2yjOSamkPP+lxS44vKx1Tk5RfeLBKaoxwkuA== +"@fun-eat/design-system@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.2.0.tgz#f5547889dcc8bdb51f3c7ad6b564a51b4f8815c6" + integrity sha512-wXnWEO+k4Kjf98uCfABhIr10Zhckjk0UetdJz2UsWDILG0/GSq/AGxIrQ/KpFRwJXqrmXPE6ZLM77nGvUumMhA== "@humanwhocodes/config-array@^0.11.10": version "0.11.10" @@ -2968,9 +2968,9 @@ "@types/react" "*" "@types/react@*", "@types/react@>=16", "@types/react@^18.2.14": - version "18.2.14" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" - integrity sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g== + version "18.2.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.15.tgz#14792b35df676c20ec3cf595b262f8c615a73066" + integrity sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3711,6 +3711,17 @@ babel-plugin-react-docgen@^4.2.1: lodash "^4.17.15" react-docgen "^5.0.0" +babel-plugin-styled-components@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz#9a1f37c7f32ef927b4b008b529feb4a2c82b1092" + integrity sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + lodash "^4.17.21" + picomatch "^2.3.1" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -3933,9 +3944,9 @@ camelize@^1.0.0: integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== caniuse-lite@^1.0.30001503: - version "1.0.30001515" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001515.tgz#418aefeed9d024cd3129bfae0ccc782d4cb8f12b" - integrity sha512-eEFDwUOZbE24sb+Ecsx3+OvNETqjWIdabMy52oOkIgcUtAsQifjUG9q4U9dgTHJM2mfk4uEPxc0+xuFdJ629QA== + version "1.0.30001516" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz#621b1be7d85a8843ee7d210fd9d87b52e3daab3a" + integrity sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g== case-sensitive-paths-webpack-plugin@^2.4.0: version "2.4.0" @@ -4631,9 +4642,9 @@ ejs@^3.1.8: jake "^10.8.5" electron-to-chromium@^1.4.431: - version "1.4.459" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.459.tgz#25a23370f4ae8aaa8f77aaf00133aa4994f4148e" - integrity sha512-XXRS5NFv8nCrBL74Rm3qhJjA2VCsRFx0OjHKBMPI0otij56aun8UWiKTDABmd5/7GTR021pA4wivs+Ri6XCElg== + version "1.4.461" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz#6b14af66042732bf883ab63a4d82cac8f35eb252" + integrity sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ== emoji-regex@^8.0.0: version "8.0.0" @@ -4977,9 +4988,9 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: estraverse "^4.1.1" eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd" + integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -4990,9 +5001,9 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== eslint@^8.44.0: - version "8.44.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" - integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + version "8.45.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78" + integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" @@ -5019,7 +5030,6 @@ eslint@^8.44.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -5031,13 +5041,12 @@ eslint@^8.44.0: natural-compare "^1.4.0" optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" espree@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" - integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: acorn "^8.9.0" acorn-jsx "^5.3.2" @@ -5978,7 +5987,7 @@ ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -7579,9 +7588,9 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss@^8.4.21, postcss@^8.4.23: - version "8.4.25" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" - integrity sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw== + version "8.4.26" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.26.tgz#1bc62ab19f8e1e5463d98cf74af39702a00a9e94" + integrity sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -8610,7 +8619,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.0.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -8621,9 +8630,9 @@ style-loader@^3.3.1: integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== styled-components@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.0.3.tgz#03863133b8340912f52be623e3262ba9eb87ccc0" - integrity sha512-qEyWvDK4CYCyDckNIruRJIcQSvcUR3dVEw/fwxu1v0LFzUMPr2uf5PhXHp17FkGK+S4TkglOS+XIealo1MssQA== + version "6.0.4" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.0.4.tgz#55bb3a1197daf8075ae8b345b57eb03f2570d51e" + integrity sha512-lRJt4vg8hKJhlVG+VKz8QEqPCXKyTryZZ59odyK0UC0HHV3u/mshWTfSay8NpkN0Xijw1iN9r0Leld3dcCcp/w== dependencies: "@babel/cli" "^7.21.0" "@babel/core" "^7.21.0" From 3ed522c79255b0532c274cca8dda09fdea79cb1a Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Tue, 18 Jul 2023 09:57:12 +0900 Subject: [PATCH 016/185] =?UTF-8?q?[FE]=20feat:=20CategoryMenu=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: storybook에 FunEatProvider 적용 * feat: TabMenu 컴포넌트 추가 * refactor: typeof meta -> typeof TabMenu로 수정 * refactor: button type 추가 및 color을 textColors로 변경 * refactor: meta안의 args 삭제 * refactor: TabMenu를 ul, li태그로 변경 * feat: api 명세에 맞춰 카테고리 타입 생성 * feat: TabMenu컴포넌트 스타일 수정 * feat: babel-plugin-styled-components 설치 * feat: selectedMenuStyles를 cssProp으로 넘겨주게끔 수정 * style: TabMenu -> CategoryMenu로 컴포넌트 네이밍 변경 --- .../CategoryMenu/CategoryMenu.stories.tsx | 34 ++++++++++ .../Common/CategoryMenu/CategoryMenu.tsx | 67 +++++++++++++++++++ frontend/src/components/Common/index.ts | 1 + frontend/src/types/common.ts | 4 ++ 4 files changed, 106 insertions(+) create mode 100644 frontend/src/components/Common/CategoryMenu/CategoryMenu.stories.tsx create mode 100644 frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx create mode 100644 frontend/src/types/common.ts diff --git a/frontend/src/components/Common/CategoryMenu/CategoryMenu.stories.tsx b/frontend/src/components/Common/CategoryMenu/CategoryMenu.stories.tsx new file mode 100644 index 00000000..f2e6fc2a --- /dev/null +++ b/frontend/src/components/Common/CategoryMenu/CategoryMenu.stories.tsx @@ -0,0 +1,34 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import CategoryMenu from './CategoryMenu'; + +const meta: Meta = { + title: 'common/CategoryMenu', + component: CategoryMenu, +}; + +export default meta; +type Story = StoryObj; + +export const FoodCategory: Story = { + args: { + menuList: [ + { id: 0, name: '즉석조리' }, + { id: 1, name: '과자' }, + { id: 2, name: '간편식사' }, + { id: 3, name: '아이스크림' }, + ], + menuVariant: 'food', + }, +}; + +export const StoreCategory: Story = { + args: { + menuList: [ + { id: 0, name: 'CU' }, + { id: 1, name: 'GS25' }, + { id: 2, name: '이마트24' }, + ], + menuVariant: 'store', + }, +}; diff --git a/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx b/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx new file mode 100644 index 00000000..460c6334 --- /dev/null +++ b/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx @@ -0,0 +1,67 @@ +import { Button, Text, theme } from '@fun-eat/design-system'; +import { useState } from 'react'; +import type { CSSProp } from 'styled-components'; +import styled from 'styled-components'; + +import type { Category } from '@/types/common'; + +interface CategoryMenuProps { + menuVariant: 'food' | 'store'; + menuList: Category[]; +} + +const CategoryMenu = ({ menuList, menuVariant }: CategoryMenuProps) => { + const [selectedMenu, setSelectedMenu] = useState(0); + + const selectMenu = (menuId: number) => { + setSelectedMenu(menuId); + }; + + return ( + + {menuList.map((menu) => { + const isSelected = menu.id === selectedMenu; + return ( +
  • + +
  • + ); + })} +
    + ); +}; + +export default CategoryMenu; + +type CategoryMenuStyleProps = Pick; + +const CategoryMenuContainer = styled.ul` + display: flex; + gap: 8px; +`; + +const selectedCategoryMenuStyles: Record = { + food: ` + background: ${theme.colors.gray5}; + color: ${theme.textColors.white}; + `, + store: ` + background: ${theme.colors.primary}; + color: ${theme.textColors.default}; + `, +}; + +const CategoryMenuButtonText = styled(Text)` + color: inherit; +`; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index 8c89a684..a8c8d44f 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -1,2 +1,3 @@ +export { default as CategoryMenu } from './CategoryMenu/CategoryMenu'; export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts new file mode 100644 index 00000000..dcb7bf32 --- /dev/null +++ b/frontend/src/types/common.ts @@ -0,0 +1,4 @@ +export interface Category { + id: number; + name: string; +} From 2046a96714f13038f1e7a9186c0045d8e17acd48 Mon Sep 17 00:00:00 2001 From: Hanuel Lee <91522259+hanueleee@users.noreply.github.com> Date: Tue, 18 Jul 2023 10:26:53 +0900 Subject: [PATCH 017/185] =?UTF-8?q?[BE]=20feat:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=EB=B3=84=20=EC=83=81=ED=92=88=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#51)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: string으로 들어온 SortOrderType과 SortType을 enum으로 변환해주는 Converter 추가 * feat: 테스트 격리용 객체인 DataCleaner 추가 * feat: 카테고리별 상품 목록 조회 api의 반환값에 사용되는 dto들(ProductsInCategoryResponse, ProductInCategoryDto, ProductsInCategoryPageDto) 추가 * feat: 카테고리별 상품 목록 조회 api 기능 구현 * refactor: SortType과 SortOrderType 삭제 * refactor: dto 패키지 위치 수정 * test: 페이징시 디폴트 사이즈는 10 * refactor: 카테고리별 상품 목록 조회 api 수정 명세 반영 (기존) /api/categories/{category_id}/products?option=price&order=desc&page=1 (수정) /api/categories/{category_id}/products?page=1&sort=price,desc * test: ProductRepositoryTest에 카테고리별로 상품 목록을 조회하는 테스트 추가 * test: 모든 테스트 격리를 위해 DataClearExtension 추가 * refactor: 코드 컨벤션 적용 * refactor: 카테고리별 상품 목록 조회 api의 page관련 명세 수정 * refactor: ReviewRepository의 countByProduct 수정 * test: ReviewRepository의 countByProduct에 대한 테스트 작성 --- .../funeat/common/GrobalControllerAdvice.java | 20 ++++ .../java/com/funeat/member/domain/Member.java | 12 +++ .../product/application/ProductService.java | 48 ++++++++++ .../funeat/product/domain/SortOrderType.java | 5 - .../com/funeat/product/domain/SortType.java | 5 - .../CategoryResponse.java | 2 +- .../ProductInCategoryDto.java} | 13 +-- .../ProductResponse.java | 4 +- .../dto/ProductsInCategoryPageDto.java | 64 +++++++++++++ .../dto/ProductsInCategoryResponse.java | 22 +++++ .../persistence/ProductRepository.java | 5 + .../presentation/CategoryController.java | 1 + .../presentation/ProductController.java | 31 +++++++ .../review/application/ReviewService.java | 1 + .../review/persistence/ReviewRepository.java | 2 + .../review/presentation/ReviewController.java | 2 +- .../acceptance/common/AcceptanceTest.java | 3 + .../product/ProductAcceptanceTest.java | 61 ++++++++++--- .../acceptance/product/ProductSteps.java | 17 ++-- .../java/com/funeat/common/DataCleaner.java | 45 +++++++++ .../com/funeat/common/DataClearExtension.java | 19 ++++ .../persistence/CategoryRepositoryTest.java | 24 ++--- .../persistence/ProductRepositoryTest.java | 91 +++++++++++++++++++ .../com/funeat/review/TestImageUploader.java | 42 +++++++++ .../persistence/ReviewRepositoryTest.java | 45 +++++++-- .../src/test/resources/application.properties | 1 + 26 files changed, 521 insertions(+), 64 deletions(-) create mode 100644 backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java create mode 100644 backend/src/main/java/com/funeat/product/application/ProductService.java delete mode 100644 backend/src/main/java/com/funeat/product/domain/SortOrderType.java delete mode 100644 backend/src/main/java/com/funeat/product/domain/SortType.java rename backend/src/main/java/com/funeat/product/{presentation => dto}/CategoryResponse.java (92%) rename backend/src/main/java/com/funeat/product/{presentation/CategoryProductResponse.java => dto/ProductInCategoryDto.java} (63%) rename backend/src/main/java/com/funeat/product/{presentation => dto}/ProductResponse.java (95%) create mode 100644 backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java create mode 100644 backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/ProductController.java create mode 100644 backend/src/test/java/com/funeat/common/DataCleaner.java create mode 100644 backend/src/test/java/com/funeat/common/DataClearExtension.java create mode 100644 backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java create mode 100644 backend/src/test/java/com/funeat/review/TestImageUploader.java diff --git a/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java b/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java new file mode 100644 index 00000000..db71af50 --- /dev/null +++ b/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java @@ -0,0 +1,20 @@ +package com.funeat.common; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GrobalControllerAdvice { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @ExceptionHandler + public ResponseEntity handle(Exception e) { + System.out.println("hello"); + log.warn(e.getMessage()); + return ResponseEntity.badRequest().build(); + } +} diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java index 53398c2a..3059f4de 100644 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -32,6 +32,18 @@ public class Member { private String phoneNumber; + protected Member() { + } + + public Member(final String nickname, final String profileImage, final Integer age, final Gender gender, + final String phoneNumber) { + this.nickname = nickname; + this.profileImage = profileImage; + this.age = age; + this.gender = gender; + this.phoneNumber = phoneNumber; + } + @OneToMany(mappedBy = "member") private List reviewFavorites = new ArrayList<>(); diff --git a/backend/src/main/java/com/funeat/product/application/ProductService.java b/backend/src/main/java/com/funeat/product/application/ProductService.java new file mode 100644 index 00000000..ec822605 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/application/ProductService.java @@ -0,0 +1,48 @@ +package com.funeat.product.application; + +import com.funeat.product.domain.Category; +import com.funeat.product.domain.Product; +import com.funeat.product.dto.ProductInCategoryDto; +import com.funeat.product.dto.ProductsInCategoryPageDto; +import com.funeat.product.dto.ProductsInCategoryResponse; +import com.funeat.product.persistence.CategoryRepository; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.persistence.ReviewRepository; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +public class ProductService { + + private final CategoryRepository categoryRepository; + private final ProductRepository productRepository; + private final ReviewRepository reviewRepository; + + public ProductService(final CategoryRepository categoryRepository, final ProductRepository productRepository, + final ReviewRepository reviewRepository) { + this.categoryRepository = categoryRepository; + this.productRepository = productRepository; + this.reviewRepository = reviewRepository; + } + + public ProductsInCategoryResponse getAllProductsInCategory(final Long categoryId, + final Pageable pageable) { + Category category = categoryRepository.findById(categoryId) + .orElseThrow(IllegalArgumentException::new); + + Page productPages = productRepository.findAllByCategory(category, pageable); + + ProductsInCategoryPageDto pageDto = ProductsInCategoryPageDto.toDto(productPages); + List productDtos = productPages.getContent() + .stream() + .map(it -> ProductInCategoryDto.toDto(it, reviewRepository.countByProduct(it))) + .collect(Collectors.toList()); + + return new ProductsInCategoryResponse(pageDto, productDtos); + } +} diff --git a/backend/src/main/java/com/funeat/product/domain/SortOrderType.java b/backend/src/main/java/com/funeat/product/domain/SortOrderType.java deleted file mode 100644 index 339b7402..00000000 --- a/backend/src/main/java/com/funeat/product/domain/SortOrderType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.funeat.product.domain; - -public enum SortOrderType { - ASC, DESC -} diff --git a/backend/src/main/java/com/funeat/product/domain/SortType.java b/backend/src/main/java/com/funeat/product/domain/SortType.java deleted file mode 100644 index d17a0be2..00000000 --- a/backend/src/main/java/com/funeat/product/domain/SortType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.funeat.product.domain; - -public enum SortType { - PRICE -} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java similarity index 92% rename from backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java rename to backend/src/main/java/com/funeat/product/dto/CategoryResponse.java index b359aa92..ebf70e60 100644 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/CategoryResponse.java @@ -1,4 +1,4 @@ -package com.funeat.product.presentation; +package com.funeat.product.dto; import com.funeat.product.domain.Category; diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java similarity index 63% rename from backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java rename to backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java index 8dc95173..5c796db5 100644 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryProductResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/ProductInCategoryDto.java @@ -1,8 +1,8 @@ -package com.funeat.product.presentation; +package com.funeat.product.dto; import com.funeat.product.domain.Product; -public class CategoryProductResponse { +public class ProductInCategoryDto { private final Long id; private final String name; @@ -11,8 +11,8 @@ public class CategoryProductResponse { private final Double averageRating; private final Long reviewCount; - public CategoryProductResponse(final Long id, final String name, final Long price, final String image, - final Double averageRating, final Long reviewCount) { + public ProductInCategoryDto(final Long id, final String name, final Long price, final String image, + final Double averageRating, final Long reviewCount) { this.id = id; this.name = name; this.price = price; @@ -21,8 +21,8 @@ public CategoryProductResponse(final Long id, final String name, final Long pric this.reviewCount = reviewCount; } - public static CategoryProductResponse toResponse(final Product product, final Long reviewCount) { - return new CategoryProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImage(), + public static ProductInCategoryDto toDto(final Product product, final Long reviewCount) { + return new ProductInCategoryDto(product.getId(), product.getName(), product.getPrice(), product.getImage(), product.getAverageRating(), reviewCount); } @@ -49,4 +49,5 @@ public Double getAverageRating() { public Long getReviewCount() { return reviewCount; } + } diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductResponse.java similarity index 95% rename from backend/src/main/java/com/funeat/product/presentation/ProductResponse.java rename to backend/src/main/java/com/funeat/product/dto/ProductResponse.java index 0645d9a2..64a857fe 100644 --- a/backend/src/main/java/com/funeat/product/presentation/ProductResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/ProductResponse.java @@ -1,4 +1,4 @@ -package com.funeat.product.presentation; +package com.funeat.product.dto; import com.funeat.product.domain.Product; import com.funeat.tag.domain.Tag; @@ -25,7 +25,7 @@ public ProductResponse(final Long id, final String name, final Long price, final this.tags = tags; } - public static ProductResponse toResponse(final Product product, List tags) { + public static ProductResponse toResponse(final Product product, final List tags) { List tagDtos = new ArrayList<>(); for (Tag tag : tags) { tagDtos.add(TagDto.toDto(tag)); diff --git a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java new file mode 100644 index 00000000..438f2610 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java @@ -0,0 +1,64 @@ +package com.funeat.product.dto; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.funeat.product.domain.Product; +import org.springframework.data.domain.Page; + +public class ProductsInCategoryPageDto { + private final Long totalDataCount; + private final Long totalPages; + private final boolean firstPage; + private final boolean lastPage; + private final Long requestPage; + private final Long requestSize; + + @JsonCreator + public ProductsInCategoryPageDto(final Long totalDataCount, + final Long totalPages, + final boolean FirstPage, + final boolean LastPage, + final Long requestPage, + final Long requestSize) { + this.totalDataCount = totalDataCount; + this.totalPages = totalPages; + this.firstPage = FirstPage; + this.lastPage = LastPage; + this.requestPage = requestPage; + this.requestSize = requestSize; + } + + public static ProductsInCategoryPageDto toDto(final Page page) { + return new ProductsInCategoryPageDto( + page.getTotalElements(), + Long.valueOf(page.getTotalPages()), + page.isFirst(), + page.isLast(), + Long.valueOf(page.getNumber()), + Long.valueOf(page.getSize()) + ); + } + + public Long getTotalDataCount() { + return totalDataCount; + } + + public Long getTotalPages() { + return totalPages; + } + + public boolean isFirstPage() { + return firstPage; + } + + public boolean isLastPage() { + return lastPage; + } + + public Long getRequestPage() { + return requestPage; + } + + public Long getRequestSize() { + return requestSize; + } +} diff --git a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java new file mode 100644 index 00000000..98546858 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java @@ -0,0 +1,22 @@ +package com.funeat.product.dto; + +import java.util.List; + +public class ProductsInCategoryResponse { + + private final ProductsInCategoryPageDto page; + private final List products; + + public ProductsInCategoryResponse(final ProductsInCategoryPageDto page, final List products) { + this.page = page; + this.products = products; + } + + public ProductsInCategoryPageDto getPage() { + return page; + } + + public List getProducts() { + return products; + } +} diff --git a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java index bab5a7e1..2fe9ef9b 100644 --- a/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java +++ b/backend/src/main/java/com/funeat/product/persistence/ProductRepository.java @@ -1,7 +1,12 @@ package com.funeat.product.persistence; +import com.funeat.product.domain.Category; import com.funeat.product.domain.Product; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface ProductRepository extends JpaRepository { + + Page findAllByCategory(final Category category, final Pageable pageable); } diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java index f514032c..a594edb6 100644 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java @@ -2,6 +2,7 @@ import com.funeat.product.application.CategoryService; import com.funeat.product.domain.CategoryType; +import com.funeat.product.dto.CategoryResponse; import java.util.List; import java.util.stream.Collectors; import org.springframework.http.ResponseEntity; diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductController.java b/backend/src/main/java/com/funeat/product/presentation/ProductController.java new file mode 100644 index 00000000..442b745f --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/ProductController.java @@ -0,0 +1,31 @@ +package com.funeat.product.presentation; + +import com.funeat.product.application.ProductService; +import com.funeat.product.dto.ProductsInCategoryResponse; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/categories") +public class ProductController { + + private final ProductService productService; + + public ProductController(final ProductService productService) { + this.productService = productService; + } + + @GetMapping("/{category_id}/products") + public ResponseEntity getAllProductsInCategory( + @PathVariable(name = "category_id") final Long categoryId, + @PageableDefault Pageable pageable + ) { + ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); + return ResponseEntity.ok(response); + } +} diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java index ea77df22..f6a58acb 100644 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -38,6 +38,7 @@ public class ReviewService { private final MemberRepository memberRepository; private final ProductRepository productRepository; private final ImageService imageService; + private final ReviewFavoriteRepository reviewFavoriteRepository; public ReviewService(final ReviewRepository reviewRepository, final TagRepository tagRepository, diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java index caa89f0a..209f3174 100644 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewRepository.java @@ -13,4 +13,6 @@ public interface ReviewRepository extends JpaRepository { Page findReviewsByProduct(final Pageable pageable, final Product product); List findTop3ByOrderByFavoriteCountDesc(); + + Long countByProduct(final Product product); } diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index 5eb68c8b..8f55950b 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -1,6 +1,7 @@ package com.funeat.review.presentation; import com.funeat.review.application.ReviewService; + import com.funeat.review.domain.Review; import com.funeat.review.presentation.dto.RankingReviewDto; import com.funeat.review.presentation.dto.RankingReviewsResponse; @@ -19,7 +20,6 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; - import java.net.URI; import java.util.List; import java.util.stream.Collectors; diff --git a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java index e62bf46e..55684aae 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/common/AcceptanceTest.java @@ -1,5 +1,6 @@ package com.funeat.acceptance.common; +import com.funeat.common.DataClearExtension; import com.funeat.member.persistence.MemberRepository; import com.funeat.member.persistence.ReviewFavoriteRepository; import com.funeat.product.persistence.CategoryRepository; @@ -11,11 +12,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; +@ExtendWith(DataClearExtension.class) @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java index 4d5e8a23..c80287c9 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java @@ -14,11 +14,10 @@ import com.funeat.acceptance.common.AcceptanceTest; import com.funeat.product.domain.Category; import com.funeat.product.domain.Product; -import com.funeat.product.domain.SortOrderType; -import com.funeat.product.domain.SortType; -import com.funeat.product.presentation.CategoryProductResponse; -import com.funeat.product.presentation.CategoryResponse; -import com.funeat.product.presentation.ProductResponse; +import com.funeat.product.dto.CategoryResponse; +import com.funeat.product.dto.ProductInCategoryDto; +import com.funeat.product.dto.ProductResponse; +import com.funeat.product.dto.ProductsInCategoryPageDto; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; @@ -30,6 +29,8 @@ @SuppressWarnings("NonAsciiCharacters") class ProductAcceptanceTest extends AcceptanceTest { + public static final int PAGE_SIZE = 10; + @Test void 카테고리별_상품_목록을_가격낮은순으로_조회한다() { // given @@ -37,15 +38,24 @@ class ProductAcceptanceTest extends AcceptanceTest { final Product product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); final Product product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", 간편식사); final Product product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", 간편식사); - final List products = List.of(product1, product2, product3); + final Product product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", 간편식사); + final Product product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", 간편식사); + final Product product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", 간편식사); + final Product product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", 간편식사); + final Product product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", 간편식사); + final Product product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", 간편식사); + final Product product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", 간편식사); + final Product product11 = new Product("삼각김밥11", 300L, "image.png", "맛있는 삼각김밥11", 간편식사); + final List products = List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10, product11); 복수_상품_추가_요청(products); // when - final var response = 카테고리별_상품_목록_조회_요청(categoryId, SortType.PRICE, SortOrderType.ASC, 1); + final var response = 카테고리별_상품_목록_조회_요청(categoryId, "price", "asc", 0); // then STATUS_CODE를_검증한다(response, 정상_처리); - 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product1, product3, product2)); + 페이지를_검증한다(response, (long) products.size(), 0L); + 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product11, product8, product1, product4, product3, product6, product7, product2, product5, product10)); } @Test @@ -55,15 +65,25 @@ class ProductAcceptanceTest extends AcceptanceTest { final Product product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); final Product product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", 간편식사); final Product product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", 간편식사); - final List products = List.of(product1, product2, product3); + final Product product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", 간편식사); + final Product product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", 간편식사); + final Product product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", 간편식사); + final Product product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", 간편식사); + final Product product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", 간편식사); + final Product product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", 간편식사); + final Product product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", 간편식사); + final Product product11 = new Product("삼각김밥11", 300L, "image.png", "맛있는 삼각김밥11", 간편식사); + final List products = List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10, product11); 복수_상품_추가_요청(products); + // when - final var response = 카테고리별_상품_목록_조회_요청(categoryId, SortType.PRICE, SortOrderType.DESC, 1); + final var response = 카테고리별_상품_목록_조회_요청(categoryId, "price", "desc", 0); // then STATUS_CODE를_검증한다(response, 정상_처리); - 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product2, product3, product1)); + 페이지를_검증한다(response, (long) products.size(), 0L); + 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product9, product10, product5, product2, product7, product6, product3, product4, product1, product8)); } @Test @@ -109,14 +129,25 @@ class ProductAcceptanceTest extends AcceptanceTest { productRepository.saveAll(products); } + private void 페이지를_검증한다(final ExtractableResponse response, Long dataSize, Long page) { + long totalPages = (long) Math.ceil((double) dataSize / PAGE_SIZE); + ProductsInCategoryPageDto expected = new ProductsInCategoryPageDto(dataSize, totalPages, + page == 0, page == totalPages - 1, page, (long) PAGE_SIZE); + ProductsInCategoryPageDto actual = response.jsonPath().getObject("page", ProductsInCategoryPageDto.class); + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + private void 카테고리별_상품_목록_조회_결과를_검증한다(final ExtractableResponse response, final List products) { - final List expected = new ArrayList<>(); + final List expected = new ArrayList<>(); for (Product product : products) { - expected.add(CategoryProductResponse.toResponse(product, 0L)); + expected.add(ProductInCategoryDto.toDto(product, 0L)); } - final List actual = response.jsonPath().getList("products"); - assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields("id", "reviewCount") + final List actual = response.jsonPath().getList("products", ProductInCategoryDto.class); + + assertThat(actual) + .usingRecursiveComparison() + .ignoringFields("id", "reviewCount") .isEqualTo(expected); } diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java index dfaf236b..f465b668 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductSteps.java @@ -4,8 +4,6 @@ import com.funeat.product.domain.Category; import com.funeat.product.domain.CategoryType; -import com.funeat.product.domain.SortOrderType; -import com.funeat.product.domain.SortType; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; @@ -24,31 +22,36 @@ public class ProductSteps { public static ExtractableResponse 공통_상품_카테고리_목록_조회_요청() { return given() + .log().all() .queryParam("type", "food") .when() .get("/api/categories") .then() + .log().all() .extract(); } - public static ExtractableResponse 카테고리별_상품_목록_조회_요청(final Long categoryId, final SortType sortType, - final SortOrderType sortOrderType, - final Integer page) { + public static ExtractableResponse 카테고리별_상품_목록_조회_요청(final Long categoryId, final String sortType, + final String sortOrderType, + final int page) { return given() - .queryParam("sort", sortType.name().toLowerCase()) - .queryParam("order", sortOrderType.name().toLowerCase()) + .log().all() + .queryParam("sort", sortType+","+sortOrderType) .queryParam("page", page) .when() .get("/api/categories/{category_id}/products", categoryId) .then() + .log().all() .extract(); } public static ExtractableResponse 상품_상세_조회_요청(final Long productId) { return given() + .log().all() .when() .get("/api/products/{product_id}", productId) .then() + .log().all() .extract(); } } diff --git a/backend/src/test/java/com/funeat/common/DataCleaner.java b/backend/src/test/java/com/funeat/common/DataCleaner.java new file mode 100644 index 00000000..82113df4 --- /dev/null +++ b/backend/src/test/java/com/funeat/common/DataCleaner.java @@ -0,0 +1,45 @@ +package com.funeat.common; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.PostConstruct; +import javax.persistence.EntityManager; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; +import org.springframework.stereotype.Component; + +@Component +public class DataCleaner { + + private static final String FOREIGN_KEY_CHECK_FORMAT = "SET REFERENTIAL_INTEGRITY %s"; + private static final String TRUNCATE_FORMAT = "TRUNCATE TABLE %s"; + + private final List tableNames = new ArrayList<>(); + + @PersistenceContext + private EntityManager entityManager; + + @SuppressWarnings("unchecked") + @PostConstruct + public void findDatabaseTableNames() { + List tableInfos = entityManager.createNativeQuery("SHOW TABLES").getResultList(); + for (Object[] tableInfo : tableInfos) { + String tableName = (String) tableInfo[0]; + tableNames.add(tableName); + } + } + + @Transactional + public void clear() { + entityManager.clear(); + truncate(); + } + + private void truncate() { + entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, "FALSE")).executeUpdate(); + for (String tableName : tableNames) { + entityManager.createNativeQuery(String.format(TRUNCATE_FORMAT, tableName)).executeUpdate(); + } + entityManager.createNativeQuery(String.format(FOREIGN_KEY_CHECK_FORMAT, "TRUE")).executeUpdate(); + } +} diff --git a/backend/src/test/java/com/funeat/common/DataClearExtension.java b/backend/src/test/java/com/funeat/common/DataClearExtension.java new file mode 100644 index 00000000..0026eb24 --- /dev/null +++ b/backend/src/test/java/com/funeat/common/DataClearExtension.java @@ -0,0 +1,19 @@ +package com.funeat.common; + +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +public class DataClearExtension implements BeforeEachCallback { + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + DataCleaner dataCleaner = getDataCleaner(context); + dataCleaner.clear(); + } + + private DataCleaner getDataCleaner(ExtensionContext extensionContext) { + return SpringExtension.getApplicationContext(extensionContext) + .getBean(DataCleaner.class); + } +} diff --git a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java index 5537da76..b01cf681 100644 --- a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java +++ b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java @@ -2,17 +2,22 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.funeat.common.DataCleaner; +import com.funeat.common.DataClearExtension; import com.funeat.product.domain.Category; import com.funeat.product.domain.CategoryType; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; @DataJpaTest +@Import(DataCleaner.class) +@ExtendWith(DataClearExtension.class) @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) class CategoryRepositoryTest { @@ -30,32 +35,27 @@ class CategoryRepositoryTest { @Autowired private CategoryRepository categoryRepository; - @BeforeEach - void setUp() { - categoryRepository.deleteAll(); - categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); - } - @Test void 카테고리_타입이_FOOD인_모든_카테고리를_조회한다() { // given - final CategoryType foodType = CategoryType.FOOD; + categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); // when - final List actual = categoryRepository.findAllByType(foodType); + final List actual = categoryRepository.findAllByType(CategoryType.FOOD); // then - assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields() + assertThat(actual) + .usingRecursiveFieldByFieldElementComparatorIgnoringFields("id") .containsOnly(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료); } @Test void 카테고리_타입이_STORE인_모든_카테고리를_조회한다() { // given - final CategoryType storeType = CategoryType.STORE; + categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); // when - final List actual = categoryRepository.findAllByType(storeType); + final List actual = categoryRepository.findAllByType(CategoryType.STORE); // then assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields() diff --git a/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java new file mode 100644 index 00000000..2181ff03 --- /dev/null +++ b/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java @@ -0,0 +1,91 @@ +package com.funeat.product.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.common.DataCleaner; +import com.funeat.common.DataClearExtension; +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; +import com.funeat.product.domain.Product; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; + +@DataJpaTest +@Import(DataCleaner.class) +@ExtendWith(DataClearExtension.class) +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class ProductRepositoryTest { + + private Category category; + private Product product1; + private Product product2; + private Product product3; + private Product product4; + private Product product5; + private Product product6; + private Product product7; + private Product product8; + private Product product9; + private Product product10; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private ProductRepository productRepository; + + @BeforeEach + void setUp() { + category = categoryRepository.save(new Category("간편식사", CategoryType.FOOD)); + + product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", category); + product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", category); + product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", category); + product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", category); + product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", category); + product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", category); + product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", category); + product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", category); + product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", category); + product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", category); + } + + @Test + void 카테고리별_상품을_가격이_높은_순으로_정렬한다() { + // given + productRepository.saveAll( + List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10)); + + // when + PageRequest pageRequest = PageRequest.of(0, 3, Sort.by("price").descending()); + List actual = productRepository.findAllByCategory(category, pageRequest).getContent(); + + // then + assertThat(actual).containsExactly(product9, product10, product5); + } + + @Test + void 카테고리별_상품을_가격이_낮은_순으로_정렬한다() { + // given + productRepository.saveAll( + List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, + product10)); + + // when + PageRequest pageRequest = PageRequest.of(0, 3, Sort.by("price").ascending()); + List actual = productRepository.findAllByCategory(category, pageRequest).getContent(); + + // then + assertThat(actual).containsExactly(product8, product1, product4); + } +} diff --git a/backend/src/test/java/com/funeat/review/TestImageUploader.java b/backend/src/test/java/com/funeat/review/TestImageUploader.java new file mode 100644 index 00000000..7d6a3587 --- /dev/null +++ b/backend/src/test/java/com/funeat/review/TestImageUploader.java @@ -0,0 +1,42 @@ +package com.funeat.review; + +import com.funeat.review.application.ImageService; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +@Component +@Profile("test") +public class TestImageUploader implements ImageService { + + @Override + public void upload(final MultipartFile image) { + // 실제로 IO 작업을 수행하는 대신, 임시 디렉토리로 복사하도록 수정 + try { + final String temporaryPath = String.valueOf(System.currentTimeMillis()); + final Path tempDirectoryPath = Files.createTempDirectory(temporaryPath); + final Path filePath = tempDirectoryPath.resolve(image.getOriginalFilename()); + Files.copy(image.getInputStream(), filePath); + + deleteDirectory(tempDirectoryPath); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void deleteDirectory(Path directory) throws IOException { + // 디렉토리 내부 파일 및 디렉토리 삭제 + try (Stream pathStream = Files.walk(directory)) { + pathStream.sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + } + Files.deleteIfExists(directory); + } +} diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java index 199fdeff..5f1d98a2 100644 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java @@ -1,37 +1,62 @@ package com.funeat.review.persistence; +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.common.DataCleaner; +import com.funeat.common.DataClearExtension; import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Category; +import com.funeat.product.domain.CategoryType; import com.funeat.product.domain.Product; +import com.funeat.product.persistence.CategoryRepository; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.domain.Review; import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Sort; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.springframework.context.annotation.Import; @DataJpaTest +@Import(DataCleaner.class) +@ExtendWith(DataClearExtension.class) @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) -public class ReviewRepositoryTest { +class ReviewRepositoryTest { @Autowired - private ReviewRepository reviewRepository; + private MemberRepository memberRepository; @Autowired - private MemberRepository memberRepository; + private CategoryRepository categoryRepository; @Autowired private ProductRepository productRepository; + @Autowired + private ReviewRepository reviewRepository; + + @Test + void 상품에_달린_리뷰의_숫자를_반환한다() { + // given + Member member = memberRepository.save(new Member("test", "image.png", 27, Gender.FEMALE, "01036551086")); + Category category = categoryRepository.save(new Category("간편식사", CategoryType.FOOD)); + Product product1 = productRepository.save(new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", category)); + Product product2 = productRepository.save(new Product("라면", 2000L, "image.png", "맛있는 라면", category)); + + reviewRepository.save(new Review(member, product1, "review.png", 4.5, "이 삼각김밥은 최고!!", true)); + reviewRepository.save(new Review(member, product1, "review.png", 3.0, "이 삼각김밥은 별로", false)); + reviewRepository.save(new Review(member, product1, "review.png", 4.0, "이 삼각김밥은 쏘쏘", true)); + reviewRepository.save(new Review(member, product2, "review.png", 3.0, "이 라면은 맛있다", true)); + + // when + assertThat(reviewRepository.countByProduct(product1)).isEqualTo(3); + assertThat(reviewRepository.countByProduct(product2)).isEqualTo(1); + @Test void 특정_상품에_대한_좋아요_기준_내림차순으로_정렬한다() { // given diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties index a0bd609c..caecd462 100644 --- a/backend/src/test/resources/application.properties +++ b/backend/src/test/resources/application.properties @@ -5,3 +5,4 @@ spring.jpa.hibernate.ddl-auto=create spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +spring.profiles.active=test From 4bb98bd5f7991bded7eaf5a6d7dd7573ec869da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 18 Jul 2023 14:29:19 +0900 Subject: [PATCH 018/185] =?UTF-8?q?chore:=20=EC=9D=B4=EC=8A=88=20=ED=85=9C?= =?UTF-8?q?=ED=94=8C=EB=A6=BF=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/feature_request.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index d2ae2081..904cf1ac 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -9,3 +9,7 @@ assignees: '' ## 어떤 기능을 구현하나요? - 구현 할 기능을 작성합니다. + +## 일정 + +- 추정 시간 From 8d2a92c6e2cb8d1ddc7de73c2c175af912cc264a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 18 Jul 2023 14:31:02 +0900 Subject: [PATCH 019/185] =?UTF-8?q?chore:=20pr=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index db30585e..5911e03c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,3 +13,8 @@ ## 🎸 기타 - 특이 사항이 있으면 작성합니다. + +## ⏰ 일정 + +- 추정 시간 : +- 걸린 시간 : From 446dba1438eceb87552805c74736db314196d353 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Tue, 18 Jul 2023 15:39:31 +0900 Subject: [PATCH 020/185] =?UTF-8?q?[FE]=20feat:=20Bottom=20Sheet=20Content?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: BottomSheetContent 구현 * feat: sortOptions 상수화 및 타입 정의 * chore: 경로 common 추가 --- .../BottomSheetContent.stories.tsx | 33 ++++++++++ .../BottomSheetContent/BottomSheetContent.tsx | 64 +++++++++++++++++++ frontend/src/constants/index.ts | 1 + 3 files changed, 98 insertions(+) create mode 100644 frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx create mode 100644 frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx create mode 100644 frontend/src/constants/index.ts diff --git a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx new file mode 100644 index 00000000..428edd18 --- /dev/null +++ b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx @@ -0,0 +1,33 @@ +import { BottomSheet } from '@fun-eat/design-system'; +import type { Meta, StoryObj } from '@storybook/react'; +import { useEffect, useRef } from 'react'; + +import BottomSheetContent from './BottomSheetContent'; + +const meta: Meta = { + title: 'common/BottomSheetContent', + component: BottomSheetContent, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => { + const ref = useRef(null); + + useEffect(() => { + ref.current?.showModal(); + }, []); + + const closeBottomSheet = () => { + ref.current?.close(); + }; + + return ( + + + + ); + }, +}; diff --git a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx new file mode 100644 index 00000000..c5e4ed29 --- /dev/null +++ b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx @@ -0,0 +1,64 @@ +import { Button, Divider, theme } from '@fun-eat/design-system'; +import { useState } from 'react'; +import { styled } from 'styled-components'; + +import { SORT_OPTIONS } from '@constants'; + +interface BottomSheetContentProps { + close: () => void; +} + +const BottomSheetContent = ({ close }: BottomSheetContentProps) => { + const [selectedOption, setSelectedOption] = useState(0); + + const handleSelectedOption = (index: number) => { + setSelectedOption(index); + close(); + }; + + return ( + + {SORT_OPTIONS.map((option, index) => { + const isSelected = index === selectedOption; + return ( + <> +
  • + handleSelectedOption(index)} + > + {option} + +
  • + {index < SORT_OPTIONS.length - 1 && } + + ); + })} +
    + ); +}; + +export default BottomSheetContent; + +const BottomSheetContainer = styled.ul` + padding: 20px; +`; + +const SortOption = styled(Button)<{ isSelected: boolean }>` + margin: 20px 0 10px 0; + padding: 0; + border: none; + outline: transparent; + font-weight: ${({ isSelected, theme }) => (isSelected ? theme.fontWeights.bold : 'inherit')}; + cursor: pointer; + + &:hover { + font-weight: ${({ theme }) => theme.fontWeights.bold}; + color: ${({ theme }) => theme.textColors.default}; + transition: all 200ms ease-in; + } +`; diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts new file mode 100644 index 00000000..dcca669a --- /dev/null +++ b/frontend/src/constants/index.ts @@ -0,0 +1 @@ +export const SORT_OPTIONS = ['높은 가격순', '낮은 가격순'] as const; From 84fca3485989b56704d316781c31acb09cf4eb9d Mon Sep 17 00:00:00 2001 From: JFe <33208246+Go-Jaecheol@users.noreply.github.com> Date: Tue, 18 Jul 2023 16:05:39 +0900 Subject: [PATCH 021/185] =?UTF-8?q?[BE]=20feat:=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EC=83=81=ED=92=88=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: git 충돌 오류 해결 * refactor: ProductResponse에 content 필드 추가 * feat: 상품 상세 정보 조회 기능 추가 * test: 리뷰 목록에서 상위 3개 태그 조회하는 repository 테스트 추가 * test: ReviewAcceptanceTest에서 중복 사용 코드 ReviewSteps로 이동 * test: 상품 상세 정보 조회 인수 테스트에서 태그 목록도 확인하도록 수정 --- .../java/com/funeat/member/domain/Member.java | 12 --- .../product/application/ProductService.java | 15 +++- .../funeat/product/dto/ProductResponse.java | 10 ++- .../presentation/ProductController.java | 13 ++- .../persistence/ReviewTagRepository.java | 12 +++ .../product/ProductAcceptanceTest.java | 56 ++++++++++-- .../review/ReviewAcceptanceTest.java | 53 +++-------- .../funeat/acceptance/review/ReviewSteps.java | 44 +++++++++ .../com/funeat/review/TestImageUploader.java | 42 --------- .../persistence/ReviewRepositoryTest.java | 4 + .../persistence/ReviewTagRepositoryTest.java | 90 +++++++++++++++++++ 11 files changed, 241 insertions(+), 110 deletions(-) create mode 100644 backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java delete mode 100644 backend/src/test/java/com/funeat/review/TestImageUploader.java create mode 100644 backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java diff --git a/backend/src/main/java/com/funeat/member/domain/Member.java b/backend/src/main/java/com/funeat/member/domain/Member.java index 3059f4de..53398c2a 100644 --- a/backend/src/main/java/com/funeat/member/domain/Member.java +++ b/backend/src/main/java/com/funeat/member/domain/Member.java @@ -32,18 +32,6 @@ public class Member { private String phoneNumber; - protected Member() { - } - - public Member(final String nickname, final String profileImage, final Integer age, final Gender gender, - final String phoneNumber) { - this.nickname = nickname; - this.profileImage = profileImage; - this.age = age; - this.gender = gender; - this.phoneNumber = phoneNumber; - } - @OneToMany(mappedBy = "member") private List reviewFavorites = new ArrayList<>(); diff --git a/backend/src/main/java/com/funeat/product/application/ProductService.java b/backend/src/main/java/com/funeat/product/application/ProductService.java index ec822605..9df95b72 100644 --- a/backend/src/main/java/com/funeat/product/application/ProductService.java +++ b/backend/src/main/java/com/funeat/product/application/ProductService.java @@ -3,14 +3,18 @@ import com.funeat.product.domain.Category; import com.funeat.product.domain.Product; import com.funeat.product.dto.ProductInCategoryDto; +import com.funeat.product.dto.ProductResponse; import com.funeat.product.dto.ProductsInCategoryPageDto; import com.funeat.product.dto.ProductsInCategoryResponse; import com.funeat.product.persistence.CategoryRepository; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.persistence.ReviewRepository; +import com.funeat.review.persistence.ReviewTagRepository; +import com.funeat.tag.domain.Tag; import java.util.List; import java.util.stream.Collectors; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,12 +26,14 @@ public class ProductService { private final CategoryRepository categoryRepository; private final ProductRepository productRepository; private final ReviewRepository reviewRepository; + private final ReviewTagRepository reviewTagRepository; public ProductService(final CategoryRepository categoryRepository, final ProductRepository productRepository, - final ReviewRepository reviewRepository) { + final ReviewRepository reviewRepository, final ReviewTagRepository reviewTagRepository) { this.categoryRepository = categoryRepository; this.productRepository = productRepository; this.reviewRepository = reviewRepository; + this.reviewTagRepository = reviewTagRepository; } public ProductsInCategoryResponse getAllProductsInCategory(final Long categoryId, @@ -45,4 +51,11 @@ public ProductsInCategoryResponse getAllProductsInCategory(final Long categoryId return new ProductsInCategoryResponse(pageDto, productDtos); } + + public ProductResponse findProductDetail(final Long productId) { + final Product product = productRepository.findById(productId) + .orElseThrow(IllegalArgumentException::new); + final List tags = reviewTagRepository.findTop3TagsByReviewIn(productId, PageRequest.of(0, 3)); + return ProductResponse.toResponse(product, tags); + } } diff --git a/backend/src/main/java/com/funeat/product/dto/ProductResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductResponse.java index 64a857fe..e01eb354 100644 --- a/backend/src/main/java/com/funeat/product/dto/ProductResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/ProductResponse.java @@ -12,15 +12,17 @@ public class ProductResponse { private final String name; private final Long price; private final String image; + private final String content; private final Double averageRating; private final List tags; public ProductResponse(final Long id, final String name, final Long price, final String image, - final Double averageRating, final List tags) { + final String content, final Double averageRating, final List tags) { this.id = id; this.name = name; this.price = price; this.image = image; + this.content = content; this.averageRating = averageRating; this.tags = tags; } @@ -31,7 +33,7 @@ public static ProductResponse toResponse(final Product product, final List tagDtos.add(TagDto.toDto(tag)); } return new ProductResponse(product.getId(), product.getName(), product.getPrice(), product.getImage(), - product.getAverageRating(), tagDtos); + product.getContent(), product.getAverageRating(), tagDtos); } public Long getId() { @@ -50,6 +52,10 @@ public String getImage() { return image; } + public String getContent() { + return content; + } + public Double getAverageRating() { return averageRating; } diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductController.java b/backend/src/main/java/com/funeat/product/presentation/ProductController.java index 442b745f..2afd0e6a 100644 --- a/backend/src/main/java/com/funeat/product/presentation/ProductController.java +++ b/backend/src/main/java/com/funeat/product/presentation/ProductController.java @@ -1,6 +1,7 @@ package com.funeat.product.presentation; import com.funeat.product.application.ProductService; +import com.funeat.product.dto.ProductResponse; import com.funeat.product.dto.ProductsInCategoryResponse; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; @@ -11,7 +12,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/categories") +@RequestMapping("/api") public class ProductController { private final ProductService productService; @@ -20,12 +21,18 @@ public ProductController(final ProductService productService) { this.productService = productService; } - @GetMapping("/{category_id}/products") + @GetMapping("/categories/{category_id}/products") public ResponseEntity getAllProductsInCategory( @PathVariable(name = "category_id") final Long categoryId, @PageableDefault Pageable pageable - ) { + ) { ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); return ResponseEntity.ok(response); } + + @GetMapping("/products/{productId}") + public ResponseEntity getProductDetail(@PathVariable final Long productId) { + final ProductResponse response = productService.findProductDetail(productId); + return ResponseEntity.ok(response); + } } diff --git a/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java b/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java index d69b7c97..7129a711 100644 --- a/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java +++ b/backend/src/main/java/com/funeat/review/persistence/ReviewTagRepository.java @@ -1,7 +1,19 @@ package com.funeat.review.persistence; import com.funeat.review.domain.ReviewTag; +import com.funeat.tag.domain.Tag; +import java.util.List; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface ReviewTagRepository extends JpaRepository { + + @Query("SELECT rt.tag, COUNT(rt.tag) AS cnt " + + "FROM ReviewTag rt " + + "JOIN Review r ON rt.review.id = r.id " + + "WHERE r.product.id = :productId " + + "GROUP BY rt.tag " + + "ORDER BY cnt DESC") + List findTop3TagsByReviewIn(final Long productId, final Pageable pageable); } diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java index c80287c9..812f8c61 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java @@ -9,20 +9,26 @@ import static com.funeat.acceptance.product.ProductSteps.상품_상세_조회_요청; import static com.funeat.acceptance.product.ProductSteps.즉석조리; import static com.funeat.acceptance.product.ProductSteps.카테고리별_상품_목록_조회_요청; +import static com.funeat.acceptance.review.ReviewSteps.리뷰_사진_명세_요청; +import static com.funeat.acceptance.review.ReviewSteps.리뷰_추가_요청; import static org.assertj.core.api.Assertions.assertThat; import com.funeat.acceptance.common.AcceptanceTest; +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; import com.funeat.product.domain.Category; import com.funeat.product.domain.Product; import com.funeat.product.dto.CategoryResponse; import com.funeat.product.dto.ProductInCategoryDto; import com.funeat.product.dto.ProductResponse; import com.funeat.product.dto.ProductsInCategoryPageDto; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.tag.domain.Tag; import io.restassured.common.mapper.TypeRef; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import io.restassured.specification.MultiPartSpecification; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; @@ -46,7 +52,8 @@ class ProductAcceptanceTest extends AcceptanceTest { final Product product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", 간편식사); final Product product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", 간편식사); final Product product11 = new Product("삼각김밥11", 300L, "image.png", "맛있는 삼각김밥11", 간편식사); - final List products = List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10, product11); + final List products = List.of(product1, product2, product3, product4, product5, product6, product7, + product8, product9, product10, product11); 복수_상품_추가_요청(products); // when @@ -55,7 +62,9 @@ class ProductAcceptanceTest extends AcceptanceTest { // then STATUS_CODE를_검증한다(response, 정상_처리); 페이지를_검증한다(response, (long) products.size(), 0L); - 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product11, product8, product1, product4, product3, product6, product7, product2, product5, product10)); + 카테고리별_상품_목록_조회_결과를_검증한다(response, + List.of(product11, product8, product1, product4, product3, product6, product7, product2, product5, + product10)); } @Test @@ -73,17 +82,19 @@ class ProductAcceptanceTest extends AcceptanceTest { final Product product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", 간편식사); final Product product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", 간편식사); final Product product11 = new Product("삼각김밥11", 300L, "image.png", "맛있는 삼각김밥11", 간편식사); - final List products = List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10, product11); + final List products = List.of(product1, product2, product3, product4, product5, product6, product7, + product8, product9, product10, product11); 복수_상품_추가_요청(products); - // when final var response = 카테고리별_상품_목록_조회_요청(categoryId, "price", "desc", 0); // then STATUS_CODE를_검증한다(response, 정상_처리); 페이지를_검증한다(response, (long) products.size(), 0L); - 카테고리별_상품_목록_조회_결과를_검증한다(response, List.of(product9, product10, product5, product2, product7, product6, product3, product4, product1, product8)); + 카테고리별_상품_목록_조회_결과를_검증한다(response, + List.of(product9, product10, product5, product2, product7, product6, product3, product4, product1, + product8)); } @Test @@ -92,13 +103,28 @@ class ProductAcceptanceTest extends AcceptanceTest { 카테고리_추가_요청(간편식사); final Product product = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", 간편식사); final Long productId = 상품_추가_요청(product); + final Long memberId = 멤버_추가_요청(); + final Tag tag1 = 태그_추가_요청(new Tag("1번")); + final Tag tag2 = 태그_추가_요청(new Tag("2번")); + final Tag tag3 = 태그_추가_요청(new Tag("3번")); + final MultiPartSpecification image = 리뷰_사진_명세_요청(); + + final ReviewCreateRequest request1 = new ReviewCreateRequest(4.5, + List.of(tag1.getId(), tag2.getId(), tag3.getId()), "request1", true, memberId); + final ReviewCreateRequest request2 = new ReviewCreateRequest(4.0, List.of(tag2.getId(), tag3.getId()), + "request2", true, memberId); + final ReviewCreateRequest request3 = new ReviewCreateRequest(3.0, List.of(tag2.getId()), "request3", true, + memberId); + 리뷰_추가_요청(productId, image, request1); + 리뷰_추가_요청(productId, image, request2); + 리뷰_추가_요청(productId, image, request3); // when final var response = 상품_상세_조회_요청(productId); // then STATUS_CODE를_검증한다(response, 정상_처리); - 상품_상세_정보_조회_결과를_검증한다(response, product); + 상품_상세_정보_조회_결과를_검증한다(response, product, List.of(tag2, tag3, tag1)); } @Test @@ -129,6 +155,17 @@ class ProductAcceptanceTest extends AcceptanceTest { productRepository.saveAll(products); } + private Tag 태그_추가_요청(final Tag tag) { + return tagRepository.save(tag); + } + + private Long 멤버_추가_요청() { + final Member testMember = memberRepository.save( + new Member("test", "image.png", 27, Gender.FEMALE, "01036551086") + ); + return testMember.getId(); + } + private void 페이지를_검증한다(final ExtractableResponse response, Long dataSize, Long page) { long totalPages = (long) Math.ceil((double) dataSize / PAGE_SIZE); ProductsInCategoryPageDto expected = new ProductsInCategoryPageDto(dataSize, totalPages, @@ -151,8 +188,9 @@ class ProductAcceptanceTest extends AcceptanceTest { .isEqualTo(expected); } - private void 상품_상세_정보_조회_결과를_검증한다(final ExtractableResponse response, final Product product) { - final ProductResponse expected = ProductResponse.toResponse(product, Collections.emptyList()); + private void 상품_상세_정보_조회_결과를_검증한다(final ExtractableResponse response, final Product product, + final List expectedTags) { + final ProductResponse expected = ProductResponse.toResponse(product, expectedTags); final ProductResponse actual = response.as(ProductResponse.class); assertThat(actual).usingRecursiveComparison() diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index 57da0fc2..b6a1af49 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -1,5 +1,15 @@ package com.funeat.acceptance.review; +import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; +import static com.funeat.acceptance.common.CommonSteps.정상_생성; +import static com.funeat.acceptance.common.CommonSteps.정상_처리; +import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; +import static com.funeat.acceptance.review.ReviewSteps.리뷰_사진_명세_요청; +import static com.funeat.acceptance.review.ReviewSteps.리뷰_좋아요_요청; +import static com.funeat.acceptance.review.ReviewSteps.리뷰_추가_요청; +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + import com.funeat.acceptance.common.AcceptanceTest; import com.funeat.member.domain.Gender; import com.funeat.member.domain.Member; @@ -14,22 +24,13 @@ import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.review.presentation.dto.SortingReviewsPageDto; import com.funeat.tag.domain.Tag; -import io.restassured.builder.MultiPartSpecBuilder; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; import io.restassured.specification.MultiPartSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import java.util.List; import java.util.stream.Collectors; - -import static com.funeat.acceptance.common.CommonSteps.STATUS_CODE를_검증한다; -import static com.funeat.acceptance.common.CommonSteps.정상_생성; -import static com.funeat.acceptance.common.CommonSteps.정상_처리; -import static com.funeat.acceptance.common.CommonSteps.정상_처리_NO_CONTENT; -import static io.restassured.RestAssured.given; -import static org.assertj.core.api.Assertions.assertThat; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; @SuppressWarnings("NonAsciiCharacters") class ReviewAcceptanceTest extends AcceptanceTest { @@ -115,36 +116,6 @@ void init() { assertThat(result.getMember().getId()).isEqualTo(memberId); } - private ExtractableResponse 리뷰_추가_요청(final Long productId, final MultiPartSpecification image, - final ReviewCreateRequest request) { - return given() - .multiPart(image) - .multiPart("reviewRequest", request, "application/json") - .when() - .post("/api/products/{productId}/reviews", productId) - .then() - .extract(); - } - - private ExtractableResponse 리뷰_좋아요_요청(final Long productId, final Long reviewId, - final ReviewFavoriteRequest request) { - return given() - .contentType("application/json") - .body(request) - .when() - .patch("/api/products/{productId}/reviews/{reviewId}", productId, reviewId) - .then() - .extract(); - } - - private MultiPartSpecification 리뷰_사진_명세_요청() { - return new MultiPartSpecBuilder("image".getBytes()) - .fileName("testImage.png") - .controlName("image") - .mimeType("image/png") - .build(); - } - private List 태그_추가_요청() { final Tag testTag1 = tagRepository.save(new Tag("testTag1")); final Tag testTag2 = tagRepository.save(new Tag("testTag2")); diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java new file mode 100644 index 00000000..fbff6d54 --- /dev/null +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewSteps.java @@ -0,0 +1,44 @@ +package com.funeat.acceptance.review; + +import static io.restassured.RestAssured.given; + +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; +import io.restassured.builder.MultiPartSpecBuilder; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import io.restassured.specification.MultiPartSpecification; + +@SuppressWarnings("NonAsciiCharacters") +public class ReviewSteps { + + public static ExtractableResponse 리뷰_추가_요청(final Long productId, final MultiPartSpecification image, + final ReviewCreateRequest request) { + return given() + .multiPart(image) + .multiPart("reviewRequest", request, "application/json") + .when() + .post("/api/products/{productId}/reviews", productId) + .then() + .extract(); + } + + public static ExtractableResponse 리뷰_좋아요_요청(final Long productId, final Long reviewId, + final ReviewFavoriteRequest request) { + return given() + .contentType("application/json") + .body(request) + .when() + .patch("/api/products/{productId}/reviews/{reviewId}", productId, reviewId) + .then() + .extract(); + } + + public static MultiPartSpecification 리뷰_사진_명세_요청() { + return new MultiPartSpecBuilder("image".getBytes()) + .fileName("testImage.png") + .controlName("image") + .mimeType("image/png") + .build(); + } +} diff --git a/backend/src/test/java/com/funeat/review/TestImageUploader.java b/backend/src/test/java/com/funeat/review/TestImageUploader.java deleted file mode 100644 index 7d6a3587..00000000 --- a/backend/src/test/java/com/funeat/review/TestImageUploader.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.funeat.review; - -import com.funeat.review.application.ImageService; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Comparator; -import java.util.stream.Stream; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -@Component -@Profile("test") -public class TestImageUploader implements ImageService { - - @Override - public void upload(final MultipartFile image) { - // 실제로 IO 작업을 수행하는 대신, 임시 디렉토리로 복사하도록 수정 - try { - final String temporaryPath = String.valueOf(System.currentTimeMillis()); - final Path tempDirectoryPath = Files.createTempDirectory(temporaryPath); - final Path filePath = tempDirectoryPath.resolve(image.getOriginalFilename()); - Files.copy(image.getInputStream(), filePath); - - deleteDirectory(tempDirectoryPath); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void deleteDirectory(Path directory) throws IOException { - // 디렉토리 내부 파일 및 디렉토리 삭제 - try (Stream pathStream = Files.walk(directory)) { - pathStream.sorted(Comparator.reverseOrder()) - .map(Path::toFile) - .forEach(File::delete); - } - Files.deleteIfExists(directory); - } -} diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java index 5f1d98a2..7b86bb3c 100644 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java @@ -13,6 +13,7 @@ import com.funeat.product.persistence.CategoryRepository; import com.funeat.product.persistence.ProductRepository; import com.funeat.review.domain.Review; +import java.util.List; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; @@ -20,6 +21,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; @DataJpaTest @Import(DataCleaner.class) @@ -56,6 +59,7 @@ class ReviewRepositoryTest { // when assertThat(reviewRepository.countByProduct(product1)).isEqualTo(3); assertThat(reviewRepository.countByProduct(product2)).isEqualTo(1); + } @Test void 특정_상품에_대한_좋아요_기준_내림차순으로_정렬한다() { diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java new file mode 100644 index 00000000..f8b70102 --- /dev/null +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java @@ -0,0 +1,90 @@ +package com.funeat.review.persistence; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.funeat.common.DataCleaner; +import com.funeat.common.DataClearExtension; +import com.funeat.member.domain.Gender; +import com.funeat.member.domain.Member; +import com.funeat.member.persistence.MemberRepository; +import com.funeat.product.domain.Product; +import com.funeat.product.persistence.ProductRepository; +import com.funeat.review.domain.Review; +import com.funeat.review.domain.ReviewTag; +import com.funeat.tag.domain.Tag; +import com.funeat.tag.persistence.TagRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; +import org.springframework.data.domain.PageRequest; + +@DataJpaTest +@Import(DataCleaner.class) +@ExtendWith(DataClearExtension.class) +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class ReviewTagRepositoryTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ReviewTagRepository reviewTagRepository; + + @Autowired + private TagRepository tagRepository; + + @Autowired + private ReviewRepository reviewRepository; + + @Autowired + private ProductRepository productRepository; + + @Test + void 리뷰_목록에서_상위_3개에_해당하는_태그를_조회한다() { + // given + final var member = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + memberRepository.save(member); + + final var product = new Product("망고", 1_000L, "mango.png", "망고망고", null); + productRepository.save(product); + + final var tag1 = new Tag("1번"); + final var tag2 = new Tag("2번"); + final var tag3 = new Tag("3번"); + final var tag4 = new Tag("4번"); + tagRepository.saveAll(List.of(tag1, tag2, tag3, tag4)); + + final var review1 = new Review(member, product, "review1.png", 5.0, "최고의 망고", true, 25L); + final var review2 = new Review(member, product, "review2.png", 3.0, "그럭저럭 망고", false, 10L); + reviewRepository.saveAll(List.of(review1, review2)); + + final var reviewTag1 = ReviewTag.createReviewTag(review1, tag1); + final var reviewTag2 = ReviewTag.createReviewTag(review1, tag2); + final var reviewTag3 = ReviewTag.createReviewTag(review2, tag2); + final var reviewTag4 = ReviewTag.createReviewTag(review2, tag3); + final var reviewTag5 = ReviewTag.createReviewTag(review2, tag3); + final var reviewTag6 = ReviewTag.createReviewTag(review2, tag1); + final var reviewTag7 = ReviewTag.createReviewTag(review2, tag4); + final var reviewTag8 = ReviewTag.createReviewTag(review2, tag2); + final var reviewTag9 = ReviewTag.createReviewTag(review2, tag2); + final var reviewTag10 = ReviewTag.createReviewTag(review2, tag1); + reviewTagRepository.saveAll( + List.of(reviewTag1, reviewTag2, reviewTag3, reviewTag4, reviewTag5, reviewTag6, reviewTag7, reviewTag8, + reviewTag9, reviewTag10) + ); + + // when + final List tags = reviewTagRepository.findTop3TagsByReviewIn(product.getId(), PageRequest.of(0, 3)); + + // then + assertThat(tags).hasSize(3); + assertThat(tags).usingRecursiveFieldByFieldElementComparator() + .containsExactly(tag2, tag1, tag3); + } +} From 8b68d0c7ce887c10f073e59b4dd7bf7fb74d104a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Tue, 18 Jul 2023 16:41:31 +0900 Subject: [PATCH 022/185] =?UTF-8?q?[FE]=20feat:=20SortButton=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: BottomSheetContent 구현 * feat: sortOptions 상수화 및 타입 정의 * feat: sortButton 구현 * feat: 상위 컴포넌트에서 state 받아오도록 구현 * chore: styled import 중괄호 삭제 * refactor: 필요없는 css 스타일 삭제 * chore: 스토리북 title 수정 * refactor: divider -> border-bottom으로 변경 * chore: BottomSheetContent 이름 SortOptionList로 변경 * refactor: SORT_OPTION 상수화 객체 배열로 수정 * refactor: bottom sheet 커스텀 훅으로 분리 * chore: 잘못 받아온 값 수정 * feat: 정렬 svg 추가 * refactor: 함수 이름 변경 * chore: import 절대 경로로 변경 * refactor: css props로 수정 * style: 주석 제거 --- frontend/.storybook/preview-body.html | 5 ++ .../Common/SortButton/SortButton.stories.tsx | 13 ++++ .../Common/SortButton/SortButton.tsx | 46 +++++++++++++ .../SortOptionList/SortOptionList.stories.tsx | 38 +++++++++++ .../Common/SortOptionList/SortOptionList.tsx | 68 +++++++++++++++++++ .../src/components/Common/Svg/SvgIcon.tsx | 1 + .../src/components/Common/Svg/SvgSprite.tsx | 3 + frontend/src/constants/index.ts | 6 +- frontend/src/hooks/useBottomSheet.ts | 17 +++++ frontend/src/types/common.ts | 1 + 10 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/Common/SortButton/SortButton.stories.tsx create mode 100644 frontend/src/components/Common/SortButton/SortButton.tsx create mode 100644 frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx create mode 100644 frontend/src/components/Common/SortOptionList/SortOptionList.tsx create mode 100644 frontend/src/hooks/useBottomSheet.ts diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index e6026e84..45f26fcc 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -58,5 +58,10 @@ d="M7.5 13.762l-1.088-.99C2.55 9.27 0 6.96 0 4.126A4.085 4.085 0 0 1 4.125 0C5.43 0 6.683.608 7.5 1.567A4.491 4.491 0 0 1 10.875 0 4.085 4.085 0 0 1 15 4.125c0 2.835-2.55 5.145-6.412 8.655l-1.088.982z" /> + + + diff --git a/frontend/src/components/Common/SortButton/SortButton.stories.tsx b/frontend/src/components/Common/SortButton/SortButton.stories.tsx new file mode 100644 index 00000000..dcf579ed --- /dev/null +++ b/frontend/src/components/Common/SortButton/SortButton.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import SortButton from './SortButton'; + +const meta: Meta = { + title: 'common/SortButton', + component: SortButton, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Common/SortButton/SortButton.tsx b/frontend/src/components/Common/SortButton/SortButton.tsx new file mode 100644 index 00000000..1f57a2cb --- /dev/null +++ b/frontend/src/components/Common/SortButton/SortButton.tsx @@ -0,0 +1,46 @@ +import { BottomSheet, Button, Text, theme } from '@fun-eat/design-system'; +import { useState } from 'react'; +import styled from 'styled-components'; + +import BottomSheetContent from '../SortOptionList/SortOptionList'; +import SvgIcon from '../Svg/SvgIcon'; + +import { SORT_OPTIONS } from '@/constants'; +import useBottomSheet from '@/hooks/useBottomSheet'; + +const SortButton = () => { + const { ref, handleOpenBottomSheet, handleCloseBottomSheet } = useBottomSheet(); + const [selectedOption, setSelectedOption] = useState(0); + + const selectSortOption = (optionIndex: number) => { + setSelectedOption(optionIndex); + }; + + return ( + <> + + + + + + ); +}; + +export default SortButton; + +const SortButtonWrapper = styled.div` + display: flex; + align-items: center; + gap: 3px; +`; diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx new file mode 100644 index 00000000..50d35213 --- /dev/null +++ b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx @@ -0,0 +1,38 @@ +import { BottomSheet } from '@fun-eat/design-system'; +import type { Meta, StoryObj } from '@storybook/react'; +import { useEffect, useRef, useState } from 'react'; + +import SortOptionList from './SortOptionList'; + +const meta: Meta = { + title: 'common/SortOptionList', + component: SortOptionList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => { + const ref = useRef(null); + const [selectedOption, setSelectedOption] = useState(0); + + useEffect(() => { + ref.current?.showModal(); + }, []); + + const closeBottomSheet = () => { + ref.current?.close(); + }; + + const handleSortOption = (optionIndex: number) => { + setSelectedOption(optionIndex); + }; + + return ( + + + + ); + }, +}; diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.tsx new file mode 100644 index 00000000..56e3476f --- /dev/null +++ b/frontend/src/components/Common/SortOptionList/SortOptionList.tsx @@ -0,0 +1,68 @@ +import { Button, theme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SORT_OPTIONS } from '@/constants'; + +interface SortOptionListProps { + selectedOption: number; + selectSortOption: (optionIndex: number) => void; + close: () => void; +} + +const SortOptionList = ({ selectedOption, selectSortOption, close }: SortOptionListProps) => { + const handleSelectedOption = (optionIndex: number) => { + selectSortOption(optionIndex); + close(); + }; + + return ( + + {SORT_OPTIONS.map((option, index) => { + const isSelected = index === selectedOption; + const isLastItem = index < SORT_OPTIONS.length - 1; + return ( + + handleSelectedOption(index)} + > + {option.label} + + + ); + })} + + ); +}; + +export default SortOptionList; + +const SortOptionListContainer = styled.ul` + padding: 20px; +`; + +const SortOptionItem = styled.li``; + +const SortOption = styled(Button)` + margin: 20px 0 10px 0; + padding: 0; + border: none; + outline: transparent; + + &:hover { + font-weight: ${({ theme }) => theme.fontWeights.bold}; + color: ${({ theme }) => theme.textColors.default}; + transition: all 200ms ease-in; + } +`; diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx index 1d275dec..ab0f0fab 100644 --- a/frontend/src/components/Common/Svg/SvgIcon.tsx +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -11,6 +11,7 @@ export const SVG_ICON_VARIANTS = [ 'review', 'star', 'favorite', + 'sort', ] as const; type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number]; diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index db81f3fc..0d1f9c82 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -46,6 +46,9 @@ const SvgSprite = () => { + + + ); }; diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index dcca669a..e4c62ae4 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -1 +1,5 @@ -export const SORT_OPTIONS = ['높은 가격순', '낮은 가격순'] as const; +export const SORT_OPTIONS = [ + { label: '높은 가격순', value: 'price,desc' }, + { label: '낮은 가격순', value: 'price,asc' }, +] as const; + diff --git a/frontend/src/hooks/useBottomSheet.ts b/frontend/src/hooks/useBottomSheet.ts new file mode 100644 index 00000000..6d9b1ab3 --- /dev/null +++ b/frontend/src/hooks/useBottomSheet.ts @@ -0,0 +1,17 @@ +import { useRef } from 'react'; + +const useBottomSheet = () => { + const ref = useRef(null); + + const handleOpenBottomSheet = () => { + ref.current?.showModal(); + }; + + const handleCloseBottomSheet = () => { + ref.current?.close(); + }; + + return { ref, handleOpenBottomSheet, handleCloseBottomSheet }; +}; + +export default useBottomSheet; diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index dcb7bf32..6b23dd0b 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -2,3 +2,4 @@ export interface Category { id: number; name: string; } + From 3c374b935908890795989a5c3e5b29678cc4dc0f Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Tue, 18 Jul 2023 16:43:42 +0900 Subject: [PATCH 023/185] =?UTF-8?q?[FE]=20feat:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=20=EC=95=84=EC=9D=B4=ED=85=9C,=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=9E=AD=ED=82=B9=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#5?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: storybook preview에 FunEatProvider 추가 * feat: RankingReview 타입 추가 * feat: RankingReviewItem 컴포넌트 추가 * feat: RankingReview mock 데이터 추가 * feat: RankingReviewList 컴포넌트 추가 * refactor: 구조분해할당으로 리팩토링 * style: 수정된 절대경로 적용 * chore: 디자인 시스템 버전업 * feat: 아이콘 스타일 수정 * feat: 긴 리뷰의 예시로 변경 * refactor: ReviewRanking으로 컴포넌트 네이밍 변경 * feat: 아이콘과 텍스트 사이에 spacing 추가 * feat: ReviewRanking 컴포넌트 export문 추가 --- .../RankingProductItem/RankingProductItem.tsx | 2 +- .../ReviewRankingItem.stories.tsx | 24 ++++++ .../ReviewRankingItem/ReviewRankingItem.tsx | 80 +++++++++++++++++++ .../ReviewRankingList.stories.tsx | 13 +++ .../ReviewRankingList/ReviewRankingList.tsx | 27 +++++++ frontend/src/components/Review/index.ts | 2 + .../src/mocks/data/rankingReviewList.json | 20 +++++ frontend/src/types/ranking.ts | 9 +++ 8 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.stories.tsx create mode 100644 frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx create mode 100644 frontend/src/components/Review/ReviewRankingList/ReviewRankingList.stories.tsx create mode 100644 frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx create mode 100644 frontend/src/components/Review/index.ts create mode 100644 frontend/src/mocks/data/rankingReviewList.json diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.tsx b/frontend/src/components/RankingProductItem/RankingProductItem.tsx index baa2a44a..460c7caa 100644 --- a/frontend/src/components/RankingProductItem/RankingProductItem.tsx +++ b/frontend/src/components/RankingProductItem/RankingProductItem.tsx @@ -1,7 +1,7 @@ import { Text } from '@fun-eat/design-system'; import styled from 'styled-components'; -import type { RankingProduct } from '@/types'; +import type { RankingProduct } from '@/types/ranking'; interface RankingProductItemProps { rankingProduct: RankingProduct; diff --git a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.stories.tsx b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.stories.tsx new file mode 100644 index 00000000..59f7425f --- /dev/null +++ b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ReviewRankingItem from './ReviewRankingItem'; + +const meta: Meta = { + title: 'review/ReviewRankingItem', + component: ReviewRankingItem, + args: { + reviewRanking: { + reviewId: 1, + productId: 5, + productName: '구운감자슬림명란마요', + content: + '할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데요 이것보다 긴 리뷰도 잘려 보인답니다', + rating: 4.0, + favoriteCount: 1256, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx new file mode 100644 index 00000000..993151df --- /dev/null +++ b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx @@ -0,0 +1,80 @@ +import { Spacing, Text, theme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; +import type { ReviewRanking } from '@/types/ranking'; + +interface ReviewRankingItemProps { + reviewRanking: ReviewRanking; +} + +const ReviewRankingItem = ({ reviewRanking }: ReviewRankingItemProps) => { + const { productName, content, rating, favoriteCount } = reviewRanking; + + return ( + + + {productName} + + + {content} + + + + + + + {favoriteCount} + + + + + + {rating.toFixed(1)} + + + + + ); +}; + +export default ReviewRankingItem; + +const ReviewRankingItemContainer = styled.div` + display: flex; + flex-direction: column; + width: 315px; + padding: 12px; + gap: 4px; + border: 1px solid ${({ theme }) => theme.borderColors.disabled}; + border-radius: ${({ theme }) => theme.borderRadius.sm}; +`; + +const ReviewText = styled(Text)` + display: -webkit-inline-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; +`; + +const FavoriteStarWrapper = styled.div` + display: flex; + gap: 4px; +`; + +const FavoriteIconWrapper = styled.div` + display: flex; + align-items: center; + gap: 4px; +`; + +const RatingIconWrapper = styled.div` + display: flex; + align-items: center; + gap: 2px; + + & > svg { + padding-bottom: 2px; + } +`; diff --git a/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.stories.tsx b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.stories.tsx new file mode 100644 index 00000000..671dd89e --- /dev/null +++ b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ReviewRankingList from './ReviewRankingList'; + +const meta: Meta = { + title: 'review/ReviewRankingList', + component: ReviewRankingList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx new file mode 100644 index 00000000..97de1c0e --- /dev/null +++ b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx @@ -0,0 +1,27 @@ +import styled from 'styled-components'; + +import ReviewRankingItem from '../ReviewRankingItem/ReviewRankingItem'; + +import reviewRankingList from '@/mocks/data/rankingReviewList.json'; + +const ReviewRankingList = () => { + const { reviews } = reviewRankingList; + + return ( + + {reviews.map((reviewRanking) => ( +
  • + +
  • + ))} +
    + ); +}; + +export default ReviewRankingList; + +const ReviewRankingListContainer = styled.ul` + display: flex; + flex-direction: column; + gap: 20px; +`; diff --git a/frontend/src/components/Review/index.ts b/frontend/src/components/Review/index.ts new file mode 100644 index 00000000..01f661af --- /dev/null +++ b/frontend/src/components/Review/index.ts @@ -0,0 +1,2 @@ +export { default as ReviewRankingItem } from './ReviewRankingItem/ReviewRankingItem'; +export { default as ReviewRankingList } from './ReviewRankingList/ReviewRankingList'; diff --git a/frontend/src/mocks/data/rankingReviewList.json b/frontend/src/mocks/data/rankingReviewList.json new file mode 100644 index 00000000..1c7fa137 --- /dev/null +++ b/frontend/src/mocks/data/rankingReviewList.json @@ -0,0 +1,20 @@ +{ + "reviews": [ + { + "reviewId": 1, + "productId": 5, + "productName": "구운감자슬림명란마요", + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데요 이것보다 긴 리뷰도 잘려 보인답니다", + "rating": 4.0, + "favoriteCount": 1256 + }, + { + "reviewId": 2, + "productId": 3, + "productName": "하얀짜파게티큰사발", + "content": "하얀 짜파게티라니 말이 안된다고 생각했었죠. 실제로 맛을 보니까 까만 짜파게티랑 맛이 뭔가 다를게 없네요.", + "rating": 4.4, + "favoriteCount": 870 + } + ] +} diff --git a/frontend/src/types/ranking.ts b/frontend/src/types/ranking.ts index 954d341f..a0501fef 100644 --- a/frontend/src/types/ranking.ts +++ b/frontend/src/types/ranking.ts @@ -4,3 +4,12 @@ export interface RankingProduct { name: string; image: string; } + +export interface ReviewRanking { + reviewId: number; + productId: number; + productName: string; + content: string; + rating: number; + favoriteCount: number; +} From be574c0b5e491345b27ad8f85fd3fd541f008ccd Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Tue, 18 Jul 2023 17:17:10 +0900 Subject: [PATCH 024/185] =?UTF-8?q?[FE]=20feat:=20NavigationBar=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#5?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: NavigationBar 컴포넌트 추가 * feat: 리스트 아이콘 수정 * feat: 네비게이션바 디자인 수정 * feat: 네비게이션바 border-top에 border radius 추가 * chore: 디자인시스템 버전업 * refactor: navigation menu 배열 상수화 및 nav, ul태그 적용 * feat: 클릭했을 때 메뉴 색상 바뀌는 기능 추가 * feat: padding-top 추가 --- frontend/.storybook/preview-body.html | 7 ++- frontend/package.json | 2 +- .../NavigationBar/NavigationBar.stories.tsx | 19 ++++++ .../Common/NavigationBar/NavigationBar.tsx | 58 +++++++++++++++++++ .../src/components/Common/Svg/SvgIcon.tsx | 3 +- .../src/components/Common/Svg/SvgSprite.tsx | 4 +- frontend/src/constants/index.ts | 31 +++++++++- frontend/src/types/common.ts | 7 +++ frontend/yarn.lock | 8 +-- 9 files changed, 128 insertions(+), 11 deletions(-) create mode 100644 frontend/src/components/Common/NavigationBar/NavigationBar.stories.tsx create mode 100644 frontend/src/components/Common/NavigationBar/NavigationBar.tsx diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index 45f26fcc..012fa5ef 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -6,8 +6,8 @@ d="M20.675 12.44c-.324-.604-.643-1.123-.868-1.533a5.058 5.058 0 0 1-.256-.516.808.808 0 0 1-.068-.244v-.005.001a.311.311 0 0 1 .054-.168.344.344 0 0 1 .116-.096.83.83 0 0 1 .135-.06l.096.045.25-.053c.53-.111.904-.529.904-1.011V7.733c0-.813-.743-1.47-1.662-1.471h-2.668c-.23-.425-.717-.72-1.284-.72h-4.035c-.567 0-1.053.296-1.284.72H8.23c-.203-.737-.762-2.216-2.261-4.017A8.064 8.064 0 0 0 4.489.91 5.94 5.94 0 0 0 3.376.287 3.487 3.487 0 0 0 2.051 0 2.454 2.454 0 0 0 .86.297a1.61 1.61 0 0 0-.664.677A1.779 1.779 0 0 0 0 1.801c0 .34.086.68.259.998.172.316.436.61.799.825.818.483 1.589 1.076 2.155 1.67.32.332.564.665.73.968h-.019c-.917 0-1.66.658-1.66 1.47v1.064c0 .487.381.907.915 1.014l.267.053.076-.04a.78.78 0 0 1 .112.049.363.363 0 0 1 .128.1.31.31 0 0 1 .057.173v.001a.77.77 0 0 1-.068.245c-.095.239-.296.596-.544 1.022-.742 1.285-1.904 3.225-1.905 5.645 0 .157.004.316.015.477.13 2.033.929 4.141 2.613 5.762C5.61 24.92 8.183 26.006 11.65 26c3.468.006 6.04-1.08 7.722-2.703 1.684-1.62 2.482-3.73 2.614-5.762.01-.161.014-.32.014-.477 0-1.844-.677-3.409-1.325-4.618zm-1.012-4.707v.866c-.221.026-.471.1-.733.245-.048.026-.093.064-.14.096h-1.937V7.48h2.523c.159 0 .287.113.287.253zM1.82 2.61a.886.886 0 0 1-.324-.342.991.991 0 0 1-.122-.468.7.7 0 0 1 .07-.318.346.346 0 0 1 .14-.151.938.938 0 0 1 .466-.115c.206 0 .464.057.742.173.417.171.864.469 1.238.775.374.304.68.62.831.802.97 1.167 1.5 2.17 1.786 2.87.058.142.104.269.144.385H5.405c-.216-.6-.628-1.168-1.142-1.712-.67-.702-1.525-1.356-2.442-1.899zm1.818 5.986v-.864c0-.14.128-.253.285-.254H9.96v1.46H4.512c-.062-.041-.124-.088-.188-.12a2.03 2.03 0 0 0-.685-.222zm16.974 8.869c-.115 1.804-.83 3.64-2.253 5.006-1.425 1.363-3.554 2.305-6.71 2.31-3.154-.005-5.283-.947-6.709-2.31-1.423-1.365-2.137-3.202-2.251-5.006a6.281 6.281 0 0 1-.014-.408c0-1.575.582-2.958 1.193-4.096.305-.57.615-1.074.862-1.521.123-.225.231-.435.315-.643.082-.208.146-.414.147-.652l-.002-.083a1.497 1.497 0 0 0-.06-.312h4.83v2.925c0 .531.49.962 1.09.962.6 0 1.091-.43 1.091-.962v-.621c0-.531.49-.966 1.09-.966.602 0 1.092.435 1.092.966v1.838c0 .617.564 1.12 1.265 1.12.696 0 1.265-.503 1.265-1.12V9.751h1.318c-.03.1-.052.203-.06.31l-.003.085c.002.239.065.444.148.652.147.362.367.74.616 1.171.749 1.286 1.756 3.022 1.753 5.09 0 .134-.003.27-.012.407z" /> - - + + + + + = { + title: 'common/NavigationBar', + component: NavigationBar, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( +
    + +
    + ), +}; diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx new file mode 100644 index 00000000..50dd7c3c --- /dev/null +++ b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx @@ -0,0 +1,58 @@ +import { Text, theme } from '@fun-eat/design-system'; +import { useState } from 'react'; +import styled from 'styled-components'; + +import SvgIcon from '../Svg/SvgIcon'; + +import { NAVIGATION_MENU } from '@/constants'; + +const NavigationBar = () => { + const [selectedMenu, setSelectedMenu] = useState('홈'); + + const navigateMenu = (name: string) => { + setSelectedMenu(name); + }; + + return ( + + ); +}; + +export default NavigationBar; + +const NavigationBarContainer = styled.ul` + display: flex; + align-items: center; + justify-content: space-around; + width: 100%; + height: 62px; + padding-top: 12px; + border: 1px solid ${({ theme }) => theme.borderColors.disabled}; + border-bottom: none; + border-top-right-radius: 20px; + border-top-left-radius: 20px; +`; + +const NavigationItem = styled.li` + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-end; + gap: 8px; + height: 50px; + cursor: pointer; +`; diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx index ab0f0fab..7ebb6760 100644 --- a/frontend/src/components/Common/Svg/SvgIcon.tsx +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -11,9 +11,10 @@ export const SVG_ICON_VARIANTS = [ 'review', 'star', 'favorite', + 'home', 'sort', ] as const; -type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number]; +export type SvgIconVariant = (typeof SVG_ICON_VARIANTS)[number]; interface SvgIconProps extends ComponentPropsWithoutRef<'svg'> { /** diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index 0d1f9c82..98b3aed7 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -4,8 +4,8 @@ const SvgSprite = () => { - - + + diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index e4c62ae4..0ec89b03 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -1,5 +1,34 @@ +import type { NavigationMenu } from '@/types/common'; + +export const NAVIGATION_MENU: NavigationMenu[] = [ + { + variant: 'search', + name: '검색', + path: '', + }, + { + variant: 'list', + name: '목록', + path: '', + }, + { + variant: 'home', + name: '홈', + path: '', + }, + { + variant: 'recipe', + name: '꿀조합', + path: '', + }, + { + variant: 'profile', + name: '마이', + path: '', + }, +]; + export const SORT_OPTIONS = [ { label: '높은 가격순', value: 'price,desc' }, { label: '낮은 가격순', value: 'price,asc' }, ] as const; - diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 6b23dd0b..4d951852 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -1,5 +1,12 @@ +import type { SvgIconVariant } from '@/components/Common/Svg/SvgIcon'; + export interface Category { id: number; name: string; } +export interface NavigationMenu { + variant: SvgIconVariant; + name: '검색' | '목록' | '홈' | '꿀조합' | '마이'; + path: string; +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index bdba20c6..9e98c659 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1558,10 +1558,10 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== -"@fun-eat/design-system@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.2.0.tgz#f5547889dcc8bdb51f3c7ad6b564a51b4f8815c6" - integrity sha512-wXnWEO+k4Kjf98uCfABhIr10Zhckjk0UetdJz2UsWDILG0/GSq/AGxIrQ/KpFRwJXqrmXPE6ZLM77nGvUumMhA== +"@fun-eat/design-system@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.2.1.tgz#56e2a581adc773cb09bd85fb9f381e959dea14da" + integrity sha512-1bLET1X8s0yfsl4OsSzsQjJ0lWLPdNS2ZelGgsxN/X9BNrqL7w5x0jG0QSjz3OmU00WDzH0psVSUflwmugviNw== "@humanwhocodes/config-array@^0.11.10": version "0.11.10" From 8ebc511acb57180baa817791b8b1adb5688872af Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Tue, 18 Jul 2023 17:22:09 +0900 Subject: [PATCH 025/185] =?UTF-8?q?[FE]=20feat:=20ProductDetail,=20TagList?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 스토리북, 타입스크립트, 린트, 패키지 설정 * feat: ProductDetail 컴포넌트 추가 * feat: ProductDetail 스토리 작성 * feat: 상품 상세 타입 추가 * refactor: TagList 컴포넌트 분리 * feat: TagList 컴포넌트 스토리 작성 * feat: img alt 속성 추가 --- .../Common/TagList/TagList.stories.tsx | 18 +++++ .../src/components/Common/TagList/TagList.tsx | 29 ++++++++ .../ProductDetail/ProductDetail.stories.tsx | 19 +++++ .../Product/ProductDetail/ProductDetail.tsx | 74 +++++++++++++++++++ frontend/src/mocks/data/productDetail.json | 22 ++++++ frontend/src/types/common.ts | 5 ++ frontend/src/types/product.ts | 12 +++ 7 files changed, 179 insertions(+) create mode 100644 frontend/src/components/Common/TagList/TagList.stories.tsx create mode 100644 frontend/src/components/Common/TagList/TagList.tsx create mode 100644 frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx create mode 100644 frontend/src/components/Product/ProductDetail/ProductDetail.tsx create mode 100644 frontend/src/mocks/data/productDetail.json diff --git a/frontend/src/components/Common/TagList/TagList.stories.tsx b/frontend/src/components/Common/TagList/TagList.stories.tsx new file mode 100644 index 00000000..b78050bf --- /dev/null +++ b/frontend/src/components/Common/TagList/TagList.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import TagList from './TagList'; + +import productDetail from '@/mocks/data/productDetail.json'; + +const meta: Meta = { + title: 'common/TagList', + component: TagList, + args: { + tags: productDetail.tags, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Common/TagList/TagList.tsx b/frontend/src/components/Common/TagList/TagList.tsx new file mode 100644 index 00000000..192fb210 --- /dev/null +++ b/frontend/src/components/Common/TagList/TagList.tsx @@ -0,0 +1,29 @@ +import { Badge } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import type { Tag } from '@/types/common'; + +interface TagListProps { + tags: Tag[]; +} + +const TagList = ({ tags }: TagListProps) => { + return ( + + {tags.map((tag) => ( +
  • + + {tag.name} + +
  • + ))} +
    + ); +}; + +export default TagList; + +const TagListContainer = styled.ul` + display: flex; + column-gap: 8px; +`; diff --git a/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx b/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx new file mode 100644 index 00000000..ccc4f767 --- /dev/null +++ b/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductDetail from './ProductDetail'; + +const meta: Meta = { + title: 'product/ProductDetail', + component: ProductDetail, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( +
    + +
    + ), +}; diff --git a/frontend/src/components/Product/ProductDetail/ProductDetail.tsx b/frontend/src/components/Product/ProductDetail/ProductDetail.tsx new file mode 100644 index 00000000..0672f934 --- /dev/null +++ b/frontend/src/components/Product/ProductDetail/ProductDetail.tsx @@ -0,0 +1,74 @@ +import { Text } from '@fun-eat/design-system'; +import styled, { useTheme } from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; +import TagList from '@/components/Common/TagList/TagList'; +import productDetail from '@/mocks/data/productDetail.json'; + +const ProductDetail = () => { + const { name, price, image, content, averageRating, tags } = productDetail; + const theme = useTheme(); + + return ( + + {name} + + + 가격 + {price.toLocaleString('ko-KR')}원 + + + 상품 설명 + {content} + + + 평균 평점 + + + {averageRating} + + + + + + ); +}; + +export default ProductDetail; + +const ProductDetailContainer = styled.div` + display: flex; + flex-direction: column; + row-gap: 30px; + + & > img { + align-self: center; + } +`; + +const DetailInfoWrapper = styled.div` + & > div + div { + margin-top: 10px; + } +`; + +const DescriptionWrapper = styled.div` + display: flex; + column-gap: 20px; + + & > p:first-of-type { + flex-shrink: 0; + width: 60px; + } +`; + +const RatingIconWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 4px; + margin-left: -4px; + + & > svg { + padding-bottom: 2px; + } +`; diff --git a/frontend/src/mocks/data/productDetail.json b/frontend/src/mocks/data/productDetail.json new file mode 100644 index 00000000..50864299 --- /dev/null +++ b/frontend/src/mocks/data/productDetail.json @@ -0,0 +1,22 @@ +{ + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "averageRating": 4.5, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + }, + { + "id": 3, + "name": "맵찔이는 안돼요" + } + ] +} diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 4d951852..acb811b2 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -5,6 +5,11 @@ export interface Category { name: string; } +export interface Tag { + id: number; + name: string; +} + export interface NavigationMenu { variant: SvgIconVariant; name: '검색' | '목록' | '홈' | '꿀조합' | '마이'; diff --git a/frontend/src/types/product.ts b/frontend/src/types/product.ts index d3cb3ef0..2bbc5854 100644 --- a/frontend/src/types/product.ts +++ b/frontend/src/types/product.ts @@ -1,3 +1,5 @@ +import type { Tag } from './common'; + export interface Product { id: number; name: string; @@ -6,3 +8,13 @@ export interface Product { averageRating: number; reviewCount: number; } + +export interface ProductDetail { + id: number; + name: string; + price: number; + image: string; + content: string; + averageRating: number; + tags: Tag[]; +} From 791ad4416b5469080a7a2d06b357a33ee5c09e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Tue, 18 Jul 2023 17:22:40 +0900 Subject: [PATCH 026/185] =?UTF-8?q?[FE]=20feat:=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 디자인 시스템 버전 업 * feat: Title 컴포넌트 구현 * style: 필요없는 스타일 삭제 --- .../components/Common/Title/Title.stories.tsx | 16 +++++++++ .../src/components/Common/Title/Title.tsx | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 frontend/src/components/Common/Title/Title.stories.tsx create mode 100644 frontend/src/components/Common/Title/Title.tsx diff --git a/frontend/src/components/Common/Title/Title.stories.tsx b/frontend/src/components/Common/Title/Title.stories.tsx new file mode 100644 index 00000000..9f455c1a --- /dev/null +++ b/frontend/src/components/Common/Title/Title.stories.tsx @@ -0,0 +1,16 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import Title from './Title'; + +const meta: Meta = { + title: 'common/Title', + component: Title, + args: { + headingTitle: '상품 목록', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Common/Title/Title.tsx b/frontend/src/components/Common/Title/Title.tsx new file mode 100644 index 00000000..dab9baf2 --- /dev/null +++ b/frontend/src/components/Common/Title/Title.tsx @@ -0,0 +1,36 @@ +import { Text, theme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import SvgIcon from '../Svg/SvgIcon'; + +interface TitleProps { + headingTitle: string; +} + +const Title = ({ headingTitle }: TitleProps) => { + return ( + + + + + + {headingTitle} + + + ); +}; + +export default Title; + +const TitleContainer = styled.h1` + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + position: relative; +`; + +const SvgIconWrapper = styled.button` + position: absolute; + left: 0; +`; From 9819090cf31e74e9b064bd3e2a609c88828e92d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Wed, 19 Jul 2023 10:21:08 +0900 Subject: [PATCH 027/185] =?UTF-8?q?[FE]=20refactor:=20RankingProduct=20->?= =?UTF-8?q?=20ProductRanking=20=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(?= =?UTF-8?q?#73)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: rankingProduct -> productRanking으로 이름 변경 * refactor: BottomSheetContent 삭제 * refactor: rankingReview -> reviewRanking으로 변경 --- .../BottomSheetContent.stories.tsx | 33 ---------- .../BottomSheetContent/BottomSheetContent.tsx | 64 ------------------- .../ProductRankingItem.stories.tsx} | 10 +-- .../ProductRankingItem.tsx} | 22 +++---- .../ProductRankingList.stories.tsx | 13 ++++ .../ProductRankingList/ProductRankingList.tsx | 16 +++++ .../RankingProductList.stories.tsx | 13 ---- .../RankingProductList/RankingProductList.tsx | 16 ----- .../ReviewRankingList/ReviewRankingList.tsx | 2 +- ...nkingProducts.json => productRanking.json} | 0 ...ReviewList.json => reviewRankingList.json} | 0 frontend/src/types/ranking.ts | 2 +- 12 files changed, 47 insertions(+), 144 deletions(-) delete mode 100644 frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx delete mode 100644 frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx rename frontend/src/components/{RankingProductItem/RankingProductItem.stories.tsx => ProductRankingItem/ProductRankingItem.stories.tsx} (62%) rename frontend/src/components/{RankingProductItem/RankingProductItem.tsx => ProductRankingItem/ProductRankingItem.tsx} (55%) create mode 100644 frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx create mode 100644 frontend/src/components/ProductRankingList/ProductRankingList.tsx delete mode 100644 frontend/src/components/RankingProductList/RankingProductList.stories.tsx delete mode 100644 frontend/src/components/RankingProductList/RankingProductList.tsx rename frontend/src/mocks/data/{rankingProducts.json => productRanking.json} (100%) rename frontend/src/mocks/data/{rankingReviewList.json => reviewRankingList.json} (100%) diff --git a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx deleted file mode 100644 index 428edd18..00000000 --- a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.stories.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { BottomSheet } from '@fun-eat/design-system'; -import type { Meta, StoryObj } from '@storybook/react'; -import { useEffect, useRef } from 'react'; - -import BottomSheetContent from './BottomSheetContent'; - -const meta: Meta = { - title: 'common/BottomSheetContent', - component: BottomSheetContent, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => { - const ref = useRef(null); - - useEffect(() => { - ref.current?.showModal(); - }, []); - - const closeBottomSheet = () => { - ref.current?.close(); - }; - - return ( - - - - ); - }, -}; diff --git a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx b/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx deleted file mode 100644 index c5e4ed29..00000000 --- a/frontend/src/components/Common/BottomSheetContent/BottomSheetContent.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { Button, Divider, theme } from '@fun-eat/design-system'; -import { useState } from 'react'; -import { styled } from 'styled-components'; - -import { SORT_OPTIONS } from '@constants'; - -interface BottomSheetContentProps { - close: () => void; -} - -const BottomSheetContent = ({ close }: BottomSheetContentProps) => { - const [selectedOption, setSelectedOption] = useState(0); - - const handleSelectedOption = (index: number) => { - setSelectedOption(index); - close(); - }; - - return ( - - {SORT_OPTIONS.map((option, index) => { - const isSelected = index === selectedOption; - return ( - <> -
  • - handleSelectedOption(index)} - > - {option} - -
  • - {index < SORT_OPTIONS.length - 1 && } - - ); - })} -
    - ); -}; - -export default BottomSheetContent; - -const BottomSheetContainer = styled.ul` - padding: 20px; -`; - -const SortOption = styled(Button)<{ isSelected: boolean }>` - margin: 20px 0 10px 0; - padding: 0; - border: none; - outline: transparent; - font-weight: ${({ isSelected, theme }) => (isSelected ? theme.fontWeights.bold : 'inherit')}; - cursor: pointer; - - &:hover { - font-weight: ${({ theme }) => theme.fontWeights.bold}; - color: ${({ theme }) => theme.textColors.default}; - transition: all 200ms ease-in; - } -`; diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx b/frontend/src/components/ProductRankingItem/ProductRankingItem.stories.tsx similarity index 62% rename from frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx rename to frontend/src/components/ProductRankingItem/ProductRankingItem.stories.tsx index 28188914..d48cee81 100644 --- a/frontend/src/components/RankingProductItem/RankingProductItem.stories.tsx +++ b/frontend/src/components/ProductRankingItem/ProductRankingItem.stories.tsx @@ -1,12 +1,12 @@ import type { Meta, StoryObj } from '@storybook/react'; -import RankingProductItem from './RankingProductItem'; +import ProductRankingItem from './ProductRankingItem'; -const meta: Meta = { - title: 'product/RankingProductItem', - component: RankingProductItem, +const meta: Meta = { + title: 'product/ProductRankingItem', + component: ProductRankingItem, args: { - rankingProduct: { + productRanking: { id: 3, rank: 1, image: 'https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg', diff --git a/frontend/src/components/RankingProductItem/RankingProductItem.tsx b/frontend/src/components/ProductRankingItem/ProductRankingItem.tsx similarity index 55% rename from frontend/src/components/RankingProductItem/RankingProductItem.tsx rename to frontend/src/components/ProductRankingItem/ProductRankingItem.tsx index 460c7caa..9eb108f0 100644 --- a/frontend/src/components/RankingProductItem/RankingProductItem.tsx +++ b/frontend/src/components/ProductRankingItem/ProductRankingItem.tsx @@ -1,31 +1,31 @@ import { Text } from '@fun-eat/design-system'; import styled from 'styled-components'; -import type { RankingProduct } from '@/types/ranking'; +import type { ProductRanking } from '@/types/ranking'; -interface RankingProductItemProps { - rankingProduct: RankingProduct; +interface ProductRankingItemProps { + productRanking: ProductRanking; } -const RankingProductItem = ({ rankingProduct }: RankingProductItemProps) => { - const { rank, image, name } = rankingProduct; +const ProductRankingItem = ({ productRanking }: ProductRankingItemProps) => { + const { rank, image, name } = productRanking; return ( - + {rank} - + {name} - + ); }; -export default RankingProductItem; +export default ProductRankingItem; -const RankingProductContainer = styled.div` +const ProductRankingContainer = styled.div` display: flex; align-items: center; width: 300px; @@ -37,7 +37,7 @@ const RankingProductContainer = styled.div` background: ${({ theme }) => theme.colors.gray1}; `; -const RankingProductImage = styled.img` +const ProductRankingImage = styled.img` width: 45px; height: 45px; border-radius: 50%; diff --git a/frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx b/frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx new file mode 100644 index 00000000..68b955e5 --- /dev/null +++ b/frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductRankingList from './ProductRankingList'; + +const meta: Meta = { + title: 'product/ProductRankingList', + component: ProductRankingList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/ProductRankingList/ProductRankingList.tsx b/frontend/src/components/ProductRankingList/ProductRankingList.tsx new file mode 100644 index 00000000..c8fc8650 --- /dev/null +++ b/frontend/src/components/ProductRankingList/ProductRankingList.tsx @@ -0,0 +1,16 @@ +import productRanking from '../../mocks/data/productRanking.json'; +import ProductRankingItem from '../ProductRankingItem/ProductRankingItem'; + +const ProductRankingList = () => { + return ( +
      + {productRanking.map((productRanking) => ( +
    • + +
    • + ))} +
    + ); +}; + +export default ProductRankingList; diff --git a/frontend/src/components/RankingProductList/RankingProductList.stories.tsx b/frontend/src/components/RankingProductList/RankingProductList.stories.tsx deleted file mode 100644 index 4c8d9544..00000000 --- a/frontend/src/components/RankingProductList/RankingProductList.stories.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import RankingProductList from './RankingProductList'; - -const meta: Meta = { - title: 'product/RankingProductList', - component: RankingProductList, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/frontend/src/components/RankingProductList/RankingProductList.tsx b/frontend/src/components/RankingProductList/RankingProductList.tsx deleted file mode 100644 index 8c551114..00000000 --- a/frontend/src/components/RankingProductList/RankingProductList.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import RankingProductItem from '@/components/RankingProductItem/RankingProductItem'; -import rankingProducts from '@/mocks/data/rankingProducts.json'; - -const RankingProductList = () => { - return ( -
      - {rankingProducts.map((rankingProduct) => ( -
    • - -
    • - ))} -
    - ); -}; - -export default RankingProductList; diff --git a/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx index 97de1c0e..e5ff4a7b 100644 --- a/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx +++ b/frontend/src/components/Review/ReviewRankingList/ReviewRankingList.tsx @@ -2,7 +2,7 @@ import styled from 'styled-components'; import ReviewRankingItem from '../ReviewRankingItem/ReviewRankingItem'; -import reviewRankingList from '@/mocks/data/rankingReviewList.json'; +import reviewRankingList from '@/mocks/data/reviewRankingList.json'; const ReviewRankingList = () => { const { reviews } = reviewRankingList; diff --git a/frontend/src/mocks/data/rankingProducts.json b/frontend/src/mocks/data/productRanking.json similarity index 100% rename from frontend/src/mocks/data/rankingProducts.json rename to frontend/src/mocks/data/productRanking.json diff --git a/frontend/src/mocks/data/rankingReviewList.json b/frontend/src/mocks/data/reviewRankingList.json similarity index 100% rename from frontend/src/mocks/data/rankingReviewList.json rename to frontend/src/mocks/data/reviewRankingList.json diff --git a/frontend/src/types/ranking.ts b/frontend/src/types/ranking.ts index a0501fef..3b9e2323 100644 --- a/frontend/src/types/ranking.ts +++ b/frontend/src/types/ranking.ts @@ -1,4 +1,4 @@ -export interface RankingProduct { +export interface ProductRanking { id: number; rank: number; name: string; From e5d1cc1933fb23afa697c52e3ca7fb0bf906edd2 Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Wed, 19 Jul 2023 13:45:34 +0900 Subject: [PATCH 028/185] =?UTF-8?q?[FE]=20fix:=20SortOptionList=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EB=A6=AC=20prop=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95=20(#80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/SortOptionList/SortOptionList.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx index 50d35213..3396db94 100644 --- a/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx +++ b/frontend/src/components/Common/SortOptionList/SortOptionList.stories.tsx @@ -25,13 +25,13 @@ export const Default: Story = { ref.current?.close(); }; - const handleSortOption = (optionIndex: number) => { + const selectSortOption = (optionIndex: number) => { setSelectedOption(optionIndex); }; return ( - + ); }, From e2e7d5eeb59e6bd34b78c9b989aab25efb58967f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Wed, 19 Jul 2023 16:20:15 +0900 Subject: [PATCH 029/185] =?UTF-8?q?[BE]=20chore:=20application.yml=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=EB=A7=88=EB=8B=A4=20=EB=B6=84=EB=A6=AC=20(#8?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/funeat/config/EnvProperties.java | 12 ---------- .../src/main/resources/application-dev.yml | 18 +++++++++++++++ .../src/main/resources/application-local.yml | 22 +++++++++++++++++++ .../src/main/resources/application-prod.yml | 17 ++++++++++++++ .../src/main/resources/application.properties | 1 - backend/src/main/resources/application.yml | 3 +++ .../main/resources/properties/env.properties | 1 - .../src/test/resources/application.properties | 8 ------- backend/src/test/resources/application.yml | 20 +++++++++++++++++ 9 files changed, 80 insertions(+), 22 deletions(-) delete mode 100644 backend/src/main/java/com/funeat/config/EnvProperties.java create mode 100644 backend/src/main/resources/application-dev.yml create mode 100644 backend/src/main/resources/application-local.yml create mode 100644 backend/src/main/resources/application-prod.yml delete mode 100644 backend/src/main/resources/application.properties create mode 100644 backend/src/main/resources/application.yml delete mode 100644 backend/src/main/resources/properties/env.properties delete mode 100644 backend/src/test/resources/application.properties create mode 100644 backend/src/test/resources/application.yml diff --git a/backend/src/main/java/com/funeat/config/EnvProperties.java b/backend/src/main/java/com/funeat/config/EnvProperties.java deleted file mode 100644 index 11a5cc86..00000000 --- a/backend/src/main/java/com/funeat/config/EnvProperties.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.funeat.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.context.annotation.PropertySources; - -@Configuration -@PropertySources({ - @PropertySource("classpath:properties/env.properties") -}) -public class EnvProperties { -} diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml new file mode 100644 index 00000000..6e1cd8bb --- /dev/null +++ b/backend/src/main/resources/application-dev.yml @@ -0,0 +1,18 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: { DEV-DB-URL } + username: { DEV-DB-USERNAME } + password: { DEV-DB-PASSWORD } + + jpa: + hibernate: + ddl-auto: update + properties: + hibernate: + format_sql: true + show_sql: true + +review: + image: + path: { DEV-IMAGE-PATH } diff --git a/backend/src/main/resources/application-local.yml b/backend/src/main/resources/application-local.yml new file mode 100644 index 00000000..d4f8d059 --- /dev/null +++ b/backend/src/main/resources/application-local.yml @@ -0,0 +1,22 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: + username: + password: + + jpa: + hibernate: + ddl-auto: create + properties: + hibernate: + format_sql: true + show_sql: true + +logging: + level: + org.hibernate.type.descriptor.sql: trace + +review: + image: + path: diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml new file mode 100644 index 00000000..ef0a53cc --- /dev/null +++ b/backend/src/main/resources/application-prod.yml @@ -0,0 +1,17 @@ +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: { PROD-DB-URL } + username: { PROD-DB-USERNAME } + password: { PROD-DB-PASSWORD } + + jpa: + hibernate: + ddl-auto: none + properties: + hibernate: + show_sql: true + +review: + image: + path: { PROD-IMAGE-PATH } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties deleted file mode 100644 index 8b137891..00000000 --- a/backend/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml new file mode 100644 index 00000000..0a9d2385 --- /dev/null +++ b/backend/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + profiles: + active: { DEPLOY-ACTIVE:dev } diff --git a/backend/src/main/resources/properties/env.properties b/backend/src/main/resources/properties/env.properties deleted file mode 100644 index ce793659..00000000 --- a/backend/src/main/resources/properties/env.properties +++ /dev/null @@ -1 +0,0 @@ -review.image.path=/Users/wugawuga/fun-eat/review/images/ diff --git a/backend/src/test/resources/application.properties b/backend/src/test/resources/application.properties deleted file mode 100644 index caecd462..00000000 --- a/backend/src/test/resources/application.properties +++ /dev/null @@ -1,8 +0,0 @@ -spring.profiles.active=test -spring.datasource.url=jdbc:h2:mem:test;MODE=MySQL -spring.datasource.username=sa -spring.jpa.hibernate.ddl-auto=create -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.show-sql=true -logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -spring.profiles.active=test diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml new file mode 100644 index 00000000..b27af211 --- /dev/null +++ b/backend/src/test/resources/application.yml @@ -0,0 +1,20 @@ +spring: + profiles: + active: test + + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:test + username: sa + + jpa: + hibernate: + ddl-auto: create + properties: + hibernate: + format_sql: true + show_sql: true + +logging: + level: + org.hibernate.type.descriptor.sql: trace From 39d022a5742f82edace60628241fb2ab5bed2f59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Wed, 19 Jul 2023 16:48:44 +0900 Subject: [PATCH 030/185] =?UTF-8?q?[BE]=20fix:=20datasource=20url=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=9C=BC=EB=A1=9C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=86=B5=EA=B3=BC=20=EC=88=98=EC=A0=95=20(#88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/test/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml index b27af211..0e698721 100644 --- a/backend/src/test/resources/application.yml +++ b/backend/src/test/resources/application.yml @@ -4,7 +4,7 @@ spring: datasource: driver-class-name: org.h2.Driver - url: jdbc:h2:mem:test + url: jdbc:h2:mem:test;MODE=MySQL username: sa jpa: From 69c730792118119e6bc30dc82db216fb1f1a1af2 Mon Sep 17 00:00:00 2001 From: Dabeen Jeong Date: Wed, 19 Jul 2023 17:54:34 +0900 Subject: [PATCH 031/185] =?UTF-8?q?[BE]=20refactor:=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=20=EC=9D=B4=EC=8A=88=20=EC=A0=9C=EC=99=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?(#90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: GrobalControllerAdvice 삭제 * refactor: final 추가, response 정적 팩토리 메서드 추가, 코드 컨벤션 추가 * refactor: 매직 넘버 적용 --- .../funeat/common/GrobalControllerAdvice.java | 20 ------- .../product/application/ProductService.java | 16 +++-- .../dto/ProductsInCategoryPageDto.java | 10 +--- .../dto/ProductsInCategoryResponse.java | 4 ++ .../presentation/ProductController.java | 2 +- .../funeat/acceptance/common/CommonSteps.java | 2 +- .../com/funeat/common/DataClearExtension.java | 6 +- .../persistence/CategoryRepositoryTest.java | 8 +-- .../persistence/ProductRepositoryTest.java | 59 ++++++++----------- .../tag/persistence/TagRepositoryTest.java | 18 ++++-- 10 files changed, 63 insertions(+), 82 deletions(-) delete mode 100644 backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java diff --git a/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java b/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java deleted file mode 100644 index db71af50..00000000 --- a/backend/src/main/java/com/funeat/common/GrobalControllerAdvice.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.funeat.common; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@RestControllerAdvice -public class GrobalControllerAdvice { - - private final Logger log = LoggerFactory.getLogger(getClass()); - - @ExceptionHandler - public ResponseEntity handle(Exception e) { - System.out.println("hello"); - log.warn(e.getMessage()); - return ResponseEntity.badRequest().build(); - } -} diff --git a/backend/src/main/java/com/funeat/product/application/ProductService.java b/backend/src/main/java/com/funeat/product/application/ProductService.java index 9df95b72..5ab93114 100644 --- a/backend/src/main/java/com/funeat/product/application/ProductService.java +++ b/backend/src/main/java/com/funeat/product/application/ProductService.java @@ -23,6 +23,8 @@ @Transactional(readOnly = true) public class ProductService { + private static final int THREE = 3; + private static final int TOP = 0; private final CategoryRepository categoryRepository; private final ProductRepository productRepository; private final ReviewRepository reviewRepository; @@ -38,24 +40,26 @@ public ProductService(final CategoryRepository categoryRepository, final Product public ProductsInCategoryResponse getAllProductsInCategory(final Long categoryId, final Pageable pageable) { - Category category = categoryRepository.findById(categoryId) + final Category category = categoryRepository.findById(categoryId) .orElseThrow(IllegalArgumentException::new); - Page productPages = productRepository.findAllByCategory(category, pageable); + final Page productPages = productRepository.findAllByCategory(category, pageable); - ProductsInCategoryPageDto pageDto = ProductsInCategoryPageDto.toDto(productPages); - List productDtos = productPages.getContent() + final ProductsInCategoryPageDto pageDto = ProductsInCategoryPageDto.toDto(productPages); + final List productDtos = productPages.getContent() .stream() .map(it -> ProductInCategoryDto.toDto(it, reviewRepository.countByProduct(it))) .collect(Collectors.toList()); - return new ProductsInCategoryResponse(pageDto, productDtos); + return ProductsInCategoryResponse.toResponse(pageDto, productDtos); } public ProductResponse findProductDetail(final Long productId) { final Product product = productRepository.findById(productId) .orElseThrow(IllegalArgumentException::new); - final List tags = reviewTagRepository.findTop3TagsByReviewIn(productId, PageRequest.of(0, 3)); + + final List tags = reviewTagRepository.findTop3TagsByReviewIn(productId, PageRequest.of(TOP, THREE)); + return ProductResponse.toResponse(product, tags); } } diff --git a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java index 438f2610..1e8939f0 100644 --- a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java +++ b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryPageDto.java @@ -1,6 +1,5 @@ package com.funeat.product.dto; -import com.fasterxml.jackson.annotation.JsonCreator; import com.funeat.product.domain.Product; import org.springframework.data.domain.Page; @@ -12,13 +11,8 @@ public class ProductsInCategoryPageDto { private final Long requestPage; private final Long requestSize; - @JsonCreator - public ProductsInCategoryPageDto(final Long totalDataCount, - final Long totalPages, - final boolean FirstPage, - final boolean LastPage, - final Long requestPage, - final Long requestSize) { + public ProductsInCategoryPageDto(final Long totalDataCount, final Long totalPages, final boolean FirstPage, + final boolean LastPage, final Long requestPage, final Long requestSize) { this.totalDataCount = totalDataCount; this.totalPages = totalPages; this.firstPage = FirstPage; diff --git a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java index 98546858..8123b4ab 100644 --- a/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java +++ b/backend/src/main/java/com/funeat/product/dto/ProductsInCategoryResponse.java @@ -12,6 +12,10 @@ public ProductsInCategoryResponse(final ProductsInCategoryPageDto page, final Li this.products = products; } + public static ProductsInCategoryResponse toResponse(final ProductsInCategoryPageDto page, final List products) { + return new ProductsInCategoryResponse(page, products); + } + public ProductsInCategoryPageDto getPage() { return page; } diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductController.java b/backend/src/main/java/com/funeat/product/presentation/ProductController.java index 2afd0e6a..7e231bd8 100644 --- a/backend/src/main/java/com/funeat/product/presentation/ProductController.java +++ b/backend/src/main/java/com/funeat/product/presentation/ProductController.java @@ -26,7 +26,7 @@ public ResponseEntity getAllProductsInCategory( @PathVariable(name = "category_id") final Long categoryId, @PageableDefault Pageable pageable ) { - ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); + final ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); return ResponseEntity.ok(response); } diff --git a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java index a2f4081b..32d0c67e 100644 --- a/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java +++ b/backend/src/test/java/com/funeat/acceptance/common/CommonSteps.java @@ -21,7 +21,7 @@ public class CommonSteps { assertThat(response.header("Location")).isNotBlank(); } - public static void STATUS_CODE를_검증한다(final ExtractableResponse response, HttpStatus httpStatus) { + public static void STATUS_CODE를_검증한다(final ExtractableResponse response, final HttpStatus httpStatus) { assertThat(response.statusCode()).isEqualTo(httpStatus.value()); } } diff --git a/backend/src/test/java/com/funeat/common/DataClearExtension.java b/backend/src/test/java/com/funeat/common/DataClearExtension.java index 0026eb24..4c06f51f 100644 --- a/backend/src/test/java/com/funeat/common/DataClearExtension.java +++ b/backend/src/test/java/com/funeat/common/DataClearExtension.java @@ -7,12 +7,12 @@ public class DataClearExtension implements BeforeEachCallback { @Override - public void beforeEach(ExtensionContext context) throws Exception { - DataCleaner dataCleaner = getDataCleaner(context); + public void beforeEach(final ExtensionContext context) { + final DataCleaner dataCleaner = getDataCleaner(context); dataCleaner.clear(); } - private DataCleaner getDataCleaner(ExtensionContext extensionContext) { + private DataCleaner getDataCleaner(final ExtensionContext extensionContext) { return SpringExtension.getApplicationContext(extensionContext) .getBean(DataCleaner.class); } diff --git a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java index b01cf681..31b97b99 100644 --- a/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java +++ b/backend/src/test/java/com/funeat/product/persistence/CategoryRepositoryTest.java @@ -41,7 +41,7 @@ class CategoryRepositoryTest { categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); // when - final List actual = categoryRepository.findAllByType(CategoryType.FOOD); + final var actual = categoryRepository.findAllByType(CategoryType.FOOD); // then assertThat(actual) @@ -53,12 +53,12 @@ class CategoryRepositoryTest { void 카테고리_타입이_STORE인_모든_카테고리를_조회한다() { // given categoryRepository.saveAll(List.of(간편식사, 즉석조리, 과자류, 아이스크림, 식품, 음료, CU, GS25, EMART24)); + final var expected = List.of(CU, GS25, EMART24); // when - final List actual = categoryRepository.findAllByType(CategoryType.STORE); + final var actual = categoryRepository.findAllByType(CategoryType.STORE); // then - assertThat(actual).usingRecursiveFieldByFieldElementComparatorIgnoringFields() - .containsOnly(CU, GS25, EMART24); + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); } } diff --git a/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java b/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java index 2181ff03..6c40fecb 100644 --- a/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java +++ b/backend/src/test/java/com/funeat/product/persistence/ProductRepositoryTest.java @@ -8,7 +8,6 @@ import com.funeat.product.domain.CategoryType; import com.funeat.product.domain.Product; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; @@ -26,49 +25,32 @@ @DisplayNameGeneration(ReplaceUnderscores.class) class ProductRepositoryTest { - private Category category; - private Product product1; - private Product product2; - private Product product3; - private Product product4; - private Product product5; - private Product product6; - private Product product7; - private Product product8; - private Product product9; - private Product product10; - @Autowired private CategoryRepository categoryRepository; @Autowired private ProductRepository productRepository; - @BeforeEach - void setUp() { - category = categoryRepository.save(new Category("간편식사", CategoryType.FOOD)); - - product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", category); - product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", category); - product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", category); - product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", category); - product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", category); - product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", category); - product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", category); - product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", category); - product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", category); - product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", category); - } - @Test void 카테고리별_상품을_가격이_높은_순으로_정렬한다() { // given + final var category = categoryRepository.save(new Category("간편식사", CategoryType.FOOD)); + final var product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", category); + final var product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", category); + final var product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", category); + final var product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", category); + final var product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", category); + final var product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", category); + final var product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", category); + final var product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", category); + final var product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", category); + final var product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", category); productRepository.saveAll( List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10)); // when - PageRequest pageRequest = PageRequest.of(0, 3, Sort.by("price").descending()); - List actual = productRepository.findAllByCategory(category, pageRequest).getContent(); + final var pageRequest = PageRequest.of(0, 3, Sort.by("price").descending()); + final var actual = productRepository.findAllByCategory(category, pageRequest).getContent(); // then assertThat(actual).containsExactly(product9, product10, product5); @@ -77,13 +59,24 @@ void setUp() { @Test void 카테고리별_상품을_가격이_낮은_순으로_정렬한다() { // given + final var category = categoryRepository.save(new Category("간편식사", CategoryType.FOOD)); + final var product1 = new Product("삼각김밥1", 1000L, "image.png", "맛있는 삼각김밥1", category); + final var product2 = new Product("삼각김밥2", 2000L, "image.png", "맛있는 삼각김밥2", category); + final var product3 = new Product("삼각김밥3", 1500L, "image.png", "맛있는 삼각김밥3", category); + final var product4 = new Product("삼각김밥4", 1200L, "image.png", "맛있는 삼각김밥4", category); + final var product5 = new Product("삼각김밥5", 2300L, "image.png", "맛있는 삼각김밥5", category); + final var product6 = new Product("삼각김밥6", 1700L, "image.png", "맛있는 삼각김밥6", category); + final var product7 = new Product("삼각김밥7", 1800L, "image.png", "맛있는 삼각김밥7", category); + final var product8 = new Product("삼각김밥8", 800L, "image.png", "맛있는 삼각김밥8", category); + final var product9 = new Product("삼각김밥9", 3100L, "image.png", "맛있는 삼각김밥9", category); + final var product10 = new Product("삼각김밥10", 2700L, "image.png", "맛있는 삼각김밥10", category); productRepository.saveAll( List.of(product1, product2, product3, product4, product5, product6, product7, product8, product9, product10)); // when - PageRequest pageRequest = PageRequest.of(0, 3, Sort.by("price").ascending()); - List actual = productRepository.findAllByCategory(category, pageRequest).getContent(); + final var pageRequest = PageRequest.of(0, 3, Sort.by("price").ascending()); + final var actual = productRepository.findAllByCategory(category, pageRequest).getContent(); // then assertThat(actual).containsExactly(product8, product1, product4); diff --git a/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java b/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java index 43bcbc5b..968a41b9 100644 --- a/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java +++ b/backend/src/test/java/com/funeat/tag/persistence/TagRepositoryTest.java @@ -2,33 +2,39 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.funeat.common.DataCleaner; +import com.funeat.common.DataClearExtension; import com.funeat.tag.domain.Tag; import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; @DataJpaTest +@Import(DataCleaner.class) +@ExtendWith(DataClearExtension.class) @SuppressWarnings("NonAsciiCharacters") @DisplayNameGeneration(ReplaceUnderscores.class) class TagRepositoryTest { @Autowired - TagRepository tagRepository; + private TagRepository tagRepository; @Test void 여러_태그_아이디로_태그들을_조회_할_수_있다() { // given - var tags = 태그_추가_요청(); - var tagIds = tags.stream() + final var tags = 태그_추가_요청(); + final var tagIds = tags.stream() .map(Tag::getId) .collect(Collectors.toList()); // then - List result = tagRepository.findTagsByIdIn(tagIds); + final var result = tagRepository.findTagsByIdIn(tagIds); // when assertThat(result).usingRecursiveComparison() @@ -36,8 +42,8 @@ class TagRepositoryTest { } private List 태그_추가_요청() { - final Tag testTag1 = tagRepository.save(new Tag("testTag1")); - final Tag testTag2 = tagRepository.save(new Tag("testTag2")); + final var testTag1 = tagRepository.save(new Tag("testTag1")); + final var testTag2 = tagRepository.save(new Tag("testTag2")); return List.of(testTag1, testTag2); } From 8bf9270a5d9500e3bcc4e1d95213b01aef76ef8d Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Wed, 19 Jul 2023 22:58:50 +0900 Subject: [PATCH 032/185] =?UTF-8?q?[FE]=20feat:=20Router=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: react-router-dom 설치 * feat: router 컴포넌트 추가 * feat: 빠져있던 home svg icon 추가 * feat: NavigationBar 컴포넌트에서 라우팅 되는 기능 추가 * refactor: navigateMenu에서 selectMenu로 네이밍 수정 * feat: Title컴포넌트에서 뒤로가기 기능 추가 * style: 불필요한 코드 삭제 * feat: index.ts에 export문 추가 * feat: 뒤로가기 기능을 Link로 수정 * fix: list 아이콘 스타일 변경 * refactor: nav 태그에 position absolute를 주는 것으로 변경 --- frontend/.storybook/preview.tsx | 5 ++- frontend/package.json | 1 + frontend/src/App.tsx | 13 ++++-- .../Common/NavigationBar/NavigationBar.tsx | 38 +++++++++++------- .../src/components/Common/Svg/SvgSprite.tsx | 7 +++- .../src/components/Common/Title/Title.tsx | 11 +++-- frontend/src/components/Common/index.ts | 1 + frontend/src/constants/index.ts | 12 +++--- frontend/src/constants/path.ts | 7 ++++ frontend/src/index.tsx | 5 ++- frontend/src/pages/.gitkeep | 0 frontend/src/pages/HomePage.tsx | 5 +++ frontend/src/pages/ProductList.tsx | 5 +++ frontend/src/pages/ProfilePage.tsx | 5 +++ frontend/src/pages/RecipePage.tsx | 5 +++ frontend/src/pages/SearchPage.tsx | 5 +++ frontend/src/router/.gitkeep | 0 frontend/src/router/index.tsx | 40 +++++++++++++++++++ frontend/src/types/common.ts | 3 +- frontend/src/types/index.ts | 2 - frontend/yarn.lock | 20 ++++++++++ 21 files changed, 155 insertions(+), 35 deletions(-) create mode 100644 frontend/src/constants/path.ts delete mode 100644 frontend/src/pages/.gitkeep create mode 100644 frontend/src/pages/HomePage.tsx create mode 100644 frontend/src/pages/ProductList.tsx create mode 100644 frontend/src/pages/ProfilePage.tsx create mode 100644 frontend/src/pages/RecipePage.tsx create mode 100644 frontend/src/pages/SearchPage.tsx delete mode 100644 frontend/src/router/.gitkeep create mode 100644 frontend/src/router/index.tsx diff --git a/frontend/.storybook/preview.tsx b/frontend/.storybook/preview.tsx index 67c7b38b..a8130e79 100644 --- a/frontend/.storybook/preview.tsx +++ b/frontend/.storybook/preview.tsx @@ -3,11 +3,14 @@ import { FunEatProvider } from '@fun-eat/design-system'; import type { Preview } from '@storybook/react'; import { mswDecorator } from 'msw-storybook-addon'; import { handlers } from '../src/mocks/handlers'; +import { BrowserRouter } from 'react-router-dom'; export const decorators = [ (Story) => ( - + + + ), mswDecorator, diff --git a/frontend/package.json b/frontend/package.json index 5be2fd15..5c2cc547 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "@fun-eat/design-system": "^0.2.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.14.2", "styled-components": "^6.0.2" }, "devDependencies": { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 44961626..6035d43b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,12 @@ -const App = () => { - return <>; -}; +import { Outlet } from 'react-router-dom'; + +import { NavigationBar } from './components/Common'; + +const App = () => ( + <> + + + +); export default App; diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx index 50dd7c3c..a800f6db 100644 --- a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx +++ b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx @@ -1,5 +1,6 @@ -import { Text, theme } from '@fun-eat/design-system'; +import { Link, Text, theme } from '@fun-eat/design-system'; import { useState } from 'react'; +import { Link as RouterLink } from 'react-router-dom'; import styled from 'styled-components'; import SvgIcon from '../Svg/SvgIcon'; @@ -9,36 +10,43 @@ import { NAVIGATION_MENU } from '@/constants'; const NavigationBar = () => { const [selectedMenu, setSelectedMenu] = useState('홈'); - const navigateMenu = (name: string) => { + const selectMenu = (name: string) => { setSelectedMenu(name); }; return ( - + + ); }; export default NavigationBar; -const NavigationBarContainer = styled.ul` +const NavigationBarContainer = styled.nav` + position: absolute; + bottom: 0; + width: 100%; +`; + +const NavigationBarList = styled.ul` display: flex; align-items: center; justify-content: space-around; - width: 100%; height: 62px; padding-top: 12px; border: 1px solid ${({ theme }) => theme.borderColors.disabled}; diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index 98b3aed7..e618f3e3 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -4,8 +4,8 @@ const SvgSprite = () => { - - + + @@ -36,6 +36,9 @@ const SvgSprite = () => { + + + diff --git a/frontend/src/components/Common/Title/Title.tsx b/frontend/src/components/Common/Title/Title.tsx index dab9baf2..35d8e4c6 100644 --- a/frontend/src/components/Common/Title/Title.tsx +++ b/frontend/src/components/Common/Title/Title.tsx @@ -1,4 +1,5 @@ -import { Text, theme } from '@fun-eat/design-system'; +import { Link, Text, theme } from '@fun-eat/design-system'; +import { Link as RouterLink } from 'react-router-dom'; import styled from 'styled-components'; import SvgIcon from '../Svg/SvgIcon'; @@ -10,9 +11,11 @@ interface TitleProps { const Title = ({ headingTitle }: TitleProps) => { return ( - - - + + + + + {headingTitle} diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index a8c8d44f..1c64dad5 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -1,3 +1,4 @@ export { default as CategoryMenu } from './CategoryMenu/CategoryMenu'; export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; +export { default as NavigationBar } from './NavigationBar/NavigationBar'; diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts index 0ec89b03..92db9bd7 100644 --- a/frontend/src/constants/index.ts +++ b/frontend/src/constants/index.ts @@ -1,30 +1,32 @@ +import { PATH } from './path'; + import type { NavigationMenu } from '@/types/common'; export const NAVIGATION_MENU: NavigationMenu[] = [ { variant: 'search', name: '검색', - path: '', + path: PATH.SEARCH, }, { variant: 'list', name: '목록', - path: '', + path: PATH.PRODUCT_LIST, }, { variant: 'home', name: '홈', - path: '', + path: PATH.HOME, }, { variant: 'recipe', name: '꿀조합', - path: '', + path: PATH.RECIPE, }, { variant: 'profile', name: '마이', - path: '', + path: PATH.PROFILE, }, ]; diff --git a/frontend/src/constants/path.ts b/frontend/src/constants/path.ts new file mode 100644 index 00000000..e3733ba2 --- /dev/null +++ b/frontend/src/constants/path.ts @@ -0,0 +1,7 @@ +export const PATH = { + HOME: '/', + SEARCH: '/search', + PRODUCT_LIST: '/product-list', + PROFILE: '/profile', + RECIPE: '/recipe', +} as const; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index cfc6c28f..3de26969 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,9 +1,10 @@ import { FunEatProvider } from '@fun-eat/design-system'; import React from 'react'; import ReactDOM from 'react-dom/client'; +import { RouterProvider } from 'react-router-dom'; -import App from './App'; import { SvgSprite } from './components/Common'; +import router from './router'; const main = async () => { if (process.env.NODE_ENV === 'development') { @@ -18,7 +19,7 @@ root.render( - + ); diff --git a/frontend/src/pages/.gitkeep b/frontend/src/pages/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx new file mode 100644 index 00000000..f5ed3ffa --- /dev/null +++ b/frontend/src/pages/HomePage.tsx @@ -0,0 +1,5 @@ +const HomePage = () => { + return <>홈; +}; + +export default HomePage; diff --git a/frontend/src/pages/ProductList.tsx b/frontend/src/pages/ProductList.tsx new file mode 100644 index 00000000..a5ca3867 --- /dev/null +++ b/frontend/src/pages/ProductList.tsx @@ -0,0 +1,5 @@ +const ProductListPage = () => { + return <>; +}; + +export default ProductListPage; diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx new file mode 100644 index 00000000..7dce5a4b --- /dev/null +++ b/frontend/src/pages/ProfilePage.tsx @@ -0,0 +1,5 @@ +const ProfilePage = () => { + return <>; +}; + +export default ProfilePage; diff --git a/frontend/src/pages/RecipePage.tsx b/frontend/src/pages/RecipePage.tsx new file mode 100644 index 00000000..4c93c922 --- /dev/null +++ b/frontend/src/pages/RecipePage.tsx @@ -0,0 +1,5 @@ +const RecipePage = () => { + return <>; +}; + +export default RecipePage; diff --git a/frontend/src/pages/SearchPage.tsx b/frontend/src/pages/SearchPage.tsx new file mode 100644 index 00000000..217363fd --- /dev/null +++ b/frontend/src/pages/SearchPage.tsx @@ -0,0 +1,5 @@ +const SearchPage = () => { + return <>; +}; + +export default SearchPage; diff --git a/frontend/src/router/.gitkeep b/frontend/src/router/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx new file mode 100644 index 00000000..889720f0 --- /dev/null +++ b/frontend/src/router/index.tsx @@ -0,0 +1,40 @@ +import { createBrowserRouter } from 'react-router-dom'; + +import App from '@/App'; +import { PATH } from '@/constants/path'; +import HomePage from '@/pages/HomePage'; +import ProductListPage from '@/pages/ProductList'; +import ProfilePage from '@/pages/ProfilePage'; +import RecipePage from '@/pages/RecipePage'; +import SearchPage from '@/pages/SearchPage'; + +const router = createBrowserRouter([ + { + path: '/', + element: , + children: [ + { + index: true, + element: , + }, + { + path: PATH.PRODUCT_LIST, + element: , + }, + { + path: PATH.RECIPE, + element: , + }, + { + path: PATH.SEARCH, + element: , + }, + { + path: PATH.PROFILE, + element: , + }, + ], + }, +]); + +export default router; diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index acb811b2..9483cc7f 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -1,4 +1,5 @@ import type { SvgIconVariant } from '@/components/Common/Svg/SvgIcon'; +import type { PATH } from '@/constants/path'; export interface Category { id: number; @@ -13,5 +14,5 @@ export interface Tag { export interface NavigationMenu { variant: SvgIconVariant; name: '검색' | '목록' | '홈' | '꿀조합' | '마이'; - path: string; + path: (typeof PATH)[keyof typeof PATH]; } diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 1855b45f..b878f1c7 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -1,3 +1 @@ -export type BBBB = ''; - export * from './ranking'; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9e98c659..5bb50fa3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1806,6 +1806,11 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@remix-run/router@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.7.2.tgz#cba1cf0a04bc04cb66027c51fa600e9cbc388bc8" + integrity sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A== + "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" @@ -7833,6 +7838,21 @@ react-refresh@^0.11.0: resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-router-dom@^6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.14.2.tgz#88f520118b91aa60233bd08dbd3fdcaea3a68488" + integrity sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg== + dependencies: + "@remix-run/router" "1.7.2" + react-router "6.14.2" + +react-router@6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.14.2.tgz#1f60994d8c369de7b8ba7a78d8f7ec23df76b300" + integrity sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ== + dependencies: + "@remix-run/router" "1.7.2" + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" From 4712ad2ef4a799543bbf8bad9f36a1b1e73c7b9e Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Thu, 20 Jul 2023 12:32:27 +0900 Subject: [PATCH 033/185] =?UTF-8?q?[FE]=20feat:=20TabMenu=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: TabMenu 컴포넌트 추가 * feat: TabMenu 스토리 작성 * refactor: TabMenuProps 추가 * refactor: 탭 버튼 스타일 분리 --- .../Common/TabMenu/TabMenu.stories.tsx | 22 +++++++ .../src/components/Common/TabMenu/TabMenu.tsx | 63 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 frontend/src/components/Common/TabMenu/TabMenu.stories.tsx create mode 100644 frontend/src/components/Common/TabMenu/TabMenu.tsx diff --git a/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx b/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx new file mode 100644 index 00000000..4ba72445 --- /dev/null +++ b/frontend/src/components/Common/TabMenu/TabMenu.stories.tsx @@ -0,0 +1,22 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import TabMenu from './TabMenu'; + +const meta: Meta = { + title: 'common/TabMenu', + component: TabMenu, + args: { + tabMenus: ['리뷰 1,200', '꿀조합'], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: ({ ...args }) => ( +
    + +
    + ), +}; diff --git a/frontend/src/components/Common/TabMenu/TabMenu.tsx b/frontend/src/components/Common/TabMenu/TabMenu.tsx new file mode 100644 index 00000000..62c10163 --- /dev/null +++ b/frontend/src/components/Common/TabMenu/TabMenu.tsx @@ -0,0 +1,63 @@ +import { Button, useTheme } from '@fun-eat/design-system'; +import { useState } from 'react'; +import styled from 'styled-components'; + +interface TabMenuProps { + tabMenus: string[]; +} + +const TabMenu = ({ tabMenus }: TabMenuProps) => { + const [selectedTab, setSelectedTab] = useState(0); + const theme = useTheme(); + + const selectTabMenu = (selectedIndex: number) => { + setSelectedTab(selectedIndex); + }; + + return ( + + {tabMenus.map((menu, index) => { + const isSelected = selectedTab === index; + return ( + + selectTabMenu(index)} + > + {menu} + + + ); + })} + + ); +}; + +export default TabMenu; + +const TabMenuContainer = styled.ul` + display: flex; +`; + +const TabMenuItem = styled.li` + flex-grow: 1; + width: 50%; + height: 45px; +`; + +const TabMenuButton = styled(Button)` + width: 100%; + height: 100%; + padding: 0; + line-height: 45px; +`; From 83dc3d4a7d53f892930deb805439f4d8e3d67780 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Thu, 20 Jul 2023 12:56:39 +0900 Subject: [PATCH 034/185] =?UTF-8?q?[FE]=20feat:=20Header=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 프로젝트에서 svg 파일을 사용할 수 있게 추가 * feat: 로고 이미지 추가 * feat: Header 컴포넌트 추가 * chore: svg파일을 컴포넌트로 import할 수 있게 설정 * feat: 로고를 이미지->svg로 변경 * chore: storybook에서 svg 파일이 렌더링 되게끔 수정 --- frontend/.storybook/main.ts | 15 ++ frontend/package.json | 1 + frontend/src/App.tsx | 3 +- frontend/src/assets/.gitkeep | 0 frontend/src/assets/logo.svg | 97 +++++++ .../Common/Header/Header.stories.tsx | 13 + .../src/components/Common/Header/Header.tsx | 21 ++ frontend/src/components/Common/index.ts | 1 + frontend/src/types/images.d.ts | 6 + frontend/webpack.common.js | 5 + frontend/yarn.lock | 249 +++++++++++++++++- 11 files changed, 404 insertions(+), 7 deletions(-) delete mode 100644 frontend/src/assets/.gitkeep create mode 100644 frontend/src/assets/logo.svg create mode 100644 frontend/src/components/Common/Header/Header.stories.tsx create mode 100644 frontend/src/components/Common/Header/Header.tsx diff --git a/frontend/.storybook/main.ts b/frontend/.storybook/main.ts index 01d201a5..ad465581 100644 --- a/frontend/.storybook/main.ts +++ b/frontend/.storybook/main.ts @@ -34,6 +34,21 @@ const config: StorybookConfig = { '@utils': path.resolve(__dirname, '../src/utils'), }; } + const imageRule = config.module?.rules?.find((rule) => { + const test = (rule as { test: RegExp }).test; + + if (!test) return false; + + return test.test('.svg'); + }) as { [key: string]: any }; + + imageRule.exclude = /\.svg$/; + + config.module?.rules?.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }); + return config; }, docs: { diff --git a/frontend/package.json b/frontend/package.json index 5c2cc547..9b939ffc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "@storybook/react": "^7.0.27", "@storybook/react-webpack5": "^7.0.27", "@storybook/testing-library": "^0.0.14-next.2", + "@svgr/webpack": "^8.0.1", "@types/react": "^18.2.14", "@types/react-dom": "^18.2.6", "@types/styled-components": "^5.1.26", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6035d43b..e6b0da8d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,9 +1,10 @@ import { Outlet } from 'react-router-dom'; -import { NavigationBar } from './components/Common'; +import { Header, NavigationBar } from './components/Common'; const App = () => ( <> +
    diff --git a/frontend/src/assets/.gitkeep b/frontend/src/assets/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/assets/logo.svg b/frontend/src/assets/logo.svg new file mode 100644 index 00000000..f243e66a --- /dev/null +++ b/frontend/src/assets/logo.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/Common/Header/Header.stories.tsx b/frontend/src/components/Common/Header/Header.stories.tsx new file mode 100644 index 00000000..b88b4258 --- /dev/null +++ b/frontend/src/components/Common/Header/Header.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import Header from './Header'; + +const meta: Meta = { + title: 'common/Header', + component: Header, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Common/Header/Header.tsx b/frontend/src/components/Common/Header/Header.tsx new file mode 100644 index 00000000..b1d80fe6 --- /dev/null +++ b/frontend/src/components/Common/Header/Header.tsx @@ -0,0 +1,21 @@ +import styled from 'styled-components'; + +import Logo from '@/assets/logo.svg'; + +const Header = () => { + return ( + + + + ); +}; + +export default Header; + +const HeaderContainer = styled.header` + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 60px; +`; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index 1c64dad5..b74d43c9 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -1,4 +1,5 @@ export { default as CategoryMenu } from './CategoryMenu/CategoryMenu'; export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; +export { default as Header } from './Header/Header'; export { default as NavigationBar } from './NavigationBar/NavigationBar'; diff --git a/frontend/src/types/images.d.ts b/frontend/src/types/images.d.ts index c13a67fc..0fa71f19 100644 --- a/frontend/src/types/images.d.ts +++ b/frontend/src/types/images.d.ts @@ -12,3 +12,9 @@ declare module '*.png' { const src: string; export default src; } + +declare module '*.svg' { + import type React from 'react'; + const SVG: React.VFC>; + export default SVG; +} diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index a259309a..43dfa9d6 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -29,6 +29,11 @@ module.exports = { test: /\.(png|jpeg|jpg)$/, type: 'asset/resource', }, + { + test: /\.svg$/, + issuer: /\.tsx$/, + use: [{ loader: '@svgr/webpack' }], + }, ], }, }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 5bb50fa3..d4654d48 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -50,7 +50,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.2", "@babel/core@^7.21.0", "@babel/core@^7.7.5": +"@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.2", "@babel/core@^7.21.0", "@babel/core@^7.21.3", "@babel/core@^7.7.5": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== @@ -951,6 +951,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" +"@babel/plugin-transform-react-constant-elements@^7.21.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz#6dfa7c1c37f7d7279e417ceddf5a04abb8bb9c29" + integrity sha512-BF5SXoO+nX3h5OhlN78XbbDrBOffv+AxPP2ENaJOVqjWCgBDeOY3WcaUcddutGSfoap+5NEQ/q/4I3WZIvgkXA== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-transform-react-display-name@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" @@ -1351,7 +1358,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.5", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.21.5", "@babel/types@^7.22.5", "@babel/types@^7.4.4": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== @@ -2631,6 +2638,112 @@ "@types/express" "^4.7.0" file-system-cache "2.3.0" +"@svgr/babel-plugin-add-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22" + integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g== + +"@svgr/babel-plugin-remove-jsx-attribute@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186" + integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA== + +"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44" + integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27" + integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ== + +"@svgr/babel-plugin-svg-dynamic-title@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0" + integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og== + +"@svgr/babel-plugin-svg-em-dimensions@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501" + integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g== + +"@svgr/babel-plugin-transform-react-native-svg@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.0.0.tgz#023cd0895b98521f566060d6bb92100b9fee3775" + integrity sha512-UKrY3860AQICgH7g+6h2zkoxeVEPLYwX/uAjmqo4PIq2FIHppwhIqZstIyTz0ZtlwreKR41O3W3BzsBBiJV2Aw== + +"@svgr/babel-plugin-transform-svg-component@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e" + integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw== + +"@svgr/babel-preset@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.0.0.tgz#6d78100b3b6daf11c940b82d5bd8c3164b9c6ad9" + integrity sha512-KLcjiZychInVrhs86OvcYPLTFu9L5XV2vj0XAaE1HwE3J3jLmIzRY8ttdeAg/iFyp8nhavJpafpDZTt+1LIpkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "8.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "8.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "8.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "8.0.0" + "@svgr/babel-plugin-transform-svg-component" "8.0.0" + +"@svgr/core@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.0.0.tgz#e96829cdb0473345d5671568282ee0736e86ef12" + integrity sha512-aJKtc+Pie/rFYsVH/unSkDaZGvEeylNv/s2cP+ta9/rYWxRVvoV/S4Qw65Kmrtah4CBK5PM6ISH9qUH7IJQCng== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.0.0" + camelcase "^6.2.0" + cosmiconfig "^8.1.3" + snake-case "^3.0.4" + +"@svgr/hast-util-to-babel-ast@8.0.0": + version "8.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4" + integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q== + dependencies: + "@babel/types" "^7.21.3" + entities "^4.4.0" + +"@svgr/plugin-jsx@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.0.1.tgz#b9495e06062cc0cac0e035751b69471ee328236b" + integrity sha512-bfCFb+4ZsM3UuKP2t7KmDwn6YV8qVn9HIQJmau6xeQb/iV65Rpi7NBNBWA2hcCd4GKoCqG8hpaaDk5FDR0eH+g== + dependencies: + "@babel/core" "^7.21.3" + "@svgr/babel-preset" "8.0.0" + "@svgr/hast-util-to-babel-ast" "8.0.0" + svg-parser "^2.0.4" + +"@svgr/plugin-svgo@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.0.1.tgz#df0199313fdc88c3d7cd8e0dff16695e9718548c" + integrity sha512-29OJ1QmJgnohQHDAgAuY2h21xWD6TZiXji+hnx+W635RiXTAlHTbjrZDktfqzkN0bOeQEtNe+xgq73/XeWFfSg== + dependencies: + cosmiconfig "^8.1.3" + deepmerge "^4.3.1" + svgo "^3.0.2" + +"@svgr/webpack@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-8.0.1.tgz#a0e4a711daae347b515335449d198a275b3ab1e4" + integrity sha512-zSoeKcbCmfMXjA11uDuCJb+1LWNb3vy6Qw/VHj0Nfcl3UuqwuoZWknHsBIhCWvi4wU9vPui3aq054qjVyZqY4A== + dependencies: + "@babel/core" "^7.21.3" + "@babel/plugin-transform-react-constant-elements" "^7.21.3" + "@babel/preset-env" "^7.20.2" + "@babel/preset-react" "^7.18.6" + "@babel/preset-typescript" "^7.21.0" + "@svgr/core" "8.0.0" + "@svgr/plugin-jsx" "8.0.1" + "@svgr/plugin-svgo" "8.0.1" + "@testing-library/dom@^8.3.0": version "8.20.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" @@ -2652,6 +2765,11 @@ dependencies: "@babel/runtime" "^7.12.5" +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + "@types/aria-query@^5.0.1": version "5.0.1" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" @@ -3943,6 +4061,11 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + camelize@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" @@ -4154,6 +4277,11 @@ commander@^6.2.1: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + commander@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -4279,6 +4407,16 @@ cosmiconfig@^7.0.1: path-type "^4.0.0" yaml "^1.10.0" +cosmiconfig@^8.1.3: + version "8.2.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" + integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== + dependencies: + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -4323,6 +4461,17 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + css-to-react-native@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" @@ -4332,7 +4481,23 @@ css-to-react-native@^3.2.0: css-color-keywords "^1.0.0" postcss-value-parser "^4.0.2" -css-what@^6.0.1: +css-tree@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +css-tree@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032" + integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA== + dependencies: + mdn-data "2.0.28" + source-map-js "^1.0.1" + +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -4342,6 +4507,13 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +csso@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6" + integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ== + dependencies: + css-tree "~2.2.0" + csstype@^3.0.2, csstype@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" @@ -4402,7 +4574,7 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.2.2: +deepmerge@^4.2.2, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== @@ -4585,7 +4757,16 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -domelementtype@^2.0.1, domelementtype@^2.2.0: +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== @@ -4597,6 +4778,13 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -4606,6 +4794,15 @@ domutils@^2.5.2, domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -4704,6 +4901,11 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + envinfo@^7.7.3: version "7.10.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" @@ -6797,6 +6999,16 @@ mdast-util-to-string@^1.0.0: resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== +mdn-data@2.0.28: + version "2.0.28" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" + integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -8404,6 +8616,14 @@ slash@^4.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" @@ -8413,7 +8633,7 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" -source-map-js@^1.0.2: +source-map-js@^1.0.1, source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -8705,6 +8925,23 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +svg-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a" + integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^5.1.0" + css-tree "^2.2.1" + csso "^5.0.5" + picocolors "^1.0.0" + synchronous-promise@^2.0.15: version "2.0.17" resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" From c77b0817a48dc50f298e83ce744e90cfa6d9f483 Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Thu, 20 Jul 2023 13:15:15 +0900 Subject: [PATCH 035/185] =?UTF-8?q?[FE]=20feat:=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#92)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: root 스타일 추가 * feat: Layout 컴포넌트 추가 * feat: common index에 Layout 추가 * refactor: NavigationBar li 태그 이동 * style: 주석에 TODO 추가 * feat: Layout에 Header 추가 --- frontend/src/App.tsx | 8 ++--- .../src/components/Common/Layout/Layout.tsx | 29 +++++++++++++++++++ .../Common/NavigationBar/NavigationBar.tsx | 20 ++++++------- frontend/src/components/Common/index.ts | 1 + frontend/src/index.tsx | 2 ++ frontend/src/styles/.gitkeep | 0 frontend/src/styles/index.ts | 14 +++++++++ 7 files changed, 59 insertions(+), 15 deletions(-) create mode 100644 frontend/src/components/Common/Layout/Layout.tsx delete mode 100644 frontend/src/styles/.gitkeep create mode 100644 frontend/src/styles/index.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e6b0da8d..24a2e644 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,13 +1,11 @@ import { Outlet } from 'react-router-dom'; -import { Header, NavigationBar } from './components/Common'; +import { Layout } from './components/Common'; const App = () => ( - <> -
    + - - + ); export default App; diff --git a/frontend/src/components/Common/Layout/Layout.tsx b/frontend/src/components/Common/Layout/Layout.tsx new file mode 100644 index 00000000..8a982ad2 --- /dev/null +++ b/frontend/src/components/Common/Layout/Layout.tsx @@ -0,0 +1,29 @@ +import type { PropsWithChildren } from 'react'; +import styled from 'styled-components'; + +import Header from '../Header/Header'; +import NavigationBar from '../NavigationBar/NavigationBar'; + +const Layout = ({ children }: PropsWithChildren) => { + return ( + +
    + {children} + + + ); +}; + +export default Layout; + +const LayoutContainer = styled.div` + max-width: 600px; + height: 100%; + margin: 0 auto; +`; + +const MainWrapper = styled.main` + height: calc(100% - 120px); + padding: 40px 20px; + overflow-y: auto; +`; diff --git a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx index a800f6db..6ee5a02c 100644 --- a/frontend/src/components/Common/NavigationBar/NavigationBar.tsx +++ b/frontend/src/components/Common/NavigationBar/NavigationBar.tsx @@ -20,14 +20,14 @@ const NavigationBar = () => { {NAVIGATION_MENU.map(({ variant, name, path }) => { const isSelected = selectedMenu === name; return ( - - selectMenu(name)}> + selectMenu(name)}> + {name} - - + + ); })} @@ -38,16 +38,14 @@ const NavigationBar = () => { export default NavigationBar; const NavigationBarContainer = styled.nav` - position: absolute; - bottom: 0; width: 100%; + height: 60px; `; const NavigationBarList = styled.ul` display: flex; align-items: center; justify-content: space-around; - height: 62px; padding-top: 12px; border: 1px solid ${({ theme }) => theme.borderColors.disabled}; border-bottom: none; @@ -56,11 +54,13 @@ const NavigationBarList = styled.ul` `; const NavigationItem = styled.li` + height: 50px; +`; + +const NavigationLink = styled(Link)` display: flex; flex-direction: column; align-items: center; justify-content: flex-end; - gap: 8px; - height: 50px; - cursor: pointer; + gap: 4px; `; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index b74d43c9..862e6058 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -3,3 +3,4 @@ export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; export { default as Header } from './Header/Header'; export { default as NavigationBar } from './NavigationBar/NavigationBar'; +export { default as Layout } from './Layout/Layout'; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 3de26969..12897b89 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -5,6 +5,7 @@ import { RouterProvider } from 'react-router-dom'; import { SvgSprite } from './components/Common'; import router from './router'; +import GlobalStyle from './styles'; const main = async () => { if (process.env.NODE_ENV === 'development') { @@ -19,6 +20,7 @@ root.render( + diff --git a/frontend/src/styles/.gitkeep b/frontend/src/styles/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/styles/index.ts b/frontend/src/styles/index.ts new file mode 100644 index 00000000..56318656 --- /dev/null +++ b/frontend/src/styles/index.ts @@ -0,0 +1,14 @@ +import { createGlobalStyle } from 'styled-components'; + +const GlobalStyle = createGlobalStyle` + #root { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + } +`; + +export default GlobalStyle; From 77865d4c9d5eee1761559f1c2017dad26fce0925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Thu, 20 Jul 2023 13:32:59 +0900 Subject: [PATCH 036/185] =?UTF-8?q?[FE]=20feat:=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20#55=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * style: 북마크 svg 추가 * feat: bookmark 값 추가 * feat: ProductTitle 구현 * style: 사용하지 않는 styled 삭제 후 div로 변경 * refactor: css 중괄호 삭제 * refactor: bookmark props로 변화 * chore: 컴포넌트 폴더 이동 * refactor: text -> heading 컴포넌트로 변경 * feat: 뒤로 가기 기능 추가 * feat: props로 name, bookmark 받아오게 변경 * feat: product index 추가 --- frontend/.storybook/preview-body.html | 14 +++--- .../src/components/Common/Svg/SvgIcon.tsx | 1 + .../src/components/Common/Svg/SvgSprite.tsx | 14 +++--- .../src/components/Common/Title/Title.tsx | 8 ++-- .../ProductRankingItem.stories.tsx | 0 .../ProductRankingItem/ProductRankingItem.tsx | 0 .../ProductRankingList.stories.tsx | 0 .../ProductRankingList/ProductRankingList.tsx | 3 +- .../ProductTitle/ProductTitle.stories.tsx | 25 +++++++++++ .../Product/ProductTitle/ProductTitle.tsx | 44 +++++++++++++++++++ frontend/src/components/Product/index.ts | 6 +++ frontend/src/mocks/data/productDetail.json | 1 + frontend/src/types/product.ts | 1 + 13 files changed, 96 insertions(+), 21 deletions(-) rename frontend/src/components/{ => Product}/ProductRankingItem/ProductRankingItem.stories.tsx (100%) rename frontend/src/components/{ => Product}/ProductRankingItem/ProductRankingItem.tsx (100%) rename frontend/src/components/{ => Product}/ProductRankingList/ProductRankingList.stories.tsx (100%) rename frontend/src/components/{ => Product}/ProductRankingList/ProductRankingList.tsx (84%) create mode 100644 frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx create mode 100644 frontend/src/components/Product/ProductTitle/ProductTitle.tsx create mode 100644 frontend/src/components/Product/index.ts diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index 012fa5ef..6da2c540 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -22,13 +22,13 @@ - - - - - - - + + + + + diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx index 7ebb6760..87d1c7f4 100644 --- a/frontend/src/components/Common/Svg/SvgIcon.tsx +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -8,6 +8,7 @@ export const SVG_ICON_VARIANTS = [ 'search', 'arrow', 'bookmark', + 'bookmarkFilled', 'review', 'star', 'favorite', diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index e618f3e3..027d0124 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -16,15 +16,11 @@ const SvgSprite = () => { - - - - - - - - - + + + + + diff --git a/frontend/src/components/Common/Title/Title.tsx b/frontend/src/components/Common/Title/Title.tsx index 35d8e4c6..48c80cbb 100644 --- a/frontend/src/components/Common/Title/Title.tsx +++ b/frontend/src/components/Common/Title/Title.tsx @@ -1,4 +1,4 @@ -import { Link, Text, theme } from '@fun-eat/design-system'; +import { Link, Heading, theme } from '@fun-eat/design-system'; import { Link as RouterLink } from 'react-router-dom'; import styled from 'styled-components'; @@ -16,16 +16,16 @@ const Title = ({ headingTitle }: TitleProps) => { - + {headingTitle} - + ); }; export default Title; -const TitleContainer = styled.h1` +const TitleContainer = styled.div` display: flex; flex-direction: row; align-items: center; diff --git a/frontend/src/components/ProductRankingItem/ProductRankingItem.stories.tsx b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx similarity index 100% rename from frontend/src/components/ProductRankingItem/ProductRankingItem.stories.tsx rename to frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx diff --git a/frontend/src/components/ProductRankingItem/ProductRankingItem.tsx b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx similarity index 100% rename from frontend/src/components/ProductRankingItem/ProductRankingItem.tsx rename to frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx diff --git a/frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx b/frontend/src/components/Product/ProductRankingList/ProductRankingList.stories.tsx similarity index 100% rename from frontend/src/components/ProductRankingList/ProductRankingList.stories.tsx rename to frontend/src/components/Product/ProductRankingList/ProductRankingList.stories.tsx diff --git a/frontend/src/components/ProductRankingList/ProductRankingList.tsx b/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx similarity index 84% rename from frontend/src/components/ProductRankingList/ProductRankingList.tsx rename to frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx index c8fc8650..42e8d14b 100644 --- a/frontend/src/components/ProductRankingList/ProductRankingList.tsx +++ b/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx @@ -1,6 +1,7 @@ -import productRanking from '../../mocks/data/productRanking.json'; import ProductRankingItem from '../ProductRankingItem/ProductRankingItem'; +import productRanking from '@/mocks/data/productRanking.json'; + const ProductRankingList = () => { return (
      diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx b/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx new file mode 100644 index 00000000..85c8c7f4 --- /dev/null +++ b/frontend/src/components/Product/ProductTitle/ProductTitle.stories.tsx @@ -0,0 +1,25 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductTitle from './ProductTitle'; + +const meta: Meta = { + title: 'product/ProductTitle', + component: ProductTitle, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + name: '사이다', + bookmark: false, + }, +}; + +export const Bookmarked: Story = { + args: { + name: '사이다', + bookmark: true, + }, +}; diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx b/frontend/src/components/Product/ProductTitle/ProductTitle.tsx new file mode 100644 index 00000000..80f88bd9 --- /dev/null +++ b/frontend/src/components/Product/ProductTitle/ProductTitle.tsx @@ -0,0 +1,44 @@ +import { Button, Heading, Link, theme } from '@fun-eat/design-system'; +import { Link as RouterLink } from 'react-router-dom'; +import styled from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; + +interface ProductTitleProps { + name: string; + bookmark: boolean; +} + +const ProductTitle = ({ name, bookmark }: ProductTitleProps) => { + return ( + + + + + + + {name} + + + + + ); +}; + +export default ProductTitle; + +const ProductTitleContainer = styled.div` + display: flex; + align-items: center; + justify-content: space-between; +`; + +const ProductTitleWrapper = styled.div` + display: flex; + align-items: center; +`; diff --git a/frontend/src/components/Product/index.ts b/frontend/src/components/Product/index.ts new file mode 100644 index 00000000..fbcc3e7a --- /dev/null +++ b/frontend/src/components/Product/index.ts @@ -0,0 +1,6 @@ +export { default as ProductDetail } from './ProductDetail/ProductDetail'; +export { default as ProductItem } from './ProductItem/ProductItem'; +export { default as ProductList } from './ProductList/ProductList'; +export { default as ProductRankingItem } from './ProductRankingItem/ProductRankingItem'; +export { default as ProductRankingList } from './ProductRankingList/ProductRankingList'; +export { default as ProductTitle } from './ProductTitle/ProductTitle'; diff --git a/frontend/src/mocks/data/productDetail.json b/frontend/src/mocks/data/productDetail.json index 50864299..7806c04e 100644 --- a/frontend/src/mocks/data/productDetail.json +++ b/frontend/src/mocks/data/productDetail.json @@ -5,6 +5,7 @@ "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", "averageRating": 4.5, + "bookmark": false, "tags": [ { "id": 5, diff --git a/frontend/src/types/product.ts b/frontend/src/types/product.ts index 2bbc5854..034157a0 100644 --- a/frontend/src/types/product.ts +++ b/frontend/src/types/product.ts @@ -16,5 +16,6 @@ export interface ProductDetail { image: string; content: string; averageRating: number; + bookmark: boolean; tags: Tag[]; } From 9041458c68f7cd9f63cb6d6f8496a9a5f71bb308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Thu, 20 Jul 2023 13:35:41 +0900 Subject: [PATCH 037/185] =?UTF-8?q?[FE]=20feat:=20=ED=8E=B8=EC=9D=98?= =?UTF-8?q?=EC=A0=90=20PB=20=EC=83=81=ED=92=88=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=ED=85=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#91)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: pbProduct mock 데이터 및 타입 추가 * feat: PBProductItem 구현 * feat: PBProductList구현 * style: styled 중괄호 삭제 * refactor: 스타일명 변경 * refactor: gap 추가 * refactor: 이미지 고정 크기 부여 --- .../PBProductItem/PBProductItem.stories.tsx | 18 +++++ .../Product/PBProductItem/PBProductItem.tsx | 74 +++++++++++++++++++ .../PBProductList/PBProductList.stories.tsx | 13 ++++ .../Product/PBProductList/PBProductList.tsx | 30 ++++++++ frontend/src/mocks/data/pbProducts.json | 23 ++++++ frontend/src/types/product.ts | 8 ++ 6 files changed, 166 insertions(+) create mode 100644 frontend/src/components/Product/PBProductItem/PBProductItem.stories.tsx create mode 100644 frontend/src/components/Product/PBProductItem/PBProductItem.tsx create mode 100644 frontend/src/components/Product/PBProductList/PBProductList.stories.tsx create mode 100644 frontend/src/components/Product/PBProductList/PBProductList.tsx create mode 100644 frontend/src/mocks/data/pbProducts.json diff --git a/frontend/src/components/Product/PBProductItem/PBProductItem.stories.tsx b/frontend/src/components/Product/PBProductItem/PBProductItem.stories.tsx new file mode 100644 index 00000000..4903f1ec --- /dev/null +++ b/frontend/src/components/Product/PBProductItem/PBProductItem.stories.tsx @@ -0,0 +1,18 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import PBProductItem from './PBProductItem'; + +import pbProducts from '@/mocks/data/pbProducts.json'; + +const meta: Meta = { + title: 'product/PBProductItem', + component: PBProductItem, + args: { + pbProduct: pbProducts[0], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Product/PBProductItem/PBProductItem.tsx b/frontend/src/components/Product/PBProductItem/PBProductItem.tsx new file mode 100644 index 00000000..5ded9463 --- /dev/null +++ b/frontend/src/components/Product/PBProductItem/PBProductItem.tsx @@ -0,0 +1,74 @@ +import { Text, theme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; +import type { PBProduct } from '@/types/product'; + +interface PBProductItemProps { + pbProduct: PBProduct; +} + +const PBProductItem = ({ pbProduct }: PBProductItemProps) => { + const { name, price, image, averageRating } = pbProduct; + + return ( + + + + {name} + + + + + {averageRating} + + + + {price.toLocaleString('ko-KR')}원 + + + + + ); +}; + +export default PBProductItem; + +const PBProductItemContainer = styled.div` + width: 110px; + height: 220px; +`; + +const PBProductImage = styled.img` + object-fit: cover; +`; + +const PBProductInfoWrapper = styled.div` + height: 50%; + margin-top: 10px; +`; + +const PBProductName = styled(Text)` + display: inline-block; + width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +`; + +const PBProductReviewWrapper = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + margin: 5px 0; +`; + +const RatingWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 4px; + + & > svg { + padding-bottom: 2px; + } +`; diff --git a/frontend/src/components/Product/PBProductList/PBProductList.stories.tsx b/frontend/src/components/Product/PBProductList/PBProductList.stories.tsx new file mode 100644 index 00000000..dc1804f0 --- /dev/null +++ b/frontend/src/components/Product/PBProductList/PBProductList.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import PBProductList from './PBProductList'; + +const meta: Meta = { + title: 'product/PBProductList', + component: PBProductList, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Product/PBProductList/PBProductList.tsx b/frontend/src/components/Product/PBProductList/PBProductList.tsx new file mode 100644 index 00000000..ee829ee0 --- /dev/null +++ b/frontend/src/components/Product/PBProductList/PBProductList.tsx @@ -0,0 +1,30 @@ +import styled from 'styled-components'; + +import PBProductItem from '../PBProductItem/PBProductItem'; + +import pbProducts from '@/mocks/data/pbProducts.json'; + +const PBProductList = () => { + return ( + + {pbProducts.map((pbProduct) => ( +
    • + +
    • + ))} +
      + ); +}; + +export default PBProductList; + +const PBProductListContainer = styled.ul` + display: flex; + overflow-x: auto; + overflow-y: hidden; + gap: 40px; + + &::-webkit-scrollbar { + display: none; + } +`; diff --git a/frontend/src/mocks/data/pbProducts.json b/frontend/src/mocks/data/pbProducts.json new file mode 100644 index 00000000..614317e5 --- /dev/null +++ b/frontend/src/mocks/data/pbProducts.json @@ -0,0 +1,23 @@ +[ + { + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg", + "averageRating": 4.5 + }, + { + "id": 2, + "name": "새우깡", + "price": 1000, + "image": "https://cdn.pixabay.com/photo/2016/03/23/15/00/ice-cream-1274894_1280.jpg", + "averageRating": 4.0 + }, + { + "id": 3, + "name": "감자깡", + "price": 1200, + "image": "https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg", + "averageRating": 3.0 + } +] diff --git a/frontend/src/types/product.ts b/frontend/src/types/product.ts index 034157a0..ed7a5cd0 100644 --- a/frontend/src/types/product.ts +++ b/frontend/src/types/product.ts @@ -19,3 +19,11 @@ export interface ProductDetail { bookmark: boolean; tags: Tag[]; } + +export interface PBProduct { + id: number; + name: string; + price: number; + image: string; + averageRating: number; +} From f48effeefc16fde3cc6f09b8faf0e9972f7cc517 Mon Sep 17 00:00:00 2001 From: JFe <33208246+Go-Jaecheol@users.noreply.github.com> Date: Thu, 20 Jul 2023 13:42:23 +0900 Subject: [PATCH 038/185] =?UTF-8?q?[BE]=20refactor:=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EB=B3=80=EC=88=98=EC=97=90=20=EC=96=B8=EB=8D=94=EC=8A=A4?= =?UTF-8?q?=EC=BD=94=EC=96=B4=20=EC=A0=81=EC=9A=A9=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: hanueleee --- backend/src/main/resources/application-dev.yml | 8 ++++---- backend/src/main/resources/application-prod.yml | 8 ++++---- backend/src/main/resources/application.yml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/main/resources/application-dev.yml b/backend/src/main/resources/application-dev.yml index 6e1cd8bb..80393be8 100644 --- a/backend/src/main/resources/application-dev.yml +++ b/backend/src/main/resources/application-dev.yml @@ -1,9 +1,9 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: { DEV-DB-URL } - username: { DEV-DB-USERNAME } - password: { DEV-DB-PASSWORD } + url: { DEV_DB_URL } + username: { DEV_DB_USERNAME } + password: { DEV_DB_PASSWORD } jpa: hibernate: @@ -15,4 +15,4 @@ spring: review: image: - path: { DEV-IMAGE-PATH } + path: { DEV_IMAGE_PATH } diff --git a/backend/src/main/resources/application-prod.yml b/backend/src/main/resources/application-prod.yml index ef0a53cc..1aa7a680 100644 --- a/backend/src/main/resources/application-prod.yml +++ b/backend/src/main/resources/application-prod.yml @@ -1,9 +1,9 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: { PROD-DB-URL } - username: { PROD-DB-USERNAME } - password: { PROD-DB-PASSWORD } + url: { PROD_DB_URL } + username: { PROD_DB_USERNAME } + password: { PROD_DB_PASSWORD } jpa: hibernate: @@ -14,4 +14,4 @@ spring: review: image: - path: { PROD-IMAGE-PATH } + path: { PROD_IMAGE_PATH } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 0a9d2385..97e5c0cf 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: profiles: - active: { DEPLOY-ACTIVE:dev } + active: { DEPLOY_ACTIVE:dev } From 62ef6c2a869aaa312084ff09fb65b5e8ce643bd8 Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Thu, 20 Jul 2023 20:21:23 +0900 Subject: [PATCH 039/185] =?UTF-8?q?[FE]=20feat:=20ReviewItem=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#75)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 리뷰 목데이터 작성 및 타입 추가 * feat: ReviewItem 컴포넌트 스토리 작성 * feat: ReviewItem 컴포넌트 추가 * feat: common 컴포넌트 index에 TagList 추가 * refactor: 불필요한 prop 삭제 * feat: 좋아요 라인 아이콘 추가 * feat: 좋아요 클릭 안 한 경우 라인 아이콘으로 표시 * feat: review 컴포넌트 index에 추가 * refactor: 별점을 별 이미지로 수정 * refactor: 좋아요 버튼으로 수정 * refactor: 스타일 컴포넌트 변수 삭제 및 추가 * feat: 이미지 없는 경우 타입 추가 및 스토리 작성 * feat: 스타일 수정 및 데이터 수정 --- frontend/.storybook/preview-body.html | 15 ++- .../src/components/Common/Svg/SvgIcon.tsx | 1 + .../src/components/Common/Svg/SvgSprite.tsx | 9 +- frontend/src/components/Common/index.ts | 1 + .../Review/ReviewItem/ReviewItem.stories.tsx | 37 ++++++ .../Review/ReviewItem/ReviewItem.tsx | 105 ++++++++++++++++++ frontend/src/components/Review/index.ts | 1 + frontend/src/mocks/data/reviews.json | 56 ++++++++++ frontend/src/types/review.ts | 14 +++ 9 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx create mode 100644 frontend/src/components/Review/ReviewItem/ReviewItem.tsx create mode 100644 frontend/src/mocks/data/reviews.json create mode 100644 frontend/src/types/review.ts diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html index 6da2c540..257e2139 100644 --- a/frontend/.storybook/preview-body.html +++ b/frontend/.storybook/preview-body.html @@ -53,11 +53,6 @@ d="M6.39 7.812l-.014 4.205 1.416.004.014-4.205 2.126.006L7.106 5.42 4.264 7.806l2.126.006zm-.697-3.589l-2.125-.006.014-4.205L2.165.01l-.014 4.205-2.125-.006 2.826 2.4 2.841-2.385z" /> - - - @@ -66,5 +61,15 @@ d="M6.39 7.812l-.014 4.205 1.416.004.014-4.205 2.126.006L7.106 5.42 4.264 7.806l2.126.006zm-.697-3.589l-2.125-.006.014-4.205L2.165.01l-.014 4.205-2.125-.006 2.826 2.4 2.841-2.385z" /> + + + + + + diff --git a/frontend/src/components/Common/Svg/SvgIcon.tsx b/frontend/src/components/Common/Svg/SvgIcon.tsx index 87d1c7f4..64d7b292 100644 --- a/frontend/src/components/Common/Svg/SvgIcon.tsx +++ b/frontend/src/components/Common/Svg/SvgIcon.tsx @@ -12,6 +12,7 @@ export const SVG_ICON_VARIANTS = [ 'review', 'star', 'favorite', + 'favoriteFilled', 'home', 'sort', ] as const; diff --git a/frontend/src/components/Common/Svg/SvgSprite.tsx b/frontend/src/components/Common/Svg/SvgSprite.tsx index 027d0124..559ecd17 100644 --- a/frontend/src/components/Common/Svg/SvgSprite.tsx +++ b/frontend/src/components/Common/Svg/SvgSprite.tsx @@ -42,12 +42,15 @@ const SvgSprite = () => { - - - + + + + + + ); }; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index 862e6058..137c5828 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -1,6 +1,7 @@ export { default as CategoryMenu } from './CategoryMenu/CategoryMenu'; export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; +export { default as TagList } from './TagList/TagList'; export { default as Header } from './Header/Header'; export { default as NavigationBar } from './NavigationBar/NavigationBar'; export { default as Layout } from './Layout/Layout'; diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx b/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx new file mode 100644 index 00000000..30061b2a --- /dev/null +++ b/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx @@ -0,0 +1,37 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ReviewItem from './ReviewItem'; + +import reviews from '@/mocks/data/reviews.json'; + +const meta: Meta = { + title: 'review/ReviewItem', + component: ReviewItem, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( +
      + +
      + ), +}; + +export const RebuyAndFavorite: Story = { + render: () => ( +
      + +
      + ), +}; + +export const NoImageReview: Story = { + render: () => ( +
      + +
      + ), +}; diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.tsx b/frontend/src/components/Review/ReviewItem/ReviewItem.tsx new file mode 100644 index 00000000..a1692aed --- /dev/null +++ b/frontend/src/components/Review/ReviewItem/ReviewItem.tsx @@ -0,0 +1,105 @@ +import { Badge, Button, Text, useTheme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SvgIcon, TagList } from '@/components/Common'; +import type { Review } from '@/types/review'; + +interface ReviewItemProps { + review: Review; +} + +const ReviewItem = ({ review }: ReviewItemProps) => { + const { userName, profileImage, image, rating, tags, content, rebuy, favoriteCount, favorite } = review; + const theme = useTheme(); + + return ( + + + + +
      + {userName} + + {Array.from({ length: 5 }, (_, index) => ( + + ))} + +
      +
      + {rebuy && ( + + 😝 또 살래요 + + )} +
      + {image !== null && } + + {content} + + + + {favoriteCount} + + +
      + ); +}; + +export default ReviewItem; + +const ReviewItemContainer = styled.div` + display: flex; + flex-direction: column; + row-gap: 20px; +`; + +const ReviewerWrapper = styled.div` + display: flex; + align-items: center; + justify-content: space-between; +`; + +const ReviewerInfoWrapper = styled.div` + display: flex; + align-items: center; + column-gap: 10px; +`; + +const ReviewerImage = styled.img` + border-radius: 50%; + border: 2px solid ${({ theme }) => theme.colors.primary}; +`; + +const RatingIconWrapper = styled.div` + display: flex; + align-items: center; + margin-left: -2px; +`; + +const ReviewImage = styled.img` + align-self: center; +`; + +const FavoriteButton = styled(Button)` + display: flex; + align-items: center; + column-gap: 8px; + padding: 0; +`; diff --git a/frontend/src/components/Review/index.ts b/frontend/src/components/Review/index.ts index 01f661af..73effc54 100644 --- a/frontend/src/components/Review/index.ts +++ b/frontend/src/components/Review/index.ts @@ -1,2 +1,3 @@ export { default as ReviewRankingItem } from './ReviewRankingItem/ReviewRankingItem'; export { default as ReviewRankingList } from './ReviewRankingList/ReviewRankingList'; +export { default as ReviewItem } from './ReviewItem/ReviewItem'; diff --git a/frontend/src/mocks/data/reviews.json b/frontend/src/mocks/data/reviews.json new file mode 100644 index 00000000..05b68dff --- /dev/null +++ b/frontend/src/mocks/data/reviews.json @@ -0,0 +1,56 @@ +[ + { + "id": 1, + "userName": "우가우가", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "rating": 5, + "tags": [], + "content": "우가우가~!~!", + "rebuy": false, + "favoriteCount": 150, + "favorite": false + }, + { + "id": 2, + "userName": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "rating": 4, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + } + ], + "content": "할머니가 먹을 거 같은 맛입니다.\n\n1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "rebuy": true, + "favoriteCount": 1320, + "favorite": true + }, + { + "id": 2, + "userName": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": null, + "rating": 4, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + } + ], + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요. 하얀 짜파게티라니 말이 안된다고 생각했었죠. 실제로 맛을 보니까 까만 짜파게티랑 맛이 뭔가 다를게 없네요.", + "rebuy": true, + "favoriteCount": 1320, + "favorite": true + } +] diff --git a/frontend/src/types/review.ts b/frontend/src/types/review.ts new file mode 100644 index 00000000..cef8f0e2 --- /dev/null +++ b/frontend/src/types/review.ts @@ -0,0 +1,14 @@ +import type { Tag } from './common'; + +export interface Review { + id: number; + userName: string; + profileImage: string; + image: string | null; + rating: number; + tags: Tag[]; + content: string; + rebuy: boolean; + favoriteCount: number; + favorite: boolean; +} From 029146fc6696ff1e829c1389adfd4c262d9c0516 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Thu, 20 Jul 2023 20:46:30 +0900 Subject: [PATCH 040/185] =?UTF-8?q?[FE]=20feat:=20Home=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A7=88=ED=81=AC=EC=97=85=20(#97)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Layout padding 수정 * feat: PBProductItem height 제거 * feat: 홈화면에 보여지는 item 들의 width, height 수정 * refactor: 네비게이션바 컴포넌트에서 isSelected를 useLocation의 pathname으로 변경 * chore: 디자인시스템 버전업 * fix: 바뀐 디자인시스템에 따라 속성 변경 * feat: ProductItem 텍스트 크기 조정 * feat: 홈페이지 마크업 * feat: 카테고리 메뉴 버튼 border 추가 * feat: 카테고리 데이터 constants->mocks로 이동 * style: 컨벤션 맞춰서 import 정리 --- frontend/package.json | 2 +- .../Common/CategoryMenu/CategoryMenu.tsx | 12 ++-- .../src/components/Common/Layout/Layout.tsx | 2 +- .../Common/NavigationBar/NavigationBar.tsx | 13 ++-- .../Common/SortOptionList/SortOptionList.tsx | 2 +- .../Product/PBProductItem/PBProductItem.tsx | 1 - .../Product/ProductItem/ProductItem.tsx | 11 ++-- .../ProductRankingItem/ProductRankingItem.tsx | 1 - frontend/src/components/Product/index.ts | 1 + .../ReviewRankingItem/ReviewRankingItem.tsx | 1 - frontend/src/mocks/data/foodCategory.json | 6 ++ frontend/src/mocks/data/storeCategory.json | 5 ++ frontend/src/pages/HomePage.tsx | 62 ++++++++++++++++++- frontend/yarn.lock | 8 +-- 14 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 frontend/src/mocks/data/foodCategory.json create mode 100644 frontend/src/mocks/data/storeCategory.json diff --git a/frontend/package.json b/frontend/package.json index 9b939ffc..4a339e52 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { - "@fun-eat/design-system": "^0.2.1", + "@fun-eat/design-system": "^0.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.2", diff --git a/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx b/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx index 460c6334..2912cf00 100644 --- a/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx +++ b/frontend/src/components/Common/CategoryMenu/CategoryMenu.tsx @@ -25,15 +25,15 @@ const CategoryMenu = ({ menuList, menuVariant }: CategoryMenuProps) => {
    • ); @@ -61,7 +61,3 @@ const selectedCategoryMenuStyles: Record { - const [selectedMenu, setSelectedMenu] = useState('홈'); - - const selectMenu = (name: string) => { - setSelectedMenu(name); - }; + const location = useLocation(); return ( {NAVIGATION_MENU.map(({ variant, name, path }) => { - const isSelected = selectedMenu === name; + const isSelected = location.pathname === path; return ( - selectMenu(name)}> + diff --git a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx b/frontend/src/components/Common/SortOptionList/SortOptionList.tsx index 56e3476f..20e3ad2c 100644 --- a/frontend/src/components/Common/SortOptionList/SortOptionList.tsx +++ b/frontend/src/components/Common/SortOptionList/SortOptionList.tsx @@ -29,7 +29,7 @@ const SortOptionList = ({ selectedOption, selectSortOption, close }: SortOptionL > { {name} - {price.toLocaleString('ko-KR')}원 + + {price.toLocaleString('ko-KR')}원 + - + {averageRating} - + {reviewCount} @@ -44,8 +46,7 @@ export default ProductItem; const ProductItemContainer = styled.div` display: flex; align-items: center; - height: 150px; - padding: 30px; + padding: 12px 0; `; const ProductInfoWrapper = styled.div` diff --git a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx index 9eb108f0..650a6751 100644 --- a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx +++ b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx @@ -28,7 +28,6 @@ export default ProductRankingItem; const ProductRankingContainer = styled.div` display: flex; align-items: center; - width: 300px; height: 50px; margin-bottom: 15px; padding-left: 15px; diff --git a/frontend/src/components/Product/index.ts b/frontend/src/components/Product/index.ts index fbcc3e7a..c697738c 100644 --- a/frontend/src/components/Product/index.ts +++ b/frontend/src/components/Product/index.ts @@ -4,3 +4,4 @@ export { default as ProductList } from './ProductList/ProductList'; export { default as ProductRankingItem } from './ProductRankingItem/ProductRankingItem'; export { default as ProductRankingList } from './ProductRankingList/ProductRankingList'; export { default as ProductTitle } from './ProductTitle/ProductTitle'; +export { default as PBProductList } from './PBProductList/PBProductList'; diff --git a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx index 993151df..a54b03a9 100644 --- a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx +++ b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx @@ -43,7 +43,6 @@ export default ReviewRankingItem; const ReviewRankingItemContainer = styled.div` display: flex; flex-direction: column; - width: 315px; padding: 12px; gap: 4px; border: 1px solid ${({ theme }) => theme.borderColors.disabled}; diff --git a/frontend/src/mocks/data/foodCategory.json b/frontend/src/mocks/data/foodCategory.json new file mode 100644 index 00000000..cce54014 --- /dev/null +++ b/frontend/src/mocks/data/foodCategory.json @@ -0,0 +1,6 @@ +[ + { "id": 0, "name": "즉석조리" }, + { "id": 1, "name": "과자" }, + { "id": 2, "name": "간편식사" }, + { "id": 3, "name": "아이스크림" } +] diff --git a/frontend/src/mocks/data/storeCategory.json b/frontend/src/mocks/data/storeCategory.json new file mode 100644 index 00000000..b478ac5a --- /dev/null +++ b/frontend/src/mocks/data/storeCategory.json @@ -0,0 +1,5 @@ +[ + { "id": 0, "name": "CU" }, + { "id": 1, "name": "GS25" }, + { "id": 2, "name": "이마트24" } +] diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index f5ed3ffa..61db5faf 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -1,5 +1,65 @@ +import { Heading, Link, Spacing } from '@fun-eat/design-system'; +import { Link as RouterLink } from 'react-router-dom'; +import styled from 'styled-components'; + +import { CategoryMenu } from '@/components/Common'; +import { PBProductList, ProductList, ProductRankingList } from '@/components/Product'; +import { ReviewRankingList } from '@/components/Review'; +import { PATH } from '@/constants/path'; +import foodCategory from '@/mocks/data/foodCategory.json'; +import storeCategory from '@/mocks/data/storeCategory.json'; + const HomePage = () => { - return <>홈; + return ( + <> +
      + + 공통 상품 + + + + + + + 전체 보기 + +
      + +
      + + 편의점 특산품 + + + + + +
      + +
      + + 👑 랭킹 + + + +
      + +
      + + 리뷰 랭킹 + + + +
      + + ); }; export default HomePage; + +const ProductListRouteButton = styled(Link)` + display: block; + width: 100%; + padding: 12px 0; + text-align: center; + border-bottom: 1px solid ${({ theme }) => theme.borderColors.disabled}; +`; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d4654d48..cebdb29a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1565,10 +1565,10 @@ resolved "https://registry.yarnpkg.com/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz#c05ed35ad82df8e6ac616c68b92c2282bd083ba4" integrity sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ== -"@fun-eat/design-system@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.2.1.tgz#56e2a581adc773cb09bd85fb9f381e959dea14da" - integrity sha512-1bLET1X8s0yfsl4OsSzsQjJ0lWLPdNS2ZelGgsxN/X9BNrqL7w5x0jG0QSjz3OmU00WDzH0psVSUflwmugviNw== +"@fun-eat/design-system@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@fun-eat/design-system/-/design-system-0.2.2.tgz#a0c6803cf42570b72ab56263c9e086e83e0a6c33" + integrity sha512-8xWLDUbps0nuJoCR4RgmaRaUrHkOOe4Yr2UZBDiDKgxZpCp8TzpDI9ZHO7Q96KKEgf2AUxtGJvwxamONcHJ3+Q== "@humanwhocodes/config-array@^0.11.10": version "0.11.10" From e30549d60777a83e25437b963014203332a79a21 Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Fri, 21 Jul 2023 00:07:17 +0900 Subject: [PATCH 041/185] =?UTF-8?q?[FE]=20feat:=20svg=20icon=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 전체보기 텍스트 옆 icon 추가 * feat: favorite -> favoriteFilled로 아이콘 수정 --- .../Review/ReviewRankingItem/ReviewRankingItem.tsx | 2 +- frontend/src/pages/HomePage.tsx | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx index a54b03a9..d0aee0ee 100644 --- a/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx +++ b/frontend/src/components/Review/ReviewRankingItem/ReviewRankingItem.tsx @@ -22,7 +22,7 @@ const ReviewRankingItem = ({ reviewRanking }: ReviewRankingItemProps) => { - + {favoriteCount} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 61db5faf..e4819fa7 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -2,7 +2,7 @@ import { Heading, Link, Spacing } from '@fun-eat/design-system'; import { Link as RouterLink } from 'react-router-dom'; import styled from 'styled-components'; -import { CategoryMenu } from '@/components/Common'; +import { CategoryMenu, SvgIcon } from '@/components/Common'; import { PBProductList, ProductList, ProductRankingList } from '@/components/Product'; import { ReviewRankingList } from '@/components/Review'; import { PATH } from '@/constants/path'; @@ -21,7 +21,7 @@ const HomePage = () => { - 전체 보기 + 전체 보기 @@ -57,9 +57,16 @@ const HomePage = () => { export default HomePage; const ProductListRouteButton = styled(Link)` - display: block; + display: flex; + align-items: center; + justify-content: center; width: 100%; padding: 12px 0; text-align: center; border-bottom: 1px solid ${({ theme }) => theme.borderColors.disabled}; + + svg { + margin-left: 4px; + transform: rotate(180deg); + } `; From 206a61fcbc5e7d351f755879b0809dc7c671b960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Fri, 21 Jul 2023 09:43:18 +0900 Subject: [PATCH 042/185] =?UTF-8?q?[FE]=20feat:=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=A7=88=ED=81=AC=EC=97=85=20(#101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: product list path 수정 * refactor: 전체 크기 수정 * refactor: productList 파일 뒤에 page 붙여 이름 변경 * feat: ProductListPage 구현 * chore: common index 작성 * chore: gitkeep 파일 삭제 --- .../src/components/Common/Title/Title.tsx | 6 ++-- frontend/src/components/Common/index.ts | 10 +++++-- frontend/src/constants/path.ts | 2 +- frontend/src/hooks/.gitkeep | 0 frontend/src/pages/ProductList.tsx | 5 ---- frontend/src/pages/ProductListPage.tsx | 29 +++++++++++++++++++ frontend/src/router/index.tsx | 2 +- 7 files changed, 41 insertions(+), 13 deletions(-) delete mode 100644 frontend/src/hooks/.gitkeep delete mode 100644 frontend/src/pages/ProductList.tsx create mode 100644 frontend/src/pages/ProductListPage.tsx diff --git a/frontend/src/components/Common/Title/Title.tsx b/frontend/src/components/Common/Title/Title.tsx index 48c80cbb..889fef27 100644 --- a/frontend/src/components/Common/Title/Title.tsx +++ b/frontend/src/components/Common/Title/Title.tsx @@ -13,10 +13,10 @@ const Title = ({ headingTitle }: TitleProps) => { - + - + {headingTitle} @@ -28,12 +28,12 @@ export default Title; const TitleContainer = styled.div` display: flex; flex-direction: row; - align-items: center; justify-content: center; position: relative; `; const SvgIconWrapper = styled.button` position: absolute; + top: 8px; left: 0; `; diff --git a/frontend/src/components/Common/index.ts b/frontend/src/components/Common/index.ts index 137c5828..ab2f3e0f 100644 --- a/frontend/src/components/Common/index.ts +++ b/frontend/src/components/Common/index.ts @@ -1,7 +1,11 @@ export { default as CategoryMenu } from './CategoryMenu/CategoryMenu'; +export { default as Header } from './Header/Header'; +export { default as Layout } from './Layout/Layout'; +export { default as NavigationBar } from './NavigationBar/NavigationBar'; +export { default as SortButton } from './SortButton/SortButton'; +export { default as SortOptionList } from './SortOptionList/SortOptionList'; export { default as SvgSprite } from './Svg/SvgSprite'; export { default as SvgIcon } from './Svg/SvgIcon'; +export { default as TabMenu } from './TabMenu/TabMenu'; export { default as TagList } from './TagList/TagList'; -export { default as Header } from './Header/Header'; -export { default as NavigationBar } from './NavigationBar/NavigationBar'; -export { default as Layout } from './Layout/Layout'; +export { default as Title } from './Title/Title'; diff --git a/frontend/src/constants/path.ts b/frontend/src/constants/path.ts index e3733ba2..3dc87bca 100644 --- a/frontend/src/constants/path.ts +++ b/frontend/src/constants/path.ts @@ -1,7 +1,7 @@ export const PATH = { HOME: '/', SEARCH: '/search', - PRODUCT_LIST: '/product-list', + PRODUCT_LIST: '/products', PROFILE: '/profile', RECIPE: '/recipe', } as const; diff --git a/frontend/src/hooks/.gitkeep b/frontend/src/hooks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/pages/ProductList.tsx b/frontend/src/pages/ProductList.tsx deleted file mode 100644 index a5ca3867..00000000 --- a/frontend/src/pages/ProductList.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const ProductListPage = () => { - return <>; -}; - -export default ProductListPage; diff --git a/frontend/src/pages/ProductListPage.tsx b/frontend/src/pages/ProductListPage.tsx new file mode 100644 index 00000000..82181f53 --- /dev/null +++ b/frontend/src/pages/ProductListPage.tsx @@ -0,0 +1,29 @@ +import { Spacing } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { CategoryMenu } from '@/components/Common'; +import SortButton from '@/components/Common/SortButton/SortButton'; +import Title from '@/components/Common/Title/Title'; +import { ProductList } from '@/components/Product'; +import foodCategory from '@/mocks/data/foodCategory.json'; + +const ProductListPage = () => { + return ( +
      + + <Spacing size={30} /> + <CategoryMenu menuList={foodCategory} menuVariant="food" /> + <SortButtonWrapper> + <SortButton /> + </SortButtonWrapper> + <ProductList /> + </section> + ); +}; + +export default ProductListPage; + +const SortButtonWrapper = styled.div` + display: flex; + justify-content: flex-end; +`; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 889720f0..a250812c 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -3,7 +3,7 @@ import { createBrowserRouter } from 'react-router-dom'; import App from '@/App'; import { PATH } from '@/constants/path'; import HomePage from '@/pages/HomePage'; -import ProductListPage from '@/pages/ProductList'; +import ProductListPage from '@/pages/ProductListPage'; import ProfilePage from '@/pages/ProfilePage'; import RecipePage from '@/pages/RecipePage'; import SearchPage from '@/pages/SearchPage'; From 11c659ae271ca6631a1aee7a7ed5e1afd597c6f6 Mon Sep 17 00:00:00 2001 From: Leejin Yang <ejin1996@gmail.com> Date: Fri, 21 Jul 2023 10:02:20 +0900 Subject: [PATCH 043/185] =?UTF-8?q?[FE]=20feat:=20=EC=83=81=ED=92=88=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A7=88?= =?UTF-8?q?=ED=81=AC=EC=97=85=20(#102)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ProductDetailPage 추가 * style: ProductDetail 이름 ProductDetailItem으로 수정 * feat: ProductTitle, 상세 페이지 간격 수정 * feat: 상품 클릭 시 상세 페이지 이동 구현 * feat: 상품 상세 데이터 추가 및 해당 상품에 맞게 데이터 렌더 추가 * feat: common index에 SortButton, TabMenu 추가 * refactor: 상세 페이지 스타일 수정 및 태그 수정 --- .../Common/TagList/TagList.stories.tsx | 4 +- .../ProductDetail/ProductDetail.stories.tsx | 19 ------- .../ProductDetailItem.stories.tsx | 24 ++++++++ .../ProductDetailItem.tsx} | 12 ++-- .../Product/ProductList/ProductList.tsx | 19 ++++++- .../Product/ProductTitle/ProductTitle.tsx | 5 ++ frontend/src/components/Product/index.ts | 2 +- frontend/src/mocks/data/productDetail.json | 23 -------- frontend/src/mocks/data/productDetails.json | 40 +++++++++++++ frontend/src/pages/ProductDetailPage.tsx | 57 +++++++++++++++++++ frontend/src/router/index.tsx | 5 ++ 11 files changed, 160 insertions(+), 50 deletions(-) delete mode 100644 frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx create mode 100644 frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx rename frontend/src/components/Product/{ProductDetail/ProductDetail.tsx => ProductDetailItem/ProductDetailItem.tsx} (88%) delete mode 100644 frontend/src/mocks/data/productDetail.json create mode 100644 frontend/src/mocks/data/productDetails.json create mode 100644 frontend/src/pages/ProductDetailPage.tsx diff --git a/frontend/src/components/Common/TagList/TagList.stories.tsx b/frontend/src/components/Common/TagList/TagList.stories.tsx index b78050bf..4ac76785 100644 --- a/frontend/src/components/Common/TagList/TagList.stories.tsx +++ b/frontend/src/components/Common/TagList/TagList.stories.tsx @@ -2,13 +2,13 @@ import type { Meta, StoryObj } from '@storybook/react'; import TagList from './TagList'; -import productDetail from '@/mocks/data/productDetail.json'; +import productDetails from '@/mocks/data/productDetails.json'; const meta: Meta<typeof TagList> = { title: 'common/TagList', component: TagList, args: { - tags: productDetail.tags, + tags: productDetails[0].tags, }, }; diff --git a/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx b/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx deleted file mode 100644 index ccc4f767..00000000 --- a/frontend/src/components/Product/ProductDetail/ProductDetail.stories.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import ProductDetail from './ProductDetail'; - -const meta: Meta<typeof ProductDetail> = { - title: 'product/ProductDetail', - component: ProductDetail, -}; - -export default meta; -type Story = StoryObj<typeof ProductDetail>; - -export const Default: Story = { - render: () => ( - <div style={{ width: '375px', padding: '0 20px' }}> - <ProductDetail /> - </div> - ), -}; diff --git a/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx new file mode 100644 index 00000000..921bb536 --- /dev/null +++ b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductDetailItem from './ProductDetailItem'; + +import productDetails from '@/mocks/data/productDetails.json'; + +const meta: Meta<typeof ProductDetailItem> = { + title: 'product/ProductDetailItem', + component: ProductDetailItem, + args: { + product: productDetails[0], + }, +}; + +export default meta; +type Story = StoryObj<typeof ProductDetailItem>; + +export const Default: Story = { + render: ({ ...args }) => ( + <div style={{ width: '375px', padding: '0 20px' }}> + <ProductDetailItem {...args} /> + </div> + ), +}; diff --git a/frontend/src/components/Product/ProductDetail/ProductDetail.tsx b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx similarity index 88% rename from frontend/src/components/Product/ProductDetail/ProductDetail.tsx rename to frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx index 0672f934..e8c26b7e 100644 --- a/frontend/src/components/Product/ProductDetail/ProductDetail.tsx +++ b/frontend/src/components/Product/ProductDetailItem/ProductDetailItem.tsx @@ -3,10 +3,14 @@ import styled, { useTheme } from 'styled-components'; import { SvgIcon } from '@/components/Common'; import TagList from '@/components/Common/TagList/TagList'; -import productDetail from '@/mocks/data/productDetail.json'; +import type { ProductDetail } from '@/types/product'; -const ProductDetail = () => { - const { name, price, image, content, averageRating, tags } = productDetail; +interface ProductDetailProps { + product: ProductDetail; +} + +const ProductDetailItem = ({ product }: ProductDetailProps) => { + const { name, price, image, content, averageRating, tags } = product; const theme = useTheme(); return ( @@ -34,7 +38,7 @@ const ProductDetail = () => { ); }; -export default ProductDetail; +export default ProductDetailItem; const ProductDetailContainer = styled.div` display: flex; diff --git a/frontend/src/components/Product/ProductList/ProductList.tsx b/frontend/src/components/Product/ProductList/ProductList.tsx index d96ac830..4be8069a 100644 --- a/frontend/src/components/Product/ProductList/ProductList.tsx +++ b/frontend/src/components/Product/ProductList/ProductList.tsx @@ -1,15 +1,26 @@ +import { Button } from '@fun-eat/design-system'; +import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; import ProductItem from '../ProductItem/ProductItem'; +import { PATH } from '@/constants/path'; import mockProducts from '@/mocks/data/products.json'; const ProductList = () => { + const navigate = useNavigate(); + + const navigateToDetailPage = (productId: number) => { + navigate(`${PATH.PRODUCT_LIST}/${productId}`); + }; + return ( <ProductListContainer> {mockProducts.map((product) => ( <li key={product.id}> - <ProductItem product={product} /> + <ProductButton type="button" variant="filled" color="white" onClick={() => navigateToDetailPage(product.id)}> + <ProductItem product={product} /> + </ProductButton> </li> ))} </ProductListContainer> @@ -26,3 +37,9 @@ const ProductListContainer = styled.ul` border-bottom: 1px solid ${({ theme }) => theme.borderColors.disabled}; } `; + +const ProductButton = styled(Button)` + padding: 0; + width: 100%; + height: 100%; +`; diff --git a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx b/frontend/src/components/Product/ProductTitle/ProductTitle.tsx index 80f88bd9..56617aa7 100644 --- a/frontend/src/components/Product/ProductTitle/ProductTitle.tsx +++ b/frontend/src/components/Product/ProductTitle/ProductTitle.tsx @@ -41,4 +41,9 @@ const ProductTitleContainer = styled.div` const ProductTitleWrapper = styled.div` display: flex; align-items: center; + column-gap: 8px; + + svg { + padding-top: 2px; + } `; diff --git a/frontend/src/components/Product/index.ts b/frontend/src/components/Product/index.ts index c697738c..d1dad1e0 100644 --- a/frontend/src/components/Product/index.ts +++ b/frontend/src/components/Product/index.ts @@ -1,4 +1,4 @@ -export { default as ProductDetail } from './ProductDetail/ProductDetail'; +export { default as ProductDetailItem } from './ProductDetailItem/ProductDetailItem'; export { default as ProductItem } from './ProductItem/ProductItem'; export { default as ProductList } from './ProductList/ProductList'; export { default as ProductRankingItem } from './ProductRankingItem/ProductRankingItem'; diff --git a/frontend/src/mocks/data/productDetail.json b/frontend/src/mocks/data/productDetail.json deleted file mode 100644 index 7806c04e..00000000 --- a/frontend/src/mocks/data/productDetail.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "id": 1, - "name": "꼬북칩", - "price": 1500, - "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", - "averageRating": 4.5, - "bookmark": false, - "tags": [ - { - "id": 5, - "name": "단짠단짠" - }, - { - "id": 1, - "name": "망고망고" - }, - { - "id": 3, - "name": "맵찔이는 안돼요" - } - ] -} diff --git a/frontend/src/mocks/data/productDetails.json b/frontend/src/mocks/data/productDetails.json new file mode 100644 index 00000000..10190fa5 --- /dev/null +++ b/frontend/src/mocks/data/productDetails.json @@ -0,0 +1,40 @@ +[ + { + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "averageRating": 4.5, + "bookmark": false, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + }, + { + "id": 3, + "name": "맵찔이는 안돼요" + } + ] + }, + { + "id": 2, + "name": "새우깡", + "price": 1000, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "averageRating": 4.0, + "bookmark": true, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + } + ] + } +] diff --git a/frontend/src/pages/ProductDetailPage.tsx b/frontend/src/pages/ProductDetailPage.tsx new file mode 100644 index 00000000..d82eb8aa --- /dev/null +++ b/frontend/src/pages/ProductDetailPage.tsx @@ -0,0 +1,57 @@ +import { Spacing } from '@fun-eat/design-system'; +import { useParams } from 'react-router-dom'; +import styled from 'styled-components'; + +import { SortButton, TabMenu } from '@/components/Common'; +import { ProductDetailItem, ProductTitle } from '@/components/Product'; +import { ReviewItem } from '@/components/Review'; +import productDetails from '@/mocks/data/productDetails.json'; +import reviews from '@/mocks/data/reviews.json'; + +const ProductDetailPage = () => { + const { productId } = useParams(); + + // TODO: productId param으로 api 요청 보내면 바뀔 로직 + const targetProductDetail = + productDetails.find((productDetail) => productDetail.id === Number(productId)) ?? productDetails[0]; + + return ( + <> + <ProductTitle name={targetProductDetail.name} bookmark={targetProductDetail?.bookmark} /> + <Spacing size={36} /> + <ProductDetailItem product={targetProductDetail} /> + <Spacing size={36} /> + <TabMenu tabMenus={[`리뷰 ${reviews.length}`, '꿀조합']} /> + <Spacing size={8} /> + <SortButtonWrapper> + <SortButton /> + </SortButtonWrapper> + <Spacing size={8} /> + <section> + {reviews && ( + <ReviewItemWrapper> + {reviews.map((review) => ( + <li key={review.id}> + <ReviewItem review={review} /> + </li> + ))} + </ReviewItemWrapper> + )} + </section> + </> + ); +}; + +export default ProductDetailPage; + +const SortButtonWrapper = styled.div` + display: flex; + align-items: center; + justify-content: flex-end; +`; + +const ReviewItemWrapper = styled.ul` + display: flex; + flex-direction: column; + row-gap: 60px; +`; diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index a250812c..0a364765 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -3,6 +3,7 @@ import { createBrowserRouter } from 'react-router-dom'; import App from '@/App'; import { PATH } from '@/constants/path'; import HomePage from '@/pages/HomePage'; +import ProductDetailPage from '@/pages/ProductDetailPage'; import ProductListPage from '@/pages/ProductListPage'; import ProfilePage from '@/pages/ProfilePage'; import RecipePage from '@/pages/RecipePage'; @@ -21,6 +22,10 @@ const router = createBrowserRouter([ path: PATH.PRODUCT_LIST, element: <ProductListPage />, }, + { + path: `${PATH.PRODUCT_LIST}/:productId`, + element: <ProductDetailPage />, + }, { path: PATH.RECIPE, element: <RecipePage />, From e4b63fc9dc8acbfe66d8fe514de87c913fd9047f Mon Sep 17 00:00:00 2001 From: Leejin Yang <ejin1996@gmail.com> Date: Mon, 24 Jul 2023 19:40:34 +0900 Subject: [PATCH 044/185] =?UTF-8?q?[FE]=20feat:=20=EC=83=81=ED=92=88,=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EB=AA=A9=20=ED=95=B8=EB=93=A4=EB=9F=AC=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80=20(#115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 상품 관련 요청 핸들러 작성 * refactor: 상품 정렬 옵션 타입 네이밍 수정 및 유틸 함수 분리 * feat: 리뷰 관련 요청 핸들러 작성 * feat: worker에 리뷰 요청 핸들러 추가 * refactor: pb -> store로 네이밍 변경 --- .../Product/ProductList/ProductList.tsx | 3 +- frontend/src/mocks/browser.ts | 4 +- frontend/src/mocks/data/foodCategory.json | 4 +- frontend/src/mocks/data/productDetail.json | 23 ++++ frontend/src/mocks/data/products.json | 44 ++++--- frontend/src/mocks/data/reviews.json | 120 ++++++++++-------- frontend/src/mocks/handlers.ts | 1 - frontend/src/mocks/handlers/index.ts | 2 + .../src/mocks/handlers/productHandlers.ts | 50 ++++++++ frontend/src/mocks/handlers/reviewHandlers.ts | 53 ++++++++ frontend/src/mocks/handlers/utils.ts | 9 ++ frontend/src/pages/ProductDetailPage.tsx | 4 +- frontend/src/types/common.ts | 4 + frontend/src/types/review.ts | 18 +++ 14 files changed, 261 insertions(+), 78 deletions(-) create mode 100644 frontend/src/mocks/data/productDetail.json delete mode 100644 frontend/src/mocks/handlers.ts create mode 100644 frontend/src/mocks/handlers/index.ts create mode 100644 frontend/src/mocks/handlers/productHandlers.ts create mode 100644 frontend/src/mocks/handlers/reviewHandlers.ts create mode 100644 frontend/src/mocks/handlers/utils.ts diff --git a/frontend/src/components/Product/ProductList/ProductList.tsx b/frontend/src/components/Product/ProductList/ProductList.tsx index 4be8069a..8cbe998d 100644 --- a/frontend/src/components/Product/ProductList/ProductList.tsx +++ b/frontend/src/components/Product/ProductList/ProductList.tsx @@ -8,6 +8,7 @@ import { PATH } from '@/constants/path'; import mockProducts from '@/mocks/data/products.json'; const ProductList = () => { + const { products } = mockProducts; const navigate = useNavigate(); const navigateToDetailPage = (productId: number) => { @@ -16,7 +17,7 @@ const ProductList = () => { return ( <ProductListContainer> - {mockProducts.map((product) => ( + {products.map((product) => ( <li key={product.id}> <ProductButton type="button" variant="filled" color="white" onClick={() => navigateToDetailPage(product.id)}> <ProductItem product={product} /> diff --git a/frontend/src/mocks/browser.ts b/frontend/src/mocks/browser.ts index 9c10cad9..5d6d39b8 100644 --- a/frontend/src/mocks/browser.ts +++ b/frontend/src/mocks/browser.ts @@ -1,5 +1,5 @@ import { setupWorker } from 'msw'; -import { handlers } from './handlers'; +import { productHandlers, reviewHandlers } from './handlers'; -export const worker = setupWorker(...handlers); +export const worker = setupWorker(...productHandlers, ...reviewHandlers); diff --git a/frontend/src/mocks/data/foodCategory.json b/frontend/src/mocks/data/foodCategory.json index cce54014..d5b9334c 100644 --- a/frontend/src/mocks/data/foodCategory.json +++ b/frontend/src/mocks/data/foodCategory.json @@ -2,5 +2,7 @@ { "id": 0, "name": "즉석조리" }, { "id": 1, "name": "과자" }, { "id": 2, "name": "간편식사" }, - { "id": 3, "name": "아이스크림" } + { "id": 3, "name": "아이스크림" }, + { "id": 4, "name": "식품" }, + { "id": 5, "name": "음료" } ] diff --git a/frontend/src/mocks/data/productDetail.json b/frontend/src/mocks/data/productDetail.json new file mode 100644 index 00000000..7806c04e --- /dev/null +++ b/frontend/src/mocks/data/productDetail.json @@ -0,0 +1,23 @@ +{ + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "averageRating": 4.5, + "bookmark": false, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + }, + { + "id": 3, + "name": "맵찔이는 안돼요" + } + ] +} diff --git a/frontend/src/mocks/data/products.json b/frontend/src/mocks/data/products.json index 542bbfd6..8d78d4a9 100644 --- a/frontend/src/mocks/data/products.json +++ b/frontend/src/mocks/data/products.json @@ -1,18 +1,28 @@ -[ - { - "id": 1, - "name": "꼬북칩", - "price": 1500, - "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "averageRating": 4.5, - "reviewCount": 100 +{ + "page": { + "totalDataCount": 99, + "totalPages": 10, + "firstPage": true, + "lastPage": false, + "requestPage": 1, + "requestSize": 10 }, - { - "id": 2, - "name": "새우깡", - "price": 1000, - "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "averageRating": 4.0, - "reviewCount": 55 - } -] + "products": [ + { + "id": 1, + "name": "꼬북칩", + "price": 1500, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "averageRating": 4.5, + "reviewCount": 100 + }, + { + "id": 2, + "name": "새우깡", + "price": 1000, + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "averageRating": 4.0, + "reviewCount": 55 + } + ] +} diff --git a/frontend/src/mocks/data/reviews.json b/frontend/src/mocks/data/reviews.json index 05b68dff..16482a11 100644 --- a/frontend/src/mocks/data/reviews.json +++ b/frontend/src/mocks/data/reviews.json @@ -1,56 +1,66 @@ -[ - { - "id": 1, - "userName": "우가우가", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "rating": 5, - "tags": [], - "content": "우가우가~!~!", - "rebuy": false, - "favoriteCount": 150, - "favorite": false +{ + "page": { + "totalDataCount": 99, + "totalPages": 10, + "firstPage": true, + "lastPage": false, + "requestPage": 1, + "requestSize": 10 }, - { - "id": 2, - "userName": "펀잇", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "rating": 4, - "tags": [ - { - "id": 5, - "name": "단짠단짠" - }, - { - "id": 1, - "name": "망고망고" - } - ], - "content": "할머니가 먹을 거 같은 맛입니다.\n\n1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", - "rebuy": true, - "favoriteCount": 1320, - "favorite": true - }, - { - "id": 2, - "userName": "펀잇", - "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", - "image": null, - "rating": 4, - "tags": [ - { - "id": 5, - "name": "단짠단짠" - }, - { - "id": 1, - "name": "망고망고" - } - ], - "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요. 하얀 짜파게티라니 말이 안된다고 생각했었죠. 실제로 맛을 보니까 까만 짜파게티랑 맛이 뭔가 다를게 없네요.", - "rebuy": true, - "favoriteCount": 1320, - "favorite": true - } -] + "reviews": [ + { + "id": 1, + "userName": "우가우가", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "rating": 5, + "tags": [], + "content": "우가우가~!~!", + "rebuy": false, + "favoriteCount": 150, + "favorite": false + }, + { + "id": 2, + "userName": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "rating": 4, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + } + ], + "content": "할머니가 먹을 거 같은 맛입니다.\n\n1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요.", + "rebuy": true, + "favoriteCount": 1320, + "favorite": true + }, + { + "id": 2, + "userName": "펀잇", + "profileImage": "https://github.com/woowacourse-teams/2023-fun-eat/assets/78616893/1f0fd418-131c-4cf8-b540-112d762b7c34", + "image": null, + "rating": 4, + "tags": [ + { + "id": 5, + "name": "단짠단짠" + }, + { + "id": 1, + "name": "망고망고" + } + ], + "content": "할머니가 먹을 거 같은 맛입니다. 1960년 전쟁 때 맛 보고 싶었는데 그때는 너무 가난해서 먹을 수 없었는데, 맛있어요. 하얀 짜파게티라니 말이 안된다고 생각했었죠. 실제로 맛을 보니까 까만 짜파게티랑 맛이 뭔가 다를게 없네요.", + "rebuy": true, + "favoriteCount": 1320, + "favorite": true + } + ] +} diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts deleted file mode 100644 index 0f3710c6..00000000 --- a/frontend/src/mocks/handlers.ts +++ /dev/null @@ -1 +0,0 @@ -export const handlers = []; diff --git a/frontend/src/mocks/handlers/index.ts b/frontend/src/mocks/handlers/index.ts new file mode 100644 index 00000000..40168602 --- /dev/null +++ b/frontend/src/mocks/handlers/index.ts @@ -0,0 +1,2 @@ +export * from './productHandlers'; +export * from './reviewHandlers'; diff --git a/frontend/src/mocks/handlers/productHandlers.ts b/frontend/src/mocks/handlers/productHandlers.ts new file mode 100644 index 00000000..c912f422 --- /dev/null +++ b/frontend/src/mocks/handlers/productHandlers.ts @@ -0,0 +1,50 @@ +import { rest } from 'msw'; + +import { isProductSortOption, isSortOrder } from './utils'; +import foodCategory from '../data/foodCategory.json'; +import productDetail from '../data/productDetail.json'; +import categoryProducts from '../data/products.json'; +import storeCategory from '../data/storeCategory.json'; + +export const productHandlers = [ + rest.get('/api/categories', (req, res, ctx) => { + const categoryType = req.url.searchParams.get('type'); + + if (categoryType === 'food') { + return res(ctx.status(200), ctx.json(foodCategory)); + } + + if (categoryType === 'store') { + return res(ctx.status(200), ctx.json(storeCategory)); + } + + return res(ctx.status(400)); + }), + + rest.get('/api/categories/:categoryId/products', (req, res, ctx) => { + const sortOptions = req.url.searchParams.get('sort'); + + if (sortOptions === null) { + return res(ctx.status(400)); + } + + const [key, sortOrder] = sortOptions.split(','); + + if (!isProductSortOption(key) || !isSortOrder(sortOrder)) { + return res(ctx.status(400)); + } + + const sortedProducts = { + ...categoryProducts, + products: [...categoryProducts.products].sort((cur, next) => + sortOrder === 'asc' ? cur[key] - next[key] : next[key] - cur[key] + ), + }; + + return res(ctx.status(200), ctx.json(sortedProducts)); + }), + + rest.get('/api/products/:productId', (_, res, ctx) => { + return res(ctx.status(200), ctx.json(productDetail)); + }), +]; diff --git a/frontend/src/mocks/handlers/reviewHandlers.ts b/frontend/src/mocks/handlers/reviewHandlers.ts new file mode 100644 index 00000000..f36c958f --- /dev/null +++ b/frontend/src/mocks/handlers/reviewHandlers.ts @@ -0,0 +1,53 @@ +import { rest } from 'msw'; + +import { isReviewSortOption, isSortOrder } from './utils'; +import mockReviewRanking from '../data/reviewRankingList.json'; +import mockReviews from '../data/reviews.json'; + +import type { ReviewFavoriteRequestBody, ReviewPostRequestBody } from '@/types/review'; + +export const reviewHandlers = [ + rest.get('/api/products/:productId/reviews', (req, res, ctx) => { + const sortOptions = req.url.searchParams.get('sort'); + + if (sortOptions === null) { + return res(ctx.status(400)); + } + + const [key, sortOrder] = sortOptions.split(','); + + if (!isReviewSortOption(key) || !isSortOrder(sortOrder)) { + return res(ctx.status(400)); + } + + const sortedReviews = { + ...mockReviews, + reviews: [...mockReviews.reviews].sort((cur, next) => + sortOrder === 'asc' ? cur[key] - next[key] : next[key] - cur[key] + ), + }; + + return res(ctx.status(200), ctx.json(sortedReviews)); + }), + + rest.post<ReviewPostRequestBody>('/api/products/:productId/reviews', (req, res, ctx) => { + const formData = req.body; + const { + reviewRequest: { rating, content }, + } = formData; + + if (!rating || !content) { + return res(ctx.status(400)); + } + + return res(ctx.status(201)); + }), + + rest.patch<ReviewFavoriteRequestBody>('/api/products/:productId/reviews/:reviewId', (_, res, ctx) => { + return res(ctx.status(200)); + }), + + rest.get('/api/ranks/review', (_, res, ctx) => { + return res(ctx.status(200), ctx.json(mockReviewRanking)); + }), +]; diff --git a/frontend/src/mocks/handlers/utils.ts b/frontend/src/mocks/handlers/utils.ts new file mode 100644 index 00000000..8b4d9a28 --- /dev/null +++ b/frontend/src/mocks/handlers/utils.ts @@ -0,0 +1,9 @@ +import type { ProductSortOption, ReviewSortOption } from '@/types/common'; + +export const isProductSortOption = (sortKey: string): sortKey is ProductSortOption => + sortKey === 'price' || sortKey === 'averageRating' || sortKey === 'reviewCount'; + +export const isReviewSortOption = (sortKey: string): sortKey is ReviewSortOption => + sortKey === 'favoriteCount' || sortKey === 'rating'; + +export const isSortOrder = (sortOrder: string) => sortOrder === 'asc' || sortOrder === 'desc'; diff --git a/frontend/src/pages/ProductDetailPage.tsx b/frontend/src/pages/ProductDetailPage.tsx index d82eb8aa..2b42af8c 100644 --- a/frontend/src/pages/ProductDetailPage.tsx +++ b/frontend/src/pages/ProductDetailPage.tsx @@ -6,7 +6,7 @@ import { SortButton, TabMenu } from '@/components/Common'; import { ProductDetailItem, ProductTitle } from '@/components/Product'; import { ReviewItem } from '@/components/Review'; import productDetails from '@/mocks/data/productDetails.json'; -import reviews from '@/mocks/data/reviews.json'; +import mockReviews from '@/mocks/data/reviews.json'; const ProductDetailPage = () => { const { productId } = useParams(); @@ -15,6 +15,8 @@ const ProductDetailPage = () => { const targetProductDetail = productDetails.find((productDetail) => productDetail.id === Number(productId)) ?? productDetails[0]; + const { reviews } = mockReviews; + return ( <> <ProductTitle name={targetProductDetail.name} bookmark={targetProductDetail?.bookmark} /> diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 9483cc7f..456990ed 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -16,3 +16,7 @@ export interface NavigationMenu { name: '검색' | '목록' | '홈' | '꿀조합' | '마이'; path: (typeof PATH)[keyof typeof PATH]; } + +export type ProductSortOption = 'price' | 'averageRating' | 'reviewCount'; + +export type ReviewSortOption = 'favoriteCount' | 'rating'; diff --git a/frontend/src/types/review.ts b/frontend/src/types/review.ts index cef8f0e2..5b5ddf89 100644 --- a/frontend/src/types/review.ts +++ b/frontend/src/types/review.ts @@ -12,3 +12,21 @@ export interface Review { favoriteCount: number; favorite: boolean; } + +export interface ReviewRequest { + rating: number; + tags: number[]; + content: string; + rebuy: boolean; + memberId: number; +} + +export interface ReviewPostRequestBody extends FormData { + image: File; + reviewRequest: ReviewRequest; +} + +export interface ReviewFavoriteRequestBody { + favorite: boolean; + memberId: number; +} From bbce2e753aabeec115602b3190e4ba59295cd152 Mon Sep 17 00:00:00 2001 From: Dabeen Jeong <hello70825@gmail.com> Date: Mon, 24 Jul 2023 20:45:36 +0900 Subject: [PATCH 045/185] =?UTF-8?q?[BE]=20test:=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EC=83=81=ED=92=88=EC=97=90=EC=84=9C=20=ED=8F=89=EC=A0=90?= =?UTF-8?q?=EC=88=9C=EC=9C=BC=EB=A1=9C=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=EC=9D=84=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 평점 기준 오름차순으로 리뷰 목록을 조회하는 인수 테스트 추가 * test: 평점 기준 내림차순으로 리뷰 목록을 조회하는 인수 테스트 추가 * refactor: beforeEach 삭제, 메서드명 수정 * test: 평점 기준 오름차순으로 리뷰 목록을 확인하는 서비스 테스트 추가 * test: 평점 기준 내림차순으로 리뷰 목록을 확인하는 서비스 테스트 추가 * style: 코드 포맷 수정 * refactor: 컨벤션에 맞게 테스트 이름을 수정 --- .../review/ReviewAcceptanceTest.java | 99 ++++++++++++++----- .../review/application/ReviewServiceTest.java | 71 ++++++++++++- 2 files changed, 143 insertions(+), 27 deletions(-) diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index b6a1af49..8e9d3ade 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -29,22 +29,11 @@ import io.restassured.specification.MultiPartSpecification; import java.util.List; import java.util.stream.Collectors; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @SuppressWarnings("NonAsciiCharacters") class ReviewAcceptanceTest extends AcceptanceTest { - @BeforeEach - void init() { - reviewTagRepository.deleteAll(); - reviewFavoriteRepository.deleteAll(); - reviewRepository.deleteAll(); - memberRepository.deleteAll(); - productRepository.deleteAll(); - tagRepository.deleteAll(); - } - @Test void 리뷰를_작성한다() { // given @@ -136,7 +125,71 @@ void init() { } @Test - void 좋아요_기준_내림차순된_리뷰_목록을_조회한다() { + void 좋아요_기준_내림차순으로_리뷰_목록을_조회할_수_있다() { + // given + final var category = new Category("간편식사", CategoryType.FOOD); + 카테고리_추가_요청(category); + + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가_요청(members); + + final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); + final var productId = 상품_추가_요청(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var sortingReviews = List.of(review2, review3, review1); + final var pageDto = new SortingReviewsPageDto(3L, 1L, true, true, 0L, 10L); + + // when + final var response = 정렬된_리뷰_목록_조회_요청(productId, "favoriteCount,desc", 0); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 정렬된_리뷰_목록_조회_결과를_검증한다(response, sortingReviews, pageDto); + } + + @Test + void 평점_기준_오름차순으로_리뷰_목록을_조회할_수_있다() { + // given + final var category = new Category("간편식사", CategoryType.FOOD); + 카테고리_추가_요청(category); + + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가_요청(members); + + final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); + final var productId = 상품_추가_요청(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var sortingReviews = List.of(review1, review3, review2); + + // when + final var response = 정렬된_리뷰_목록_조회_요청(productId, "rating,asc", 0); + final var page = new SortingReviewsPageDto(3L, 1L, true, true, 0L, 10L); + + // then + STATUS_CODE를_검증한다(response, 정상_처리); + 정렬된_리뷰_목록_조회_결과를_검증한다(response, sortingReviews, page); + } + + @Test + void 평점_기준_내림차순으로_리뷰_목록을_조회할_수_있다() { // given final var category = new Category("간편식사", CategoryType.FOOD); 카테고리_추가_요청(category); @@ -157,13 +210,14 @@ void init() { 복수_리뷰_추가(reviews); final var sortingReviews = List.of(review2, review3, review1); + final var page = new SortingReviewsPageDto(3L, 1L, true, true, 0L, 10L); // when - final var response = 좋아요_기준_리뷰_목록_조회_요청(productId, "favoriteCount,desc", 0); + final var response = 정렬된_리뷰_목록_조회_요청(productId, "rating,desc", 0); // then STATUS_CODE를_검증한다(response, 정상_처리); - 좋아요_기준_리뷰_목록_조회_결과를_검증한다(response, sortingReviews); + 정렬된_리뷰_목록_조회_결과를_검증한다(response, sortingReviews, page); } @Test @@ -221,9 +275,9 @@ void init() { reviewRepository.saveAll(reviews); } - private ExtractableResponse<Response> 좋아요_기준_리뷰_목록_조회_요청(final Long productId, - final String sort, - final Integer page) { + private ExtractableResponse<Response> 정렬된_리뷰_목록_조회_요청(final Long productId, + final String sort, + final Integer page) { return given() .queryParam("sort", sort) .queryParam("page", page) @@ -241,14 +295,15 @@ void init() { .extract(); } - private void 좋아요_기준_리뷰_목록_조회_결과를_검증한다(final ExtractableResponse<Response> response, - final List<Review> reviews) { - 페이지를_검증한다(response); + private void 정렬된_리뷰_목록_조회_결과를_검증한다(final ExtractableResponse<Response> response, + final List<Review> reviews, + final SortingReviewsPageDto pageDto) { + 페이지를_검증한다(response, pageDto); 리뷰_목록을_검증한다(response, reviews); } - private void 페이지를_검증한다(final ExtractableResponse<Response> response) { - final var expected = new SortingReviewsPageDto(3L, 1L, true, true, 0L, 10L); + private void 페이지를_검증한다(final ExtractableResponse<Response> response, + final SortingReviewsPageDto expected) { final var actual = response.jsonPath().getObject("page", SortingReviewsPageDto.class); assertThat(actual).usingRecursiveComparison().isEqualTo(expected); } diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java index ce596240..5e2bab2a 100644 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -18,6 +18,9 @@ import com.funeat.review.presentation.dto.SortingReviewDto; import com.funeat.tag.domain.Tag; import com.funeat.tag.persistence.TagRepository; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.Nested; @@ -25,13 +28,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.mock.web.MockMultipartFile; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; @Transactional @SpringBootTest @@ -183,7 +182,7 @@ void init() { class sortingReviews_페이징_테스트 { @Test - void 좋아요_기준으로_내림차순_정렬이_되는지_확인한다() { + void 좋아요_기준으로_내림차순_정렬을_할_수_있다() { // given final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); @@ -212,6 +211,68 @@ class sortingReviews_페이징_테스트 { // then assertThat(actual).usingRecursiveComparison().isEqualTo(expected); } + + @Test + void 평점_기준으로_오름차순_정렬을_할_수_있다() { + // given + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가(members); + + final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); + 상품_추가(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var pageable = PageRequest.of(0, 2, Sort.by("rating").ascending()); + final var expected = Stream.of(review1, review3) + .map(SortingReviewDto::toDto) + .collect(Collectors.toList()); + + // when + final var actual = reviewService.sortingReviews(product.getId(), pageable) + .getReviews(); + + // then + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } + + @Test + void 평점_기준으로_내림차순_정렬을_할_수_있다() { + // given + final var member1 = new Member("test1", "test1.png", 20, Gender.MALE, "010-1234-1234"); + final var member2 = new Member("test2", "test2.png", 41, Gender.FEMALE, "010-1357-2468"); + final var member3 = new Member("test3", "test3.png", 9, Gender.MALE, "010-9876-4321"); + final var members = List.of(member1, member2, member3); + 복수_유저_추가(members); + + final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); + 상품_추가(product); + + final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var reviews = List.of(review1, review2, review3); + 복수_리뷰_추가(reviews); + + final var pageable = PageRequest.of(0, 2, Sort.by("rating").descending()); + final var expected = Stream.of(review2, review3) + .map(SortingReviewDto::toDto) + .collect(Collectors.toList()); + + // when + final var actual = reviewService.sortingReviews(product.getId(), pageable) + .getReviews(); + + // then + assertThat(actual).usingRecursiveComparison().isEqualTo(expected); + } } private void 복수_유저_추가(final List<Member> members) { From abce4eb9fa0d8e9cadac33cff36c6c2a12fb5c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= <solbi2004@naver.com> Date: Tue, 25 Jul 2023 09:48:05 +0900 Subject: [PATCH 046/185] =?UTF-8?q?[FE]=20feat:=20title=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EA=B3=BC=20favicon=20=EC=B6=94=EA=B0=80=20(#114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: title 변경과 favicon 추가 * feat: manifest 작성과 meta og 추가 * feat: favicon 크기별로 추가 --- frontend/public/assets/apple-icon-180x180.png | Bin 0 -> 17919 bytes frontend/public/assets/favicon-16x16.png | Bin 0 -> 1285 bytes frontend/public/assets/favicon-32x32.png | Bin 0 -> 2259 bytes frontend/public/assets/favicon.ico | Bin 0 -> 1150 bytes frontend/public/index.html | 19 +++++++++++++- frontend/public/manifest.json | 24 ++++++++++++++++++ 6 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 frontend/public/assets/apple-icon-180x180.png create mode 100644 frontend/public/assets/favicon-16x16.png create mode 100644 frontend/public/assets/favicon-32x32.png create mode 100644 frontend/public/assets/favicon.ico create mode 100644 frontend/public/manifest.json diff --git a/frontend/public/assets/apple-icon-180x180.png b/frontend/public/assets/apple-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..6534aca97913d4cf18fd13e90edddf293c77321d GIT binary patch literal 17919 zcmW)n1yCDZ7ltWP+$rv^h2riMFB-JL-QA@{i@OHb5=zhpcM8R=#ex=hcl-1G!$OA5 zWcKFl-DB@_Vl+P~Vq=hFARr)MD=W!s!(W^J`$c;T|0^;6mKXklY%8NKgMd(<g!yES z0{@@ZQb}7K0U>}H0pUvo0>T6Q)R#R31aBS$gab1K1kr2+1QNHrHZ5`Z8*eRC73C3L z|NANGtW1NSLG@BrS3uoGB}EevWe#UiK|qkJRF;>~^;<gb^bMk%_dk7>(D8wm5JVzS zNZ0ws5Glm4>owL`sCxVxP!3vwC8&B>4=J1Me)Nf@g8g@>Qa-i~tazkYGZ84i(=L(M z=Cn9PL5MMF)(|<R;cGOgL*(osSXxu8M)6$|u?@K#^DMX|qAO1)a+gtJJC9b;B65N; zrwu0$TYVeaq?8KD%NO(Y*jty$1%X8TFBm0OR<5qDl2w?MD`%D1w1^@#S>=TZikq6! z!C>}-gM(~dZ+1SLF|&!~)-flivZfzc{X9HaNb0k0l&A=F$g<zgsT0E@SqUDV&SHMB zVr1GABn|D;kgiYlhC8ix<Q&7A8(mLLJ{5=$udmZ)Ix|d)4^I`Yc0ap{hq1?oRe&SH zWE>nFbA()}<FYu-7aOd{a>TNfGcx2YKVbF${#m7T!l%V}C9+QVQDs;<REjD;zoX3( zbyEBii^4Q0B^(tedCW>$#S6$no<M*ViAMHzS}DSgKNcU0fCr1H&`aAQmpMl*N|*hY z8NCc|#>lf+hFYq?_%cWuO-6b)Thf_)zSihhP4mj&&v_G7Adt%1+S)Ex&}of6Hi9}T z$io1tu4qj0cynsIk}Dwod^OwA=E<_?G`E7P87Yl8s{dm>Y3)XkoPweX0;wozNS&F{ zrcXiZHN#WT^4JwpPt#>r5T~xJtxY<CykA;c5)L^cjmDwbSx8~Xb8@Y0u8dw?HPJ2r zBNkP$V`F14`@o!+{pW$4z=nvnhXC1P&6B5EZwnIiaW{I<BIi&djqPQHy7>K8+7JH+ z`z0~4H<}0lt=}}fv+jPA?Q7#%T~F2)^$SdC;L6HqOU0xH+evDZmHKdCyN~<T!UrQ` zW3u=m&&OZ%_z=^dv4JP8(SNJ;`FvN!=~I*-Qndh!?h15?_}>-7=axf$C#}18e=U>F zmmjVNHr<KnG$lOl+L|C$q-{}PRxjLv*4?g0zpH7*9A0Y-sB<aU;e@=BkR!C^@>LQf z$yr|3*_p5Jd$eAjWnyu;Gk&z%37VVJYp6h%mYz3kutLWbKSO9<3lW3gb~;k5E*n6~ zJZJHx>)r62UO~`>a>(_9rIX(V$^+_KxTVi^F;J!iu;u2_-g<ed%gqx}qC2m2a(?!~ zHoYIwH)!?PW>Osra#WEVrW)G#Go0oWbgnSxy3iAaO|j7IN;9mB;TF^Bw6A><y8h;* z^Hg$YA|EtcZ>m}rGLA1=$cco6^aI!!Rrva}`vcgXnK&^)M;|yNOge>zf=Jqy2&ocD zeawtK`6^tTs!i>`g}-{k66)cOJkpJ_o4j|wtHwy+v<6KOaGX1PTzf6h_dQ|E|Ad5x z9^JYzNa26>6}9E{<*}-&W?bxgfCc(+){iSCzM4SFA)FYFhX{lEU;ov=zsTwun5Pa- z-<_{z$yX`%PZLtWkH8L}QOY)8RbnkF1+ybY<!nXZ32V>%omG{yWT@7!{WyO;oo4i{ zI4P-7$-Z#;tMnK9*$Vd+@A>+tCC4g|c#fa=CtrP%DzDs9#l*9V3Ux|RuWttYD)3Lo zRz7NYX?NS89Sh^cmhkZK^x%e`AxYl0PM(9-UNTQ6CdBDEA`-r+Zn>e!%>(K4HuEGG z399&6G2naN>|$%U)tTWdzq%D~!j+drF>62iiK^?VQ5PHjXN!4_oW6_gQNvabri^#* zn)rqIrq(prbF6L)B%EMf`Cq#vZ3G3hw6%GizS_`R^bv}hvaI}cBzZl{nLImwebTr& zTER>V4>gv1liy%Bz|asyA7#up@s>^+9}(tmXD^|=IhMm|+C}**ZD%*~*sQ>g?x!{C zB9bCclAxtv;ehDh#1)}0P0s0G5~qIB4d*xGrE&lC23B@;6<E)I?uW1JgL+zjG>*AV zqnEFDz5gij&FPv3(3xx2cI>y~>j1-b1aniZg>Yj3WaxvA<aL$C`JY)0zJ;|@qxWU< zIslbYbNCU~7FB}wk*^w0wg@A#t5=41k&{F3M^Qz^PFSrkO9p+TC--7-bV?P`gc)UJ zC_;N9YIO}&^)}<quuoo>OawjNMlweyoubq0gQRRPEp|e}=VlKkKHG_{-y8jKe{5#c zNGb02r3MBqJ`}y&nJla$97I>Od;PmKJKIkp`Rf02ZTgxK9C%Cb41>uLpp)yS%cD_z zbV;#l!t48~E|$3UyWa8-{6;ibk4|~7z(Rcb(9{)}XALTsfME|6l5d1@cpd*;4b_W^ zelY%I?(6lnq)47YZeW7p=kM8GRaMoTZnHPldNmeiW?#ZlaV-`F^QovhI_wEMJ3Gff zQiZ9hspyIa=*o6O?#bE>zLV51JFYKLG%sh-S6;sDpHm2c0Jfx$tO*g*{Ku)aED_QG zz`rVfmRyiw$nnos{YQ~udSnOAFfYoZM3<~T_wwU~j5dwJozDhbEcNXHnqe~O23B@! zvrC9YVD_}bgO>9_iX47>g4&Y>xD%((Q92vpsGSP>hYWWtrQo)Qr{j(ho?^7)!wzkg z0>kFSB&jPr$N3+<BQ`)jP<jN?8+MhFi10T9kpe6DasVPCg{c5=!dJgM$J)-!h6djC zAZ;VL8nFDHsE-D>mAd9cL#>7jtapEZ$Vnt7ZK5c15btXbS=mi3TLdjZOdZ^h5ObT1 zt#$_P9~}+UbNZYv_P;zo<_I_v>r|+b`7Hm9N0+lJ9LuymA2odn>wboHTMs2tp|QDl z%sVIx%PVlkLhO}V;=O!gimJel1j|1ufU^crC$eHU;pS&fR6725H5C{?8?bWJHU`oP znuu*(i%Py&TQqxH+}@OC{?er1@Juz*BH~vst*VNl(5opaFOOtUOrocXwj4^V_de9C zn9P=ulk1N-TLs}o+zjU_G2TtOhJrLgA7&}Ve6W5s5;}ra{Q1@F8S~oB_5VTHVr8Xu zlx@|aF0fDP-U9Fq$AX8}GA~Y26)DZdMaNS^jgLXELuSQ{v?{)?)_|c&l(!mkR3Q21 zqJi#RL?f_IqN@U$d05+B;3qzDveI7983%<5LQfZZ|5WMN+u8jV(FWEGbRD1{r?s|M z$JK^D9|mn;niiFIuKb)A#d!lPr6*>h<R2`P0D}ppbpKTmQJ5_ME*nvS`{_8y;cn)a z0u?%bY`9f|jU*iS|K(CP5-;4G{gr=p*L|1wRYtn#uP$9j%--U<Y4eXC@!K4FMXVTN zzQ^=wpJu10do$HR-yskiJsrX&6y~PL`0Cs#$s7%MN|HtGAHf}bKZ<pA+s+Z7O~8w& zJ6J;c=6U8lzsg>26a<VlK<MOmt}vpaYVVM%EKC%2Y54A`Ioce4J^bts@0_(!=t<@6 z*o9jY18IDZwG%#09XM#cKbv}p<DIrk-yY2(<i+wiSyEunDXVPGQU?iY85@rtEjHE~ zX`B9xm`G{;nGqVig-;!Pr8TqfHG#`OfT2yDfHx)Z(b}L(?RQE5&%^^Z1y3V0A1IfV zn|t!u^OOBCcc3G=gCY9EA5p~F!)fOEBE5`Kz4YSDtm04XEIdDPEI$z%S>GF8@9}Az zo>-E;g-6)ldT02({V14bZswNEWenBS*B@GL_4G6e*C(vvUa=9tIU5c-*ixpx|3`BF zAk_6WXFMQLE;ZHd+l&$^p!3XOEyKz3HyRK6UINQ~bLgXxQ7-8IOi{A$;vL5XjdN-@ z@7}X<gsfdLo)IvidHivWDOr7%zhs)V4YDbW(*~YdS{kX8dbWq3xm^xC<<VcLexsis zaicutb3kNz_q2PzBh;P4z^934H4zQ5NA|`Zg)Vk^vcRA&3R*-5%o@yk+Pq^M`#%OG z^OsxKL@bqq_&2l0ZTye2y0_v?pXe)%jNqw(8tfy0C?Ftk5gt$Y$jP%sJa+)r&n;jy zE{T=;($etB;1kcFL*q4i$K3wkpY^1zZi>O|GC)Oqw<CEz?v69W8Ws%#ek=zE#}8PR z*|XF4%!zr9Fr~o>G2hToDXIJOr2FOWC(LLZN;7Rk^l42rnEP)dVsOdS;^OdX@S_1w zsH9G%db@)jQ7yUbqmUI3^{%PpqhZ&5jcdxd)!E#f_%J2;Pwqhj87(3#VE>di!7yX3 z6Krr4Bse{@tj0=O*4CCQO`FVcv;9q&%!m54`M<#Zvo<87HsrWEPuM3NoJv4Xhcw<e zkqu3-USy4x4Ga`hOro+pFH!jOcb-9FEn9XFu(5tB^dW8dtUUBtzw7Q7H?J_yy2zrc ziiAL^=5QYFJPnL4XLzPii5`%#*l6Rvi7n(dMCI!?J1(RH07{I3POh3zcIp4NmH-jj zM8)n`Hj^$J3t!T)DTFL4baV&kBM`slEirs)vY+8grgbzt5#dh=Jm~^)2k)z@6@DmP zXSxQ+*|9O1K9A_bZ>^Mut)ij}TRS6V2`4#qEMqIiItA}%3tLj93imI=*Vki(LP;6( zz#%Y&i`jW9ldiu0F0t50%vt`$c3<wpImmX7_o1Klq27{4K0PrGJ|+sf-2s^;a|CV1 z@QeiBP=hlbA_M6b@H7CPLL|K^85$bOq_wT1ALFi0hTibKJk-AC60&@v73Ur5qZ_H^ z<=ySiiid6{3v#7a@eiwIy78+3yajX)5C5w{E=f3x;64?eMt!iKDf_Hf9+X|7Y-&nF zb$rY(B-A(x`f{?^o|QSL_Ye?>j^bspQB*lu7&=C<k0pWRPRae}M@LDRM$kV(@vHMB zlBVs_BJ|@&zFOnPsA$O-_nV`|%{D!iwQIhBX_vxCWYalibVC@urBNBL;Y<#v1FbY8 zITHa6A$Bwy7Z($89Dc$_Pt}78`&1>yy;GW^+U`Pm-d=bWb?!&`?pp*_*jI!%>Iz!O zvb-f9D3mJTPbOZA<?__RZ>iT=sdH4<V8>pS{V=AcwIA>z>~b16G&{>A!2S-yOquAK z-t<=_)<%r8V%YIQHZ<IN(TXe1Nifb@Ojwqd$hxKASQu?mV2%fIFBcxFkagcFynD~4 z=wDr)el?he#i#=@#j11o%l_ZqYPfhf^fY&lj0pQ|a9;Q_mr4Ax?OC|FJr45hM&(bq zutQb{V2<$n-anj#U~br`%m`p(B`Z-571H9=RNwtu5mJ4m_L0+GTQM{l2vkFpv1g31 zhQSSjT<5;|vFqVyt(KHE20W~?2`d8)lCn_gkcZ{N6uWN(;6=i4o@XMMHGPPFZ)t$> zaxuB_vyxDV7J!H!Kv%?u>^1*~cxrh~S7DgzdDmF{JxXqgCNX;HV?bm2)5-bJeJ#yP zvYIGR(<%y)scBRAX>UksC;Ii;l#vj<hsocNQ<kU5^jh?C`%C*EKV$u+y9gWe*DsLm z&#v$2NGVCzUxQBr|0@VZ9CeC|iqN>q2iz&=Ai&XXsRz~?E-sPIRz?EmgYhFwRpt&s zJe;@RBEy7NK^|*e!B3C#-4iDgfl)Oci9#4rQ(f<U&U)T-UAVpuLXTlU%qZJwR&>-j zHEKmxSv<^WG>SJ_e`L?DW;HH1X`a7zbxG=9vm$7dU=>H=U0vT?be;w$!Ap-K{~h7} zz!f%>it5fe+BZM|D|+;luJ6pg0kPCwFRtIoqHX<T=3X$RCSF9QcIvpz{W1Kxem)H? zbYey_3HUN&M+)TrEcfDwP5}3$7R9vdtA&K)<3Z1drn@~~kxnExIm?Igr(O`8V9@cu zAdLdA<52<?dOF#!J-_wd#F7%C(S3Ux9ubeAt-`NhzS8FXbj2cNZf0iI0TSwpxVhfB zfeLm#)4cXpsDq}6P=;nfr81@81xZNDNW3U9QMCZS62Q&QU5_iL7Z<ABt>7VHm3{ZD zsbQ;B)8~izr+*~fzsq+q?8xig&;WqAzE)Z~bQDDSa2ZC(!SwP>pHTO`dRtrDvczD_ zpUsbUYx2$48Txkz`dzZ2q20|ROmK^$A1t*TS}NpG5%>al$$8=5QV0-XdwqB88mcOo z=I@NZq%RN<7<hlHNMrr_@@!}F@~;k`vN0V=;j?&|SN~{9vDpM-#+jZkF17gHG2cxY zHHoZPq8xMDMsxQ)w#L)TZL6&K8=$9m<K>ps`x3Wxj3R`M4)lLr!H;)0&E4wEX=GV~ z2_nVi>1bwatDQmjqo&N%N#Xi0bi^3u9Z^sG%!ot^bmTZCj$jbuEcAYVGGuTtfx?}V zB@4x8Y>96UiME_EN9BCnH7NWU6$h(|{7t?Rk7oLqK3Q2hvUh^g)bDbE!;_O8(Ar%T zh{Za7MRn-P;f3(=kB-K9W8o{MJ*+hduiowZdXS@H+*5Y<vZ7X$r0t^QhxMfjq=Izf z#Qrwhf{mE9XB=4jkY(ft%Va71qmzSwYc#=sembnb5M!f!KzsmHJ8aEM)^lNw^4F`K ziaA>37;?@IG;wl{K0~=M_l2|4St~SY%jIWvEsD)tDB+&+eom-c*;ij5Vm57HHb(hW zo^Fu&%eU*2O3-;V9#J@vBA5UhwH2I+<e4Z8nU&BkaUd_Zw$=sc>lo`Rs;hIo%g8|P zZ1|<5RG#YP>pQ#Z6FYH(1CLC5r-z>5T4e7xYN63EUwT!WhB984e}%_lK4%N?#>ES6 z5bb<7N5TiXCeusT-Rj!^KPpz@iHNN+MWRs}XXQOCxtqdDLo&E~Y7o%alR{jJ&o2yO zPNT803Pasbxe*8YZ-x5}7r8j*VJqX)rHTvffh4squtA8v?-V><i)J7)#+I0P59me6 z@{=;=3jo<x=7*9(t{7I&qJ(%#i_O(aj0YJjG*y&E^LMx<Z$5;ah0~aUm=ibgFj1?% zDb!wVb7L<5lM{p<<6%VS2-xD(wm;*zbZ&my;WmiC=Z{*cdvZX$-~Ii1GwdRUGn7QV z{p))2{E2U}?l^-BUcS;%p#o+h>=vr%K%{WSS+a-{JTA5kSIK*2$rt@lfsRvsH2u7( z5ZdiD4as3tsk1jmAdu(cN%g48)DWB4-Z#|W3~AQ6q{XzCyWCreLaXBxr=3ZI6&1@N z+M?9O8TczrPk2>N!)NO~2#1BiQ7j>k6oTDNDTy~eL<;UJ2pE2O9-Am-jm-Ja;lkP$ zs>3509QPBx-J|f_vbH6?t0dxvVr^}0@^S4}G6e2O7$kNx8tfuw>{{xEf3PlOMq0x7 z$MS|(ZaZ@xU(ayub0usIMj)f+`69s7+uQ5^zUW!C=~H~dVIfP7QW3?+_hFS&(-NdC z1zwgo{q>}{5?6SI4}U5>p4+us{<R)Q;5}WCbgzr8J--isenuc|`{e(lF8?MHFS{b9 z;&9n#7Z|ivoOC%S^~?tbJ4PLQwz^F0@A8Jp*i7b+#%9bDCdiBY^@)W{d?Hd{^Si9* z2|k2}BFfL+vrtZ#Gj<Q4GF$L3=E#szV|e<zuMw)EXl$I=Q|~qf)NIuz>6S;M`&~Ax zs|UDL485`KI6gi#J7}^OsR~js&8gg9zt*WZ>rD!YM^Vi@dv<+Aos?+A8FHWUS`m1B zN6TntM_z39#g8`u%zCuc31SJk726rh;S1vvR$}>zCxcg7-R1VHhB8U&MVQ-lDV^e} z4&N=mzxFDU`)wcBRi;Y0U9JG=HuR36W52f<FX3Z~hZCRc4x#a^w@c@-PsJzDV7jZu zZhhf%gcYwcEgtD*`L-4o)3jN#u4vq`@YT)L*;TH<a_H+b|3scYbuVqoCyXKSZ{!9% z2ZtspzFBJv@P6XO0HMaz-^DlImg!aKz~p@lo}&3S`vUW4^P#UixLwyt;=Ju?o$iiV z&HscQCqBRYtNm}+U3{>pC(Ti-F#C&(j?6E>(m_PG(BRMa`n;7!B0SPtpGcbz(NeW- zK`6c~w|j3q$$BRB`n=|})Un9nwl5l)<o%{Fr6-GMUpMw0hyKNI_hX<-_u(~icyHJ+ z)>gGx8W`+1|0vZH{W>X^o{^D8h8h|(-6TSZRkU^Q_XF5Gq2A&j#Wus|DH<%iBZ>EB z0_`%3`S+0sWXCXm0#;<6FsjP#S6~^0NGa*{jV0tYuuV)%s{@&-K3OYyhHQCxc2wVF za_IF&=Z8_S4#diXE_pFpBg{Y<J@jV6x4jJVofgFPS=rf6l#XFt?-fzdb#+XYLa3m| z_shTsT)N{BJo8iELTBQ{l_;-(Nq;VT-vVX37>)!~_2|-Y=Jo*Iqk~~LKU!6drNJ9= zpZD62$Og?mToj#AV%h{a#1O9un?_EPw)mvi@z){mY8Pq1-!S@%{o@R6f|!r@v%Vc^ zxWQYN11c&!>W#FpAFN^UD6qZkXm7t!RasEd*vO}$((dGO^q%svW%nn^)x^5%A$(b_ z+5x1?o8>ECEvY3vwe3VwcUp#vzj7+il70Lv_f8GNVeH$!?%Th4P2(C^tP%e;^C4a* zCm$~zKLzHqGosP)aK+ir+MX4|cQc?`A9wV?@cUT|UZT9;WBb~s-qiD8Y1wQ}nDH0< zWR71c6Kw~5nA{ybH_ot@OGx|0jYsm%n=&;7cG-w1=rDhxKi>Y8OyZuuezM`w(Wi)S z*jZB*3?iat4UH`QMgBJblH<ATV?a&^O7-n5aw9&r3zhorM&0H}hx4_Q$`{G_v_*#~ z8TU$PU#$&wb#>v3dO>k*xs{av+6A5b8)B;6z1exXSs*0wrGyp5_WCT`jzOmRDv}-f z|6TnyZ^nP_Sp?c`Gm9?QiP~?F3V9NPG3==o(4L!OO2lHC#ygb`iDbEa(Yf3&@e5Uq zj!s9-RQ*2z_0h8KqTe<+?JG^EK|N!T-h85znwZs)P|$GY2D2RZ$!Pe#$LrmAuogxR zmwDOKc#TJ0dzh<k_-*wffBsDKg2n+c1!&HQbmvDw+N-`wq)K*&NWhnbS_>^}!ePiS z^ZgR5x!j_?e`RiaOKVMz#$#pg<B2YI!<KhsQl^QglA~((hLJN`?%9wxG2BCvNn~DW zPsO-nCZ~kUdIx-SvQQs>Jt>tGmX#-tNBi9oW&bAd^3Y{>h2{>gSM$5s53<ro)7hZ8 zJdvO?1{&+7D>C6kpk~NiSGU)1Hx3+D*}C9IAE?u6ytScHCF+pF`R&J|R&eB43pi@! ztt?V8T|}XRDy=moOC#p~06sk;eLVdeRh3W7Y7e|nkd6x@ZY%OvS{KodrHl#&>*>|i z2|^8VnYgTHrSmaCNZ_6yZnY3r!Q;*k5zqUnA<2&bZ*R!eufG@@t!t|uu;Y&Bl~X!O zvd73GOqn-fQHd28x^zm+Y2w#^bodMH-nNt34_%6u(M5dqYI7e8e@QA}n`CK9NLz`s zY<`0y%O-=?Ow358I+M%8%{$u8v2u%T(9vKkH7VKnzLFiaV_3Ra3G?>dF<#s+zf)AH zff+{#*Y5@l5==Ix(6b@eO{mx@yHaKC7jSEeatm_kMr6X#$|Ht0lrD13BnD5WOwH2P zR6&$2=&%7#RI-@rQaI6{)J>Wdt(-_xixORe4Ak|*bNX)?5gqAoQ72c5GO6teD-=9I ztSiLog9G8z_O&OQ6aUNStZ^L(-umGzJlW~Em3sw|CGgcdx0{tUiHQ(JAig2m_=E!! zTQYdO;@(SY_ID+7RWx5Wh<5*>r~miN(e)bw8p{5a87NRZ4(&^|6ax{Fv5KfRaiXnh zSIb<Rxe|6<?e>GvY7$;wjXM=9=f0C6apUg9=>9k%c2|bw#y_TxXW%KF8EGU_Rb<bU z0AlXW1))Ks57JO#-jyEVN^lw9w|oh|hu$wZGkp^$3QJTism5VE;^;L+*@<OBmb)sx zKqon87_*+<;I(T<(r`lKvM4P|`h=>AGAq#z)oCy{b^>3g4as1?kI2UDeithBR@7gi zR?PP(7sMBY5^s?LAU0!N&(2P(UZk@(?@TrzZ$}tK`r0n2BBCs*6pR$fz}Z{0@>oRr z(rnL$pfV<)^#j4if}oD=xaBYy#KZs7Y-}R71TJj0+<P~lKeV5ESIetQ^Y-HvkB*M| z{}&R<4jl3jwIw3(kdZ{e2k*ynD#m9WeWziCV~&<4{<X{QqQXcEa9@<hy!(@mNtVr% zi$N)UH9ma%a~sih4Qk4a2XSpj%Eg5T=Pdc{*DFB6K`!~reEXWZz2RaD)Rq(8W&BSN zF`0yCFl2lns@uop@bo?KPFH6N8ZyP)>F!jQUX%%AWix=bJkpX%aS51w8I*n!p_w1y zTB!H70qXm2bXrfysMTeMlAT$jx4x#4-^FP|v0(YOC!Di&XQ`V0-)6&=u?klIdeY0$ zv6`zLtubvEHGw7|vIxR}_dT|#(eaCI;hO45dL9sp(IYP!{$-p;NW{E|b(?w^MWxT{ zy?Ur*>95q(5q6boNB6C}_x%rvitr}YW^eP$hYcmG&oI7<;9KgYs2sl5)32!RKh8sq zcb1}zmt-#;X)xgXL8udm^nK)hP6k=dikQ~fm-oB8M>hwVqKO_Id$}ABIfLGZVTzt} z5YfZI;EHPiA6(05T{Gdplf}b`^qUQLEiSH#QDfG@d-m<fkds1f2{VHwZaS^GQ&z=l z)kL#8s2A8$vl>2Le5Y9VZ&K0Zr(&R#(Hfk*98JQ^o1W?kzLiq?QD*gd=|jQUu(6ai zyKmP<0*Zi$ZbT2-9&7BOUaksA{Bq2Nw}KCmY|lstPbNatKf6EG<9>ffk~S6`-~14N zEZp%@5STN);P>~z;WG|gT0RNBd{zT}C^akNZ*eJa+dW~Y8upw#Ev+o20Z@Idj%d<q z8b&M63o2ndus+IjbaKzi8h7$pE=gU9G0PlIJ8U_I%<Ei`*E!|6aw1fW+pX`+uS5Z8 z+nQX{1igfmE9sF@aeg*#XOtEJ?YtM?6f-p0qcN#<Dd{5~So<96A}@O7TA6Zt<mH7d z4;Kv|7`s}}{aA#XZIqRRdv`*pBV}Yn8HI3#x6G#{(aCVu;&=&YrT!?ch@OlSYm`u; zx4Be$$>^n~{~ol6^Z6g+LjdXCf!W;;FQ;|TzRIXd>oyct^#e?PeQ~O(b=POO?M2D0 zyrs}Ij2r5Z%a>(6|DMH|)*-sEyZ`pq-6<NIo3g07lLjCLTH4s3i2XVR!W+rCq%Y?z z^6*z?*>iR*Z+_lt;cRW25$W$!P&KHt)0%J(<#(fumZ$$DnosU)@woUYE#{61UrR;V zTOC_yErjAD&o@gLiFd*1iqO1*#c!n3y_omV4i6&txf#Y2-j<RHX{|~(D5-0WXqmeg zPn`d7btGgr*V;zJ+m0rD1^9hw;n4%Gk5=q2`^}ESm8{%2L29<t|JtPiIb7V`a7MxW znyc1wqQk$H^{hT3J@sy#mfOW(5^@Zy%I1$TcI^(2x@2|`5en<2orkm}l3`<0E_WAd z3X8wc8Lw*l(dWM{HnZWHgTTgDZcV~dFE`JMX`Af756WuzN)j4X6>E~|+N)=`hMkiL z-4!{oBLS%>VXYZXqLCX4Veu;qXuLUCIu)O`a}V_h66HSzZ9YF20NbM`C7(h?g7)xa z(K2g45ASwAy@Qt)S?z-c(kd~|u&14@?sd;X|7>^@haa%H{b;ha5&FXUGwtww>>qe} z;Bo9d-x#{q6{b~%m>X5`JsTQ%+%rgmJLL~9Y<mz9PK>18grI?cvIeB>8v6RMw&9I^ zL*XHcRK=e;>A^6<J=(aNs3ExqcT?6mgCgTdc8Rq=A{LT{BO!@DZ=H(xY@wwnr7Q7| z?7%hom069n;j$sM-23AgT&%Ow(`Tu-sBgc?)3bv6qHGJdf9)1d9t&x24bhl{ZYHE> zW)esI>em;;0(#E#;N{sJZ~V;<W*ubL-6U?Bs<G=e)=F@kP_=Pm$yLJutHte;xE>`F z6tk9Xh951~Wv)W?{z#jRF@14{!LuQVVSOsR@!PY<2)55H(_!;X<@{aaWy81co??^f zY-uD%j82D~=%rMyQ+fizr9*Dq87ns584Gha(iZ>OuO3*D{0ZjdSC7rcfF=;8$&Tkg z3fq;)LgrWCX=IcJ%J<)#z`nP!A;)Ukt+s~)CnfS#1B5frvRWM-9CO56C6`#Qd`$x~ zGO`*UGL5Ui5OX)TFTF1zJ7b{917jB^Jf6X^DmJNyt%Tbybigc}nz7;~`>!bgFAz;< z?hh-~tl_~E!WaDJJC(~cKtey4F>Ux_X0$zq<sWOGcO@wsf1w{f&^)9Sq}eWD-dY{Z zz^}y-tHQoInYkwQQUCG-qU8^<M>oPWp%2c3SDv^9)(q+trriX&7`b7ZK$?A_u3@IB ziUjVGLs8$AU|h^BPAZajDzp4ASNW&(lreR-^Pku`Sk9v!Pb}y1T3uj~(nf;WiOt`b z@N%X1$Zb8{NKEQWN0ieHI-JP5@3*;sTk)0#+|?p8ZXwF1+RvwYUM*<(954UE7Fzz2 z&VU;<qP~KnNuX(HfY12DT0}CN$x>DKm&Q*&mxavXL~o4-%aye64|ipc7oN<Mpzbfj z-q)>?+t4Atg<xeX_Bcx;>f%UMD>=Cs6fIRbcmjTb8hRyo;`dCU3CMu+Fdvv3=ii^= z%>9|WD8LO$UM-*wE5>6gFL$xXlz&^v9g{Xj-zYfa$x8^9CKXlrBh8gW>vhd(8yz*6 z^Jng_``vo4d;NMI4jr-#kY(Rm_ZZF=m6Mom4N|yL{_Pk1!Gieh0K;<>n;k1-(>-k> zJ#xQAH4Rb7M>7DB`xM0p-+l&<fo}sv(LY*Pc5NM3Q@}4hRv?rFcLZaI>sOV(p;xqN zi+sd}j6Jjqo#=V7Xb)SwO+^!BVDN`yxffs^7v`_YS{9}@hz`*BP{80HmJN?_fEz&$ z-y_4D6!%BM$s&zvw>8;H^N~OHV|I31>o3o4GARPY$#8IleYpj0{NYS4<2p6Z?<EUp zZhGZBMQ8`~qN*I>oV6I!ngp~ZnEaD*?<o^@u9VS{WzE5rlU~Y_pK9L^L91%=$Ko3e zgpRrxKP_`qKv+%K|8^vm&~m|6xGh_s+j_!OARRF4gIZS^!%HcRi>%NWI;hLyQGwKH zfVPSo<Bb_cyVUlzVhc?-H~7|BOt=%hY`$<-%MhRdCqUXN5>ymBwUY_>)nzV;b)9Cd zUb}+%0~IPhI+%;@aw_6OOn@ZQh6d(tvs<iOldp+|uQ1!iGni>=JZ=ECI~u7B@v@hg zwLn_^;IM|Y%J5d3=)qxSUcEXOX0g0@b=>h*$)9sHdS}#P*6;7|)9XMhyRz3h7tQxf zg^H>!uf=uFu$s*lOEJ@zJ`%r!Ta9GWF^!H?eHBB1rX^jv!4v@G)QsEzunG7M7-2xO zZ1Ec19*C3A7A*a0#;VJ+kE2Vs{LO8dr{QGP1cy>w80`7kZRvdk<9Bw_8H;|x?tdh& zFKBCb?XUgS6b07@K`qrHX`r3yIx7^Yb)x?P;y%Emrn*qp(EmOspA#KF+b}`09Py`k zJQ@mK)XqU}J*|;9R%bm%XV()}{^jRAJJ-!t??XEgcf_IUqwVHj(BsbuhqF@mu%L`S zC@0PlY^b^W<@9Lrp8fQG{nh5Y!Fo67dwRBvna#Tg{#vOGguW?!87d}n`RZ7FyC1TJ z0Y2XSiVD^!y_$AU`k29f5jW};=kj5lfj54jkAqd1>?|`WXbNs{XbXp0SXwZLHch4e zxV5tN1QPh!7k?j*jm>fuySX9onVeDsl`<};ktVO&o*W<`Am71LZH`2Xtit-mVo#|O zS=^o-uT>v<W(p%A$7NGyBzU?*m0IUW67TD{zcl#yzzpd=|G2~bS;^j>EEc?DL84HA zuG7ckF@%b`*yzY|SP&4qTWB~`9FCeTQXM0-99a8kR^a$_@u&kxO&ja^{y><Jffpmc zcYM<I@7BHy&CZ3tg@FmK(IBDX#2?(@JHmz%smtd%MwzKV{k2YI?bpa@6%#%6&bmO$ z!GDf9%luiJV$B3ogOIny=>z~k%%H_?cMM`g#X3Y0DDDNc{b<Q>eeHd;+M4EE>$Hu! zb}tAIlrzGP#Msyge>6ZNWy*l`%#7lFQ6**3j1oBambpVnDd7?E9C+h)b!P6%#6!%4 zhej)3z<m};&9jlaKS7zCa^25^h4{vDxiY-fMl}Y^{yjF#8D`aJn5&UTOP#d)w@u_V z`*ZTt3p$QJQqpd`#KP)GF&DS?EDwkNK}(4Xd!zoc(&fpqrQ`mY9=ms~Y>$Xo2$?P& z9WtE3YCjDm#3X+I@7{K<-n~Bn=VVR8QI!J~hqA9h%?m(hhCC&(aw+Nsz~=)o<$u7B zRS~8vgF-XsSKLvXSMCF#t}A^>U9S=*elH?6<2iCuXa#GQTJaaRd_EMRo%dd>vMtIm zF8u^YN5|5dnuLmqicvW`8pR|^a~^8nLV5+66r4vv$)StD7X*NgyAuTik@;LKh5vSU zsJg2$M?eoLMR;droTv%n4Tp(_5gNa`<<1X7enEkAmQz0#E|yPqp{LY1G@|mOF`G?= zq6)9lQaN6OKnp$OD06s&r|SC;tEH>`u>!FqlsJ;vDd99Tp5LjCkT+%rS7eZ1dl<L< z4O~vJwkty5PCcMA+5jI<gL(hppoFa!>-yf!MD|fbUJ$em$3h@d?@xqLvBEdoo2PxO zq-IGt89>TNscfFaGj&!X5-gx0FFZ;ZU4~lXzQA*|04dfCf1n_r?KrufR&{rGp&Glw zAcNNw8UYtuaaozw0&kn^v0bhNDD<h1!E!Ddo(&Gmm(X%jN<*GN6=3Q~qM3buIK#vs zIrNRu3l7B{J3B8VhBG7Q>Z=9URi{03*VqpBIpVcU$V|to*qAowN>I$Ds58~JXf(Y} z<zy<@+M@1z1&oc<ZyVEH9M8$3g%wZv6Ur8tbVzpBSORLG?d@^$^jbU_!%r7P;N2+N zuWF*|`ug+VCf#2|BUiuR4sK#ST=Td&+Mq3yJ$_X`RKB_`u_K>>YUyS~m2-S|%stFK z8$^gLF*9TyN~AoG4N8=F;;EdC@rP=>yx4mBwWV6!)E25S3J2WrROvc=M*J|bD7nEb zf15IFvwwI<ao!UDJNO6OGEJ2(UyQ-3XkSa%^TeBy2-}-W_fPsFoeg!>or~=6vF#of zGEUz0r1VD%i>t^zy>r%C4O4ol#G6`L9$N*r4?5EweRcyR6yF$RD$u8po}V+&U#<7_ z)CJ+FK%63J7uSYX6DHg6S7XC0VaQ>0HZ?6@FO~w1K9KTsQ{Pmj)i|wm<or0@HKx_B z#H$b7tv-S^!UWieNj;!}`{4S0U5A08#BWnox)`IB+B6ZQBT+2nTYJ#)D`=toW!?i7 ztRiGWwt~wbAd{d)|1B7ts%&fU_~*4QaQ9QkIV(MGmrF>)&&Zz|&$ufqD|;p;@v^c7 zGnpWsBq9d-rek4Y|NMz&Qw+L2VfTnqm|(HP$*c>Axjt5Ttjk59*nllDcv~)*5f2S{ z0q)(BUJpLN^(pt+I^wx;a#whsHz#pTGzkInrE}KzB)40dC?8L^G@E=z8>|CZ%ZTFJ zzE)gk=QL$aPF(9``;V)DZO14<3dFT0v0jwakBb_2$F(8KOPB!m)PUb0S6Jrw(|Y@> zGF*W?jMMpI$*`^2_7MPpvnBsGK}Za=n4@47A)2t=cgp<;FQDgIud#s)Tp_@FnVa!F zpr<BZ=q(c5vvo>H1RXk>TCeE5hTG?E78qN0Z0J9TwuI7{25(^=t#nfKla?yfYV~m; ziv4*HoR9fs!5|qTACdSGD1}4=O;?f<9|g7gHW#c<ad5Y){*+x`5NtE&as6A94$z=( zY%<eULHvpU7p<Qz`(Lw6U#^B2)0BOg>NPt?2GQ;%VSzr!TQn~xIvp+M5?|fX%&6HS z(%}yew*Yp3QAq>ud?BVe{9?Z>kS_}ChK5!B*_mWnt#p(Voq}_=%nB*YX^}H?te3)= z)QnraT4u-JMH#p1Zt6O66h2?|2VJLdPpz(Iw)Ya|FWk#dSn-N?yKoiW?I|vtEjQX3 zZ5Rr89KUTw$k^P%((Tp!21nBI3oYd%-op_IBCH+ZBU7F%)fEoA#fDIlN}Ln9zete6 zT6+SvYO__aG5SYKk)g{EWR#PNqWDRd`K}@1Ng?ac;`ev1F+-@HLXRFF^N0{HxDcVv z)6=VirKSN98bL=4aEm%FhnsUF&%ZOYFoFae2;*#Mej}H9azYk9y_(j?XJ=6_84Ofm zG=t$JdTIXBBqf$DZhmHrhIXm-&m4Me2f%l+{V(i4W7)&?K-l1)ZjSD{9e76H;5g*t z(su3rv{CrPHqU7)GoVvmMsu8!_4>~tY*YB81>>uA?$zz>`O%WV)7|5m<>OopYeBUv zU7E-|30u8Wb7HK#HV~)|=hR(p-v^m~l;4Bniw6gX)814=(0a<D>%%iboxp+N3T;YQ zePk~Lr{rB)-595hpq%wHzfFyS`sHfAC40HK(&>yh)hz%0DGRmjv*)D8aqPS0`>6I- zm%r=|4lIO;@}EPkNasGTC7Pfy-ojEe)TDaoa@3|4r+Y`?jr`5-fOvgD*FJuAui)S1 z&?&lz9$qd^*hjRfYlH-BY+e61qgiTuPclwUPK@MnAExG`H<cPHB2#Q>t{Sm^QZLJm zz5qE}_Hj7*4pV^Es|mH1qo<+DrjJ$baU#e(@wAI-QMU6C>;A?`ZGF?phHVZ5?@b3{ zPLn=(KMzjVZ@4Yg3<NBl+qR=`%4DiB%LTb5cfqs3BiLx%VWI6NCWWxa9S_vnf8x<{ zH^9x1md-I(IPq|eI_{T2lNEZ;H^+vg>?zgE_vIxeVP98Ge5KXn><ajD2GoUT*QO^n zoVocP+y1s{cj^iDr~Aiccb$<xEEST@f4FpYV<kdlvv+phUYITwe>pVO0sz%5bqL`F zsx`cku2wiLt?taVp%kIXBhN}Xi1v2cJ9uHe;Pg!=?IL%w^@uxVLk<?4XppCu33B@a z7qM}u7AC93pp9@hNhuYOE-#)~afpog#!J1OeHRrcRd;Uey1nB^{JYVtqytKZL<2)O zVRoIy;<+_NM@08Kn>h0rO!dX_SQGF=Ey_}?c?RZ_s;pB9FIa`4>Z}Boz2m-hyT7~* za>o3UfQbujiim7X!2J9?2WRVk&>cWgD=A!*?*G7*21twFQ739lmGlDl6NQ)f-^- zqXo#%O!HHz&uaq4ulE7Vbx)s*e~X8-LYPCOYa*=;;bfFx8Xagnb=+$2^t7_MIV%yW z(<IpJ_V4ZkTxPa+buH~~CK+we6v&XKRT{w5YBO>^k(Hin`W^7gs2q=9K;VPxOOWfM zHAd>SY1Pj(mPU_l6{ypSieN@xlk<#Wnt`zJ7Qz1Unvb0n*Q1ZNfb&Pl&rEw>$9wEQ zf@3=a1ym*_x#u)@L5O!-H=!Dg1XnZ*lOfy}3%<D6QL^&%`1%$UGP1>Z8+*w_xTJ4C z_DIj_VljNv%;0NdM}{0^!WUkqTt3Svfliq;1dO3fB1{@mW=d1i_{R<ogP`(+$%KW_ z=f{}EriaPHB~p=Ag;Fh-?q?qrRaHfSXtHVg*3iEBl@Kv0xDqU~ID;t7n?^@~h2`m? z&!690@IKLLZS0A{RgMqKX|zwUF<k><+pFF@+i?rPm?k^%+OM!Ta?##)MHQ-2hXZC3 z5)voXD-1dzz8^(Z&vZQVUusyv#<<1+v`E?AtHuH}yf5}}EQ&1X6~uOZz3h3~+4^Fl zC;P}^49Dy-Nx1&OhaOyrJm(lQ#tG1Il<gJVF>fdXuiv#9X=n;LQbV_Y)hG`?{cUbM z*O#j3Zmgl@MBVLgRVw{{pb3UFQte)Q^9#dTFFb?Qh%O4S3BFa%W@m!j6tR(84k7K? z5`i~7vxiDNB@)lNYTMh};&(kr11c+Jv)U?e)9jrB?+!ox<zGsoF7uznDciK!#*Yol z80T%Ze-F=-d6=pF^f4rRCo+`=vlez^^+e`(f9&S0%fGEX2(wR7YQ1DvR5>_2@Polt zcSJ!64X+FpUA3xfzH7mp#!xKr=<;|t0aqDaMFp-%+dJL-4OM4EebCb7^7ZuUH#>Ls zwTz%gsq`TF06pu!dz;3_$xW48aoDlli)z?pH~qbwFFth&0j?3>^*pX=D04<NSM-^& zs}IJhozHK|csT8HcnQ$R`F;2%t5w%%NS?`QJJO+GVB_<d-TIxD@V81{Mm|F!V}d%j zY5LX2u3K~DaFXk{H19x9n9J7>^PaCrxJ7p4v0w`$!Qk_<$8`VH&$CEg1;QieD=>FX zKE=;N?%&e&>-Og6OIh_RN3;gY<<Mk!)2wOtGbcRD7>O{>H>6%Y;k<SV%(jX2ryr55 zrnYvU9L=k695MIcxd~>KTIP#9#?WYeg&ld0RhXQy3FGv9+U4<?r`?#bH^)0~ogv1Z zx^I19cwuUDW!xzPSGw56;Ev~G*WH4^Qw531h)3BLR#{axdk2SqwNm%W>?5O|IsBVd z{Le9EAXkI15(mn)p9wdX(b0MX5k2)>%<B2Qd55lKSx)5&@iY<oh^;~ew2nsb3JGc1 zW0dP@+w_Gg1KvE-9o;EOyy|Q|X*+yR9k54-*h{<q2@N%s?O~IR5{}d$?@9a}nbvPM z+ua{U)b!4eY+hDiH8R2jsK>Qj;I&kLl?Zp&HY8Ngsw=nJ=!<-MUVDB1u5zA_N%G7J z=UN%bw}Gc2LZ^Xik5gC-m3naM@ah+#aLdeHkLn@W(TOY?({9E7g5WGMXR-Od#}B6p zVUfH&yktT3Hq_0s#tM+Rt_qwq8?=?YB-1kqeI|<uc>gwQfL=lCdmevG_rn&;y{T)c zSO9JEX!M6<wP^{SZwy564(iUI;$V}Zj<>4HWo7a3HOGM8xhQGkXQ9S;MM2(VjrFly z{;7J}lzL(CUG<)4+8@7fv8rc{D1tx|yC2)TH}g+}ST^j4hE}A-Gb)5~il6@|<2WsS z$r#TI;c3iVQ*yQ>)MEyw&k*84H>V1fW@c%I8pJtBd3+}*U+x<Vw|kpAQ2Ke^gUqFa z?>3@oy3z^n(gspzv?0dzhycK1qbp5}pZKkjvX%OnQznzp%BQj)W1a?XVtDcL`_59s zDaRm9?;5<PjMx@=zfrt0wY1e<#&ca!-);cVc6%h_X;$i)&^6!^<SFtWdKh9^mL9=? z59z!y0#}2DeU3<hD=Q*HN2@3A$w{~@AM#L4{xUTq<yJ5d^?uDNV9Ae9GEO+7e@`XJ zrao%JlTjg6FmQDc151=+B;M=_Jl*CiyvGt+^H4p>9<_&daz;#?#+#FJ8n&h1U+&JX zt!b#LV&*sz!y3>OEI2#wedg<jZWRd;87MclzDmHW6%sBpq5=vN{N2dVQ!W^D%EVNb zQmR_}LHU&)f~{KNL?9d_lDr?*;0^5!;~<{%SwzcU`=Fp@XXj*ff3clDVBzKEH8l$x zHFFi3U-lr`2K*G(4oJ^Lj>p@V&kF~cxH&KVdN&ZJ7?P<u>k+eDsFb2qUf(%pn7N6E z6Ww|_A$EVqBH7!%`s@M*SJ&Fb?^rYOV|B_>4iz^vq#o))tX?|&k-hSf+I{iatF2bO ztD^`em14k$-vhpc8=F?#>SBJC<`J?}AR;EJ?Wts7BAaC_nBYlPOHs0}>qIO_)W5VD zPGt&$Q(#-AG>@eJS!$yBNd<ysT2DXOby#Rly6nE2&uP?=ZS2~bBS#gSU6o#K<d>ZI zU;b9X1stB>;<hZY*dxz9$oOdK*VRZrNZe)Jmy#N9oidzTrk<uYH_$-k+qJ77_%3X* z@u2l_wfSsENUCS#P#;@W%;#4%`|l4t2)o@DpM1O;gs6(vi2G+Mz(!wcCMAE(zwY#V z6*~Qz@<s&5;!mONk+<nTa_eWbDV=e=m6B>aRNrR~88#xepJjA3GRRE0vT$T1fVsa4 zINEUCt~6%1pLT^i)&`=MTLbzhM2K4~2p+E9Toi(q!0c}y8?SE5R(Lp{!Vaf+LO(QS z0f(aIhUzGze^{sdw$glu0@OjbBYy)_&<8Zp_i|GN(NLTr`BJHk(mNVjae{u>xBhiy zY7Hw$WT-C>2cu=aFBqgaF&YEbn)dzsb)ByNL*6c9Re}ZvMzNH&^X-Td1yIZhi_`u# zaFP4~*AlF?^Z@-cf1B%{TLs`f;R4*BJt@f}xn+DV0&D=SDcyI#zi4*sxfLZ~j9f*x zPiZ{&XO=f69KxO5wiGm@?sZO}ABrZk(0V7aT@g0%PD2&cp|rs?b`=AEAd+i~C%zE8 zjTIHeZn1%o_$OKIZI$2T?}fyhY<y7~eD(i?q!{D7`Zb8N(Z++=s#0s(dbz;sIP_R- z@9=ae#bkibrNJAPVgOafn8jv&Cpio(am+n<sc8WyM{0UpmrjzddMe#i=iJ@Pk7QWi z&__8bT8Ic{ZksI$R%}<4o~1249u$KVGR>#AHr%p*#gTYE!fF1|aqqEQYrZP__!H}? z1~o#38rkpGo3b{f3{;{tU9r=tJc~;?<R6*Q?P7~QZ1D`MxIGP^n*{7L3A!x*p5rnW z_CFx1aNhL$wf0UL@Z9qzM@J&)AG=9*c1g?6Q(cX=>ew*w11##meHTqC74#vB{S$;~ z7i%P=<yE=%sM$E;&y6;85gNX~U;w45-4p%u7*Hpr=dvmb8_zrS*UijKuXEdwb?Mk= zb}{PA74h8F(yPFq@!41ylG!OQO%`-;Lvb*BQ)J?YRXbpiAW$3(M_m$}l^+^$z@$d! z&~mHP;yN$wZ!=$1MAMklCZ=Xr#v#D=1*kIxtl8Ce@?-_>36g(uI*pD;Gk2Cng7sV6 z4O&7?5)r-{A4vO!Vk&Hvb(Gw}E?$Y{aASP)9+!wK-PP`1^F^jeNRwt=WnY|AL>g3X zd#~xmDK)?kp)e>Vvwe9w#B|)g_3SBGc*wm@f)L(W`kwpoH&3=?u;?hHH3~j>R0mID zD?a7OvQjdWVwHf|^=^=Tc2JVhBQ&D}XtA26s93>;FE;|Rc5J|ZC-Q3|jL-YYK zqBofAST>o+%NN9}1AS0u&e3t+KlAfLU7dl8&8~%yoxa@T<KtJjSjiJME6-}POl{7) zWyzZ9bo;rggzGEGjo8U7HNWg%sh1=f|33To?UxUGDCS+h;Hc+%26R*M0&rI7b2cl+ z9v{a9<MIlQYS9-8l-0EW@M+pv?|A7isdnjbiF6Dkl!Ssw(%`kPooh2rPk>XpFPdAi zJ+T3H5&YzX>f_VCrgO`nPUQU7!A?LnuYG+lWAF}d>G4F(uw%KPk6bSqjgzek$DXqW zOPj!rNxG{EaPg1u*)4RmVQ>Fr=k9zHK7SSuA8K*C+S7v15bYlwSQK>^T8SPX#8WS- zYqag|ejUYsy_yfC-gtW6vWJ~ftGQ>=bxzU7nLnaOB)5ip^bTPGXhuT?wzR4J9>L() zibMFM7ByPYp(tH{QAbDK!$SbM?L=99eTreDEuZa;lY@hU2c)};qH=7$@{(gmw9{ap zBxOz+PqcNuz)LfHzVz+R7a<rKZaiq@O)`|pSX8HpNsZsSC0*~Gff1Qi0H@oW<t6%w zzO{t01u+b|e|TIDA8o$5@evgh+dtYX>MRY1qwR2ToNNzXPA3s+Fe=&d8bI@PERSX- z6F1ivppOo|l=@4%%G#!XTbtT>8G)V;KT3)m$iW9Tx-;T44)94cdt~fzB}Q3gdp7vY z?4Lh>G>wf_pVQe+*3wvXO!XBF{rx}DZl(PVWqq$p`=>t9yj<u@+PTLKde6siq(*y! z5u)}Td&u!=8U4`^X=kN!27Bx@vsi@L6fbKhhkgP%2~W34fxXJK&j7%GMINNee8W($ zx-1<ozCJuWl2MZW#ut+mRuZRXNl{8xV_ZJ9N`+6ZiyrTkjF!!cy!*|s&dDv8zF#H! z0)USzjoD0!zIB<?W6GM=mHq@iXiKaBBg?r=bl5Tet9VF|XN~y<=NKx>$|7bOcvVzY zSh=|VE49r`4(et-l^?tF3ou>f-Gg}JnqV>&GF$!%DyYSP|E*C-l3$AV@D8CU(I!vD z-i|yWb6LOdp0B*Em*}u3x9BZRSFZfW`!zr5U!4<&(?f%W1v5@gR*w&{mpOh?@K*0^ z<Jjy*_S@SG&u+eMJN=%;i(T_(uX!r`E9q`#`_;pZiGRO_c3+%t+?ZVYB-DDsJNFEY zELkTOx7u^3dka-f93p~O@MZF(7_X?D?98|%r~Qqp+SN1aKX&FN?OQeJfAqhN={}!6 z0sCZJZHZ4_y=+~7z+&#bZJW1#F1a*s{dw`LY|HsyeyO^2bNiKOj-^Zs!ye9hbxi!R zUq-Cpl{V8^kJYAauV8xM!Lf>|mS@o>KlLLSg-e$!F5h2yOtruBjM7`Z36cVpTP6y* zu65N2e`F|Udu;9nCJk<lkoFM2BfciAImf5o=~ZP+YCP0$T%)UUYT^tJ^>^PT*1M=_ zusN+|HT;ydK3oWBUBd$RdsE&TWLQmM7m@N}y5e54<vK&;I=&~;Vzsx6*%pQ=UMV_u z+Na!#tLXyM3Z7Lut9d4FQBCv}xGZ`_X(l(DlY~Z=ZeQt@P-_Mkj?nq70ba*;S319y z@wYA!3>RPG^KeFc!J04@sV+NN*V%lm33opPs+0&WZr(D%h*g6_)k87D>=Q^tn5#A4 zNn=q*-~x{qN@^lqEqX~#3kNy#U;Vcu>U^r7|6T^3;-OmN8c~vxSdwa$T$Bo=7>o>z zjCBpnb&X6y3@ofn&8<ugwG9lc3=ASp{Iy5Xkei>9nO2EggXUD%W}pTIkPXH8X(i=} zMX3xKB_##LR{HvxxryniK%AMJt(RYvzURE`T%cMBklK)p(%d8~E0_G_(%jU%5-Y0! zpweOn!{z^X>!Ydh1es!G<&m11o>9VJXldjSTNnydBaUPaR84qhN=XJt$>lYQ96%+K zNJ@M&b5lzy3sM=tuF@~aOSj(?=7(mEZwOEogQ1zFfuV(^fw|#gCDHFdCHzR{1ZP&I zG8i~HO<8331}GJVBo!Lu$&i+rlM3{@er{e#PJX(6T4Gsda(-U1J|f8VEmMqBk_=Ll yO_EZR6D<r=O;b`+Q_L+5Op;T~%q>hT^?*9`fbM&Jv8@<r1B0ilpUXO@geCwpG~jmt literal 0 HcmV?d00001 diff --git a/frontend/public/assets/favicon-16x16.png b/frontend/public/assets/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..3e04f12c2d8720735d5154369adad7883113b8e2 GIT binary patch literal 1285 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a><YADU|>8Q;1lBd|NsBLfB){? zvnMpbZ_<Pb=Z~M*xnbj>Lx;Y6`SSbs?*;Sb8fvP`2@6Dp1l>7%?)$Y{H!ojVx@5_R z4<D8<TdJWTt0*R{EFtP(VOE@*otKersG-);+WP0upNkhRnCt6mE6QogNy`fH%Zmu= zC@UV=zyI&Q|KET7$Vy9<6A@ICmMqE5Iez5uk%I>cGSgM1CE}wazkU7m|NoyeZ+@;h zd`3@QRY_c=G%x4tmoJ|`eag>B(^QZ<v~TwRKbJQ?{4#CdIekqHMNuIu1HG%4FMa?1 z{olWT-@kpkc>bK3j;5T5P)(`-pC1R$z4+8PXSs&FOm1rOmsjteJb3v2-Mi<{p541~ z^V{2xDKSwBB7zE{LY_|6uU|htckWz5PLZ*`zPzx&uOC1E{`vFb`SYhw9{>9FD<w8c zK~&I0N9)?PYnLxw3J3}J50BPWR;Vg2+`M7^$z#X1Z{Ac<n6IrMYinlm=;6bkKYuni zH768SXe!DpiV3MoiEGKrs!2=8iwG%8icje6`SAYzw&Ul*;}Q+jRn%mplq5tI#Do>a zMOCFG4K&n(yzQ1Qn)&V9_s_ro-g^1|`t@rE_V3%ce*LNy%U3L0x^B&y>({P*e1GZR zzt8`GQTYG&|Ns9s_g+5=jBmyyZ+91l4pvzYAcwug)7O>#IlBahrIlK<x))IBwx^3@ zh{WaO1O_HIwKTCXvobz5jR#L2J$v}{vABRp6BkE!M^|T;w}+SK>4u-1iX8UsX<9UC z#U`!^t5+>sr?_n8(zT0Mt1nxUpKu}}B_~NMEA8BYghb7(tkm4Y3EAoIAG~;S=E0*^ z&+=Y9e0lW2+sCV)JeL=c5Rnm*5|a}=DIh8<oGmFnf5MC>v*sktnssc#yooO-Or5)U z?(FID39kYojs(O6MTN=51x5yl>P5u{zdm#zApHJ?8&~dJx^(N>y^A+liz3SX^W*=w zbWWb3=0B6G@R!Mq<;%_|C@?WF#FQFkUwaiO5A>#LiEBhjN@7W>RdP`(kYX@0Ff!IP zFxNFQ2{Ev+GBvj{HPkjRure@+IPupWMMG|WN@iLmZVj4KU7LX#6hJl<=ckpFCl;kL zWR#Q?6kF-*XXYlRrvh<ia<*Q6QTm?qvU7oIB|vIJGD>rktgKw}lS^|`^Gd9&0)R@3 z84Q>I->r|P#uH?Um6b<oW_m^mgQ2C7Lu_FvP>nc}IZ!p>nJFb1ASIXAC~^RmNFpim z&CE?LsVqok0J}=RATQm1Q<xu`Ildu4RSbq^mIj6vmImgAi<Lyb1C{V2nG>8@mC9h? z<TPcG-5a1(7?M<IkS9Z0W=<;5>-xEQB{})&`e}(}naTNi#rlXK*SAbDPDwIIO*TnN vO-{5hOf^kOO-(VkG%!g{F*CO?vD5?V&;z>f^~JVgpbZS3u6{1-oD!M<{l^vr literal 0 HcmV?d00001 diff --git a/frontend/public/assets/favicon-32x32.png b/frontend/public/assets/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..9c20736cba02de39073b826b001f3e8e42cddfb4 GIT binary patch literal 2259 zcmZ{l2{hF28pnS`P0EtUlC@+jX2xK~_*=rr3^OvwQiSm{#xiJzE@~zv^jFr&&KX;a ztW6CKLU#W`Ny=8&)*@So6z;g^o^!kZbMAT1^FHT1&*%F*&-<S9p7+LS!YNS^c@Y2r zM6otjM7~$qG01NIPK%0K;X5JnN&HCws7c=U%UgISNwOj00U$~p0ItLWzy?2b<r4se z!vNrm7XYBM06-=*kLU0QKe5}#_LLQ{y|dVjchmWiJ#;J{v*+U;S%?u@{h9`j&qQOb zPCB`^&SeA!NexMNGS_-<HvZ+~nrqMxL<9$MYR1gvi%*kI9wg=pISOKx_a1Y)7m%<~ z=$w1%sB~$-QP!Om%e~kx4pvymU(%?=e6OUMAgj%*St@OH*RJ-&rE8CeJSWGp^Pg;1 zceH)&h!`v*mMT$7-kH$0J~FpIj?$`5<Fq>b?@%s$>R0_fKaRIY*)bTV@PRhOcq?iR zHL5GKd|Z3+=O*d8)y>=Hj*bVb8Xjfm)V^0&;5~aKy|y%QysBtl%-}et?S^CYjJ}3_ z9-Ga+Qr4c>-PI+l=z6O~Oaa9{#9~_F^Ro)S-CckET+4Lz@zqPKTa_L-w+voX-I>*= zoU_>)ga<a7C0y!QkzvqV_VDnqb-U7Sb3Fc*wwAWj0H+~f=JnUYj}P9tmHEsE2Mk3U zXdA-Vc^^RVO+JxO=b}~mnsyjjHu&Jo{iLL%152k)v}|qu{PE6SN%fWDv;I}Jrb$#? zN94V8#20hZ({{OriX&VKHE3!q-x%e2@#ccrqa1R<rU|w7=lR)~jg2TJPfnbzjJplu zW-2aW5~ZSM?Jh5JT^f-p7%P#*op5QooD)A(iI;;qZpw*VUmx38Jr_h#f*Yx&`RBx` z3ll~y+9yBdSmCl4_qNaA`y5<ckP(7R#xh=vv4|Y4Tw$F|n)Dj^-XCJDWPdN^8698T zxOxN{G#pFGDb8d+eOfRmZfah~QN#}r)z@S)s~3BXwKQ|CD<%)fLP9^aUeh-aO40IR z5jDrWCTBA5S5_(!B+LbTBz{#?oTZ3!C&KE9J&p7qB8;uJ^r@L?(Paaq;kS758xC`! zfED&~i@^;Uu?DAWZ4gokvAm){mCD|=Y+s(}Fkxt^__nx2;>wZf#V@F&R+IOh9Rq#T z=G^RT;(TQ<;f=|X?9Z(YMw%o}%a@m%x&0{B_|VipKUhL`y^~D}d)hZ)SKD1M?6|T@ zziY>!VYQmpr_;~pC^Tp0zpz)noR^KkU`<qGRV2lh$Lp&OBwvmry_s~?>FO0YtkoPQ z<tSp?^$ny=_P!3!U#K{T9e*xpjbMhs#Z6aQOE5s)tNlrDc0)XQ`w4s4DHDk|_U}?{ zt5zA|YBhp}wi!BmKHywr!v4Yuim$Cm%zqZ}a&VT8{DDY!>eui7kwFk+NSS}}EOAJ9 z{sNX_p;9t6+FiU7A5B#$)U3a(l&+_7V0MnYJ#G?fQK%-U@g>P2dL5EwXoR@n=H!ei zle-(#{*ttr@JJZ)BHlA^+QMUQx_AIjf9%;@GubMyX)@Mc0fPN9nC}=j#`}v2$K&Ig zKdxUgWnG==*!|}(#-+&y*GwLq+H<6d4VyM33>f6dUyY46Xz=V07f9VF`ZaPjl6sbo zwC3$IrizAFQe}Ppz?h+1@R!0C>>`I@_iK5kZpkS(u&CJ?pI3UFtybbTF@>`b)inO` zZh6g``PWOe&E{ui?NZG55DE}SEX+rD>*QN5FWILMEg>z<gAWuo*GV(C`{G+()t%FQ z8ad=)`Jol3F3b&H{y`>J2OHm)6ZYz`JCbTsv;gp*{!1I>ikK!xUUeF$(@gQuqv=~i z*o&o{x+txIJAJZg-#g-*lPqCxdyI{_9NKJ#8ndWaa$i>#Of{yEMby#JDR<=wExe|j zh@66Rw`Uxp(7NIRnxzsJu%pP#s>~Oqp&813>r0moj6XclYlvL>oO(1b(8ySdR-ApO zyJETL9d{Y}k3R}x1tTLPouFrXXQ7Q<&*qP_bjzpulaLa~@|9;#XnAJQhg2O4ye+X+ z`LXwZar^IiMnIL4H5_UJ_c9tHbe3maPBYgnMS6O$v}5O0N?889B$nQEF~z+{K<!Fh z@WICu<?fs~<#P9M#pPjJ0zr&<X-inU#}Coi--mCnl}NO%LduhB1K(?Rw&?3@>@M<U zg4)Sd(SL^9Z$`bH)BKF$2Io4CsVjdS>C`axEPkSDeo-?P%z>EWV$AnbAz>zRBBjj^ zVo5l=!E&srpR^2u+$%KSv|Yb$TbAB^hnFV&Q%BH5orCy*8^OrS%TuTUMHO?BHU2@N zOyjp<hdaY0EZB$9kQkPHx`utn!kXhbB92N>|NQV!wxP@vtL6YqvHiMf;^#@x(c!KM zrdzG}kGTkiS%sVjH(NZ+&@tQPMr;L%DBJ=wrnyUf`*One!F6SgMSDg!x#?8(*mwTV zbc~L1rTco*K{V+C$ajDqOb?+0L+a@1JHb$B10>o2ejElv!(gsmn`Zwb2oCY3_(lGI zf!1xzHNHS)rvoj-kABhn0tooi>7g_<6iNy9CWCyVkOFl>E|44g6!ZD4><;VXA0F(3 zMq7oD!h=C79gRNCM`-~3?sh%&Zwz*)3L0$#Qpo;v0B)pb?h)q9XJme>^B=~A;!F46 zK?cfIA$&ycHv~@!2I-NZAn@<0prKT<*~2r0ztzDz@hJdqXas|!j9^H3@e%1YJ|g~G z9Y;zO2*4~XSVg8^`KdF%rJRXaz>g9H@{bM;rqY8#$WTA;2ns2LN`wCQ%ArQS2wxu< xNYeKKN!}<pXy6NizDOgOKFQY*iPAUH<tudg^Uc1DqwzZcSPa3c(!%TNzW@{h@f!dD literal 0 HcmV?d00001 diff --git a/frontend/public/assets/favicon.ico b/frontend/public/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..41fe60fe0bfc86b76f5ba08aa3412c9f6f895a68 GIT binary patch literal 1150 zcmb_cT}TvB6dp@+b=4hr*BQsvb$69)Q^JRWUZRAM9+W5uQv)fpR;-pp@m27#pl2(i zk^))n2Mm;=GO*2hSbs5eqjvnE6wFc?ZFlGE%#|rKUxLnX=gvLne&>AWhvB#wdU&3r zw}bP<a-6_%+%8(ABF(y<HjX3PX2E|I713xEkx1m%3UBz<Y+H${uH&|{ZPrETuIB%@ zs2~;8@G`>7GGuuM{r&yu?tTJaXBRp;e0cciF<$n)!b~WHiAWSH8h8Vj?@J2C15Z(Y zq6EAp8>Z9@BqRygkzhfb!2*LZ4M`>mlH|gk1BY?reg_M$@z$iFu?Y=zWe}uXnA4nu zO@}ch1F2RAogIX=F`YF%2PWPIjx*rO)oTnpIyR1yBSr93pM)XF1~DTS-pfts>3xQw z;Zcl{{rQVNT=X_FUs7fsQYnTjw*ZTaiwF#kz*}31!oq!si8Z6PuAceT>i%VJ9@8_k zSX=wSbQP!0QZ6FWh^I3rA5&8@)a40u-Dv@Db&^d$&G}1MT~aYRGLD6XuLy-?yc-w< z$VGYO8I}icv19N4gHXa@C~_BqZ*RixaI@NFy9)5}QwWNpu$Wq}6lE2YlOK?7+X;b| zG2V<~%5i{hsNVt@@;_i$Ew}hY0p~nkhSlapuW{9B58{cRl;wsgS;XzvZ}6UO=`&Sc zQANG7A~V}f97W2*3$3=pBV(wpxxjj0v%6XTrKQK2|1kO2@xfpaqU3~GaDwrmSgdy` z66=@7%PQu;NU{BXKf_08mNe1l(dX;Lwzw2nL>KJNd{(Qx-G!)cXktB0Ocv1G-0}y8 z9X9xAZ@))zv%qcVar9Uj=02}reio2J(`akEMZH|pA%E>Uk3b-RLq)}CZM}}+p$W9# W?L=>H55sEp)N86a*!(~E*Yi)&UF^jG literal 0 HcmV?d00001 diff --git a/frontend/public/index.html b/frontend/public/index.html index dae2be05..f0224cee 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -2,8 +2,25 @@ <html lang="ko"> <head> <meta charset="UTF-8" /> + <meta property="og:type" content="website" /> + <!-- TODO: url 추가 예정 + <meta property="og:url" content="" /> + --> + <meta property="og:title" content="펀잇" /> + <meta property="og:description" content="궁금해? 맛있을걸? 먹어봐 🥄" /> + <meta property="og:site_name" content="펀잇" /> + <meta property="og:locale" content="ko" /> + <!-- TODO: image 추가 예정 + <meta property="og:image" content="" /> + <meta property="og:image:width" content="" /> + <meta property="og:image:height" content="" /> + --> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <title>Document + + + + + 펀잇
      diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 00000000..a1dd331e --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,24 @@ +{ + "short_name": "펀잇", + "name": "펀잇", + "description": "궁금해? 맛있을걸? 먹어봐 🥄", + "theme_color": "#D8EAFF", + "background_color": "#ffffff", + "icons": [ + { + "src": "/favicon-16x16.png", + "sizes": "16x16", + "type": "image/png" + }, + { + "src": "/favicon-32x32.png", + "sizes": "32x32", + "type": "image/png" + }, + { + "src": "/apple-icon-180x180.png", + "sizes": "180x180", + "type": "image/png" + } + ] +} From 889f382c1e61207c37fb8a71c4675d71b38de8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Tue, 25 Jul 2023 09:49:26 +0900 Subject: [PATCH 047/185] =?UTF-8?q?[FE]=20feat:=20=EB=B3=84=EC=A0=90=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#1?= =?UTF-8?q?18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 리뷰 별점 구현 * style: 중괄호 삭제 * refactor: 변수 위로 밖으로 이동 --- .../Review/StarRate/StarRate.stories.tsx | 13 +++++ .../components/Review/StarRate/StarRate.tsx | 51 +++++++++++++++++++ frontend/src/hooks/useStarRate.ts | 22 ++++++++ 3 files changed, 86 insertions(+) create mode 100644 frontend/src/components/Review/StarRate/StarRate.stories.tsx create mode 100644 frontend/src/components/Review/StarRate/StarRate.tsx create mode 100644 frontend/src/hooks/useStarRate.ts diff --git a/frontend/src/components/Review/StarRate/StarRate.stories.tsx b/frontend/src/components/Review/StarRate/StarRate.stories.tsx new file mode 100644 index 00000000..37db0d89 --- /dev/null +++ b/frontend/src/components/Review/StarRate/StarRate.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import StarRate from './StarRate'; + +const meta: Meta = { + title: 'review/StarRate', + component: StarRate, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Review/StarRate/StarRate.tsx b/frontend/src/components/Review/StarRate/StarRate.tsx new file mode 100644 index 00000000..6f5e956d --- /dev/null +++ b/frontend/src/components/Review/StarRate/StarRate.tsx @@ -0,0 +1,51 @@ +import { Button, Heading, Spacing, theme } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +import { SvgIcon } from '@/components/Common'; +import useStarRating from '@/hooks/useStarRate'; + +const starList = Array.from({ length: 5 }, (_, index) => index + 1); + +const StarRate = () => { + const { rating, hovering, handleRating, handleMouseEnter, handleMouseLeave } = useStarRating(); + + return ( + + + 별점을 선택해주세요. + + +
      + {starList.map((star) => ( + + ))} +
      +
      + ); +}; + +export default StarRate; + +const StarRateContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +`; + +const SvgIconWrapper = styled(SvgIcon)` + transition: all 0.3s ease-out; +`; diff --git a/frontend/src/hooks/useStarRate.ts b/frontend/src/hooks/useStarRate.ts new file mode 100644 index 00000000..006fda35 --- /dev/null +++ b/frontend/src/hooks/useStarRate.ts @@ -0,0 +1,22 @@ +import { useState } from 'react'; + +const useStarRating = () => { + const [rating, setRating] = useState(0); + const [hovering, setHovering] = useState(0); + + const handleRating = (starIndex: number) => { + setRating(starIndex); + }; + + const handleMouseEnter = (starIndex: number) => { + setHovering(starIndex); + }; + + const handleMouseLeave = () => { + setHovering(0); + }; + + return { rating, hovering, handleRating, handleMouseEnter, handleMouseLeave }; +}; + +export default useStarRating; From 9f0541ec6f2992075d206482f5cf60c9ef064d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?s=E1=B4=8F=CA=9F=CA=99=C9=AA=20=E2=98=94=EF=B8=8F?= Date: Tue, 25 Jul 2023 09:50:50 +0900 Subject: [PATCH 048/185] =?UTF-8?q?[FE]=20feat:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EC=9A=94=EC=95=BD=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 랭킹과 리뷰에서 사용할 수 있게 variant 분리 * style: 파일명 수정 * feat: variant 추가 * chore: args default를 기본으로 받게 하고 Story default args 삭제 * refactor: type pick으로 변경 * refactor: interface명 변경 * refactor: import 파일 명 변경 * refactor: rank 유무로 스타일 분리 * feat: rank의 존재 여부에 따라 image alt 변경 --- .../ProductOverviewItem.stories.tsx | 23 ++++++++++ .../ProductOverviewItem.tsx | 43 +++++++++++++++++++ .../ProductRankingItem.stories.tsx | 21 --------- .../ProductRankingItem/ProductRankingItem.tsx | 43 ------------------- .../ProductRankingList/ProductRankingList.tsx | 4 +- frontend/src/components/Product/index.ts | 2 +- 6 files changed, 69 insertions(+), 67 deletions(-) create mode 100644 frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx create mode 100644 frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx delete mode 100644 frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx delete mode 100644 frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx new file mode 100644 index 00000000..4dc5590b --- /dev/null +++ b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.stories.tsx @@ -0,0 +1,23 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ProductOverviewItem from './ProductOverviewItem'; + +const meta: Meta = { + title: 'product/ProductOverviewItem', + component: ProductOverviewItem, + args: { + image: 'https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg', + name: '소금빵', + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const Ranking: Story = { + args: { + rank: 1, + }, +}; diff --git a/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx new file mode 100644 index 00000000..d02ef521 --- /dev/null +++ b/frontend/src/components/Product/ProductOverviewItem/ProductOverviewItem.tsx @@ -0,0 +1,43 @@ +import { Text } from '@fun-eat/design-system'; +import styled from 'styled-components'; + +interface ProductOverviewItemProps { + rank?: number; + name: string; + image: string; +} + +const ProductOverviewItem = ({ rank, name, image }: ProductOverviewItemProps) => { + return ( + + {rank && ( + + {rank} + + )} + + + {name} + + + ); +}; + +export default ProductOverviewItem; + +const ProductOverviewContainer = styled.div>` + display: flex; + align-items: center; + height: 50px; + margin-bottom: 15px; + padding-left: 15px; + gap: 15px; + border-radius: ${({ theme }) => theme.borderRadius.xs}; + background: ${({ theme, rank }) => (rank ? theme.colors.gray1 : theme.colors.white)}; +`; + +const ProductOverviewImage = styled.img` + width: 45px; + height: 45px; + border-radius: 50%; +`; diff --git a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx deleted file mode 100644 index d48cee81..00000000 --- a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.stories.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import type { Meta, StoryObj } from '@storybook/react'; - -import ProductRankingItem from './ProductRankingItem'; - -const meta: Meta = { - title: 'product/ProductRankingItem', - component: ProductRankingItem, - args: { - productRanking: { - id: 3, - rank: 1, - image: 'https://t3.ftcdn.net/jpg/06/06/91/70/240_F_606917032_4ujrrMV8nspZDX8nTgGrTpJ69N9JNxOL.jpg', - name: '소금빵', - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = {}; diff --git a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx b/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx deleted file mode 100644 index 650a6751..00000000 --- a/frontend/src/components/Product/ProductRankingItem/ProductRankingItem.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Text } from '@fun-eat/design-system'; -import styled from 'styled-components'; - -import type { ProductRanking } from '@/types/ranking'; - -interface ProductRankingItemProps { - productRanking: ProductRanking; -} - -const ProductRankingItem = ({ productRanking }: ProductRankingItemProps) => { - const { rank, image, name } = productRanking; - - return ( - - - {rank} - - - - {name} - - - ); -}; - -export default ProductRankingItem; - -const ProductRankingContainer = styled.div` - display: flex; - align-items: center; - height: 50px; - margin-bottom: 15px; - padding-left: 15px; - gap: 15px; - border-radius: ${({ theme }) => theme.borderRadius.xs}; - background: ${({ theme }) => theme.colors.gray1}; -`; - -const ProductRankingImage = styled.img` - width: 45px; - height: 45px; - border-radius: 50%; -`; diff --git a/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx b/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx index 42e8d14b..b8b117ef 100644 --- a/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx +++ b/frontend/src/components/Product/ProductRankingList/ProductRankingList.tsx @@ -1,4 +1,4 @@ -import ProductRankingItem from '../ProductRankingItem/ProductRankingItem'; +import ProductOverviewItem from '../ProductOverviewItem/ProductOverviewItem'; import productRanking from '@/mocks/data/productRanking.json'; @@ -7,7 +7,7 @@ const ProductRankingList = () => {
        {productRanking.map((productRanking) => (
      • - +
      • ))}
      diff --git a/frontend/src/components/Product/index.ts b/frontend/src/components/Product/index.ts index d1dad1e0..fe9a4594 100644 --- a/frontend/src/components/Product/index.ts +++ b/frontend/src/components/Product/index.ts @@ -1,7 +1,7 @@ export { default as ProductDetailItem } from './ProductDetailItem/ProductDetailItem'; export { default as ProductItem } from './ProductItem/ProductItem'; export { default as ProductList } from './ProductList/ProductList'; -export { default as ProductRankingItem } from './ProductRankingItem/ProductRankingItem'; +export { default as ProductOverviewItem } from './ProductOverviewItem/ProductOverviewItem'; export { default as ProductRankingList } from './ProductRankingList/ProductRankingList'; export { default as ProductTitle } from './ProductTitle/ProductTitle'; export { default as PBProductList } from './PBProductList/PBProductList'; From 0c034f68994cb9465d28fc4b2df472f37bdd110b Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Tue, 25 Jul 2023 15:57:21 +0900 Subject: [PATCH 049/185] =?UTF-8?q?[FE]=20feat:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#119)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 리뷰 이미지 업로드 컴포넌트 추가 * feat: 사진을 업로드 하는 기능 추가 * feat: 사진 장수 업로드 문구 추가 * refactor: 불필요한 styled 삭제 * feat: 이미지 alt 속성 추가 * style: 코드 스타일 정리 * refactor: 인라인 스타일 제거 * feat: 이미지 너비 고정 * style: 코드 스타일 수정 --- .../ReviewImageUploader.stories.tsx | 13 ++++ .../ReviewImageUploader.tsx | 77 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.stories.tsx create mode 100644 frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.tsx diff --git a/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.stories.tsx b/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.stories.tsx new file mode 100644 index 00000000..f8f21693 --- /dev/null +++ b/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ReviewImageUploader from './ReviewImageUploader'; + +const meta: Meta = { + title: 'review/ReviewImageUploader', + component: ReviewImageUploader, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.tsx b/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.tsx new file mode 100644 index 00000000..241e1675 --- /dev/null +++ b/frontend/src/components/Review/ReviewImageUploader/ReviewImageUploader.tsx @@ -0,0 +1,77 @@ +import { Button, Heading, Spacing, Text, useTheme } from '@fun-eat/design-system'; +import type { ChangeEventHandler } from 'react'; +import { useState } from 'react'; +import styled from 'styled-components'; + +const ReviewImageUploader = () => { + const [reviewImage, setReviewImage] = useState(''); + const theme = useTheme(); + + const uploadReviewImage: ChangeEventHandler = (event) => { + if (!event.target.files) { + return; + } + setReviewImage(URL.createObjectURL(event.target.files[0])); + }; + + const deleteReviewImage = () => { + URL.revokeObjectURL(reviewImage); + setReviewImage(''); + }; + + return ( + + + 구매한 상품 사진이 있다면 올려주세요. + + + 사진은 1장까지 업로드 가능합니다. + + + {reviewImage ? ( + + 업로드한 리뷰 사진 + + + ) : ( + + + + + + )} + + ); +}; + +export default ReviewImageUploader; + +const ReviewImageUploaderContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +const ImageUploadLabel = styled.label` + display: flex; + align-items: center; + justify-content: center; + width: 92px; + height: 95px; + background: ${({ theme }) => theme.colors.gray1}; + border: 1px solid ${({ theme }) => theme.borderColors.disabled}; + border-radius: ${({ theme }) => theme.borderRadius.xs}; + cursor: pointer; + + & > input { + display: none; + } +`; + +const ReviewImageButtonWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; +`; From 4650aafd53b03a95833ff29dd81ac09e92e4f17a Mon Sep 17 00:00:00 2001 From: Taeeun Kim Date: Tue, 25 Jul 2023 16:09:14 +0900 Subject: [PATCH 050/185] =?UTF-8?q?[FE]=20feat:=20api=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20api=20=EC=97=B0=EA=B2=B0=20(#122)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: msw 버전업 Co-authored-by: Leejin Yang * fix: 스토리북 오류 해결 * feat: ApiClient 클래스 생성 Co-authored-by: Leejin Yang * feat: fetch 함수 작성 Co-authored-by: Leejin Yang * feat: useGet, useMutate hook 추가 Co-authored-by: Leejin Yang * feat: 카테고리를 조회해오는 api 연결 Co-authored-by: Leejin Yang * chore: dotenv-webpack plugin 설치 * chore: gitignore에 env파일 추가 * feat: params와 queries를 객체로 받아오게 수정 --------- Co-authored-by: Leejin Yang --- frontend/.gitignore | 3 +- frontend/package.json | 3 +- frontend/public/mockServiceWorker.js | 2 +- frontend/src/apis/.gitkeep | 0 frontend/src/apis/ApiClient.ts | 53 +++++++++++++++++++ frontend/src/apis/fetch.ts | 13 +++++ frontend/src/apis/index.ts | 3 ++ .../ProductItem/ProductItem.stories.tsx | 2 +- .../Review/ReviewItem/ReviewItem.stories.tsx | 8 +-- frontend/src/hooks/product/index.ts | 1 + frontend/src/hooks/product/useCategory.ts | 10 ++++ frontend/src/hooks/useGet.ts | 30 +++++++++++ frontend/src/hooks/useMutate.ts | 31 +++++++++++ frontend/src/pages/HomePage.tsx | 10 ++-- frontend/webpack.common.js | 2 + frontend/yarn.lock | 27 ++++++++-- 16 files changed, 182 insertions(+), 16 deletions(-) delete mode 100644 frontend/src/apis/.gitkeep create mode 100644 frontend/src/apis/ApiClient.ts create mode 100644 frontend/src/apis/fetch.ts create mode 100644 frontend/src/apis/index.ts create mode 100644 frontend/src/hooks/product/index.ts create mode 100644 frontend/src/hooks/product/useCategory.ts create mode 100644 frontend/src/hooks/useGet.ts create mode 100644 frontend/src/hooks/useMutate.ts diff --git a/frontend/.gitignore b/frontend/.gitignore index 771d65fe..7c9c8a46 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -2,4 +2,5 @@ node_modules dist .DS_Store .AppleDouble -.LSOverride \ No newline at end of file +.LSOverride +.env \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index 4a339e52..72eab287 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "@typescript-eslint/eslint-plugin": "^5.60.1", "@typescript-eslint/parser": "^5.60.1", "babel-plugin-styled-components": "^2.1.4", + "dotenv-webpack": "^8.0.1", "eslint": "^8.44.0", "eslint-import-resolver-typescript": "^3.5.5", "eslint-import-resolver-webpack": "^0.13.2", @@ -42,7 +43,7 @@ "eslint-plugin-react": "^7.32.2", "eslint-plugin-storybook": "^0.6.12", "html-webpack-plugin": "^5.5.3", - "msw": "^1.2.2", + "msw": "^1.2.3", "msw-storybook-addon": "^1.8.0", "prettier": "^2.8.8", "storybook": "^7.0.27", diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js index 8ee70b3e..36a99274 100644 --- a/frontend/public/mockServiceWorker.js +++ b/frontend/public/mockServiceWorker.js @@ -2,7 +2,7 @@ /* tslint:disable */ /** - * Mock Service Worker (1.2.2). + * Mock Service Worker (1.2.3). * @see https://github.com/mswjs/msw * - Please do NOT modify this file. * - Please do NOT serve this file on production. diff --git a/frontend/src/apis/.gitkeep b/frontend/src/apis/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/src/apis/ApiClient.ts b/frontend/src/apis/ApiClient.ts new file mode 100644 index 00000000..cba35570 --- /dev/null +++ b/frontend/src/apis/ApiClient.ts @@ -0,0 +1,53 @@ +import { fetchApi } from './fetch'; + +interface RequestOptions { + params?: string; + queries?: string; +} + +export class ApiClient { + #path: string; + + #headers: HeadersInit; + + constructor(path: string, headers: HeadersInit = {}) { + this.#path = path; + this.#headers = headers; + } + + getUrl(params = '', queries = '') { + return '/api' + this.#path + params + queries; + } + + async get({ params, queries }: RequestOptions) { + const response = await fetchApi(this.getUrl(params, queries), { + method: 'GET', + headers: this.#headers, + }); + const data: T = await response.json(); + return data; + } + + post(body: T, { params, queries }: RequestOptions) { + return fetchApi(this.getUrl(params, queries), { + method: 'POST', + body: JSON.stringify(body), + headers: this.#headers, + }); + } + + patch(body: T, { params, queries }: RequestOptions) { + return fetchApi(this.getUrl(params, queries), { + method: 'PATCH', + body: JSON.stringify(body), + headers: this.#headers, + }); + } + + delete(params?: string, queries?: string) { + return fetchApi(this.getUrl(params, queries), { + method: 'DELETE', + headers: this.#headers, + }); + } +} diff --git a/frontend/src/apis/fetch.ts b/frontend/src/apis/fetch.ts new file mode 100644 index 00000000..2c89a0b7 --- /dev/null +++ b/frontend/src/apis/fetch.ts @@ -0,0 +1,13 @@ +export const fetchApi = async (url: string, options: RequestInit) => { + if (!navigator.onLine) { + throw new Error('네트워크 오프라인이 감지되었습니다'); + } + + const response = await fetch(url, options); + + if (!response.ok) { + throw new Error(`에러 발생 상태코드:${response.status}`); + } + + return response; +}; diff --git a/frontend/src/apis/index.ts b/frontend/src/apis/index.ts new file mode 100644 index 00000000..ec7e83d2 --- /dev/null +++ b/frontend/src/apis/index.ts @@ -0,0 +1,3 @@ +import { ApiClient } from './ApiClient'; + +export const categoryApi = new ApiClient('/categories'); diff --git a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx b/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx index 4961a449..6afb4a23 100644 --- a/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx +++ b/frontend/src/components/Product/ProductItem/ProductItem.stories.tsx @@ -8,7 +8,7 @@ const meta: Meta = { title: 'product/ProductItem', component: ProductItem, args: { - product: mockProducts[0], + product: mockProducts.products[0], }, }; diff --git a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx b/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx index 30061b2a..72df8c2a 100644 --- a/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx +++ b/frontend/src/components/Review/ReviewItem/ReviewItem.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import ReviewItem from './ReviewItem'; -import reviews from '@/mocks/data/reviews.json'; +import mockReviews from '@/mocks/data/reviews.json'; const meta: Meta = { title: 'review/ReviewItem', @@ -15,7 +15,7 @@ type Story = StoryObj; export const Default: Story = { render: () => (
      - +
      ), }; @@ -23,7 +23,7 @@ export const Default: Story = { export const RebuyAndFavorite: Story = { render: () => (
      - +
      ), }; @@ -31,7 +31,7 @@ export const RebuyAndFavorite: Story = { export const NoImageReview: Story = { render: () => (
      - +
      ), }; diff --git a/frontend/src/hooks/product/index.ts b/frontend/src/hooks/product/index.ts new file mode 100644 index 00000000..6756ece4 --- /dev/null +++ b/frontend/src/hooks/product/index.ts @@ -0,0 +1 @@ +export { default as useCategory } from './useCategory'; diff --git a/frontend/src/hooks/product/useCategory.ts b/frontend/src/hooks/product/useCategory.ts new file mode 100644 index 00000000..b9b30d38 --- /dev/null +++ b/frontend/src/hooks/product/useCategory.ts @@ -0,0 +1,10 @@ +import { useGet } from '../useGet'; + +import { categoryApi } from '@/apis'; +import type { Category } from '@/types/common'; + +const useCategory = (type: string) => { + return useGet(() => categoryApi.get({ queries: `?type=${type}` })); +}; + +export default useCategory; diff --git a/frontend/src/hooks/useGet.ts b/frontend/src/hooks/useGet.ts new file mode 100644 index 00000000..1905d75e --- /dev/null +++ b/frontend/src/hooks/useGet.ts @@ -0,0 +1,30 @@ +import { useEffect, useState } from 'react'; + +export const useGet = (callback: () => Promise, dependency?: unknown) => { + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(''); + + useEffect(() => { + request(); + if (error) { + throw new Error(error); + } + }, [dependency, error]); + + const request = async () => { + try { + const data = await callback(); + setData(data); + } catch (error) { + if (!(error instanceof Error)) { + return; + } + setError(error.message); + } finally { + setIsLoading(false); + } + }; + + return { data, isLoading, error }; +}; diff --git a/frontend/src/hooks/useMutate.ts b/frontend/src/hooks/useMutate.ts new file mode 100644 index 00000000..bff49822 --- /dev/null +++ b/frontend/src/hooks/useMutate.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react'; + +export const useMutate = (callback: () => Promise) => { + const [isSuccess, setIsSuccess] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(''); + + useEffect(() => { + if (error) { + throw new Error(error); + } + }, [error]); + + const request = async () => { + try { + const res = await callback(); + setIsSuccess(true); + return res; + } catch (error) { + if (!(error instanceof Error)) { + return; + } + setError(error.message); + setIsSuccess(false); + } finally { + setIsLoading(false); + } + }; + + return { request, isLoading, isSuccess, error }; +}; diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index e4819fa7..dc580328 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -6,10 +6,12 @@ import { CategoryMenu, SvgIcon } from '@/components/Common'; import { PBProductList, ProductList, ProductRankingList } from '@/components/Product'; import { ReviewRankingList } from '@/components/Review'; import { PATH } from '@/constants/path'; -import foodCategory from '@/mocks/data/foodCategory.json'; -import storeCategory from '@/mocks/data/storeCategory.json'; +import { useCategory } from '@/hooks/product'; const HomePage = () => { + const { data: foodCategory } = useCategory('food'); + const { data: storeCategory } = useCategory('store'); + return ( <>
      @@ -17,7 +19,7 @@ const HomePage = () => { 공통 상품 - + @@ -30,7 +32,7 @@ const HomePage = () => { 편의점 특산품 - +
      diff --git a/frontend/webpack.common.js b/frontend/webpack.common.js index 43dfa9d6..96373c97 100644 --- a/frontend/webpack.common.js +++ b/frontend/webpack.common.js @@ -1,4 +1,5 @@ const path = require('path'); +const Dotenv = require('dotenv-webpack'); module.exports = { entry: './src/index.tsx', @@ -36,4 +37,5 @@ module.exports = { }, ], }, + plugins: [new Dotenv()], }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index cebdb29a..768dc37c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4811,16 +4811,35 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +dotenv-defaults@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz#6b3ec2e4319aafb70940abda72d3856770ee77ac" + integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg== + dependencies: + dotenv "^8.2.0" + dotenv-expand@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== +dotenv-webpack@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz#6656550460a8076fab20e5ac2eac867e72478645" + integrity sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w== + dependencies: + dotenv-defaults "^2.0.2" + dotenv@^16.0.0: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + duplexify@^3.5.0, duplexify@^3.6.0: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" @@ -7191,10 +7210,10 @@ msw-storybook-addon@^1.8.0: dependencies: is-node-process "^1.0.1" -msw@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.2.tgz#126c3150c07f651e97b24fbd405821f3aeaf9397" - integrity sha512-GsW3PE/Es/a1tYThXcM8YHOZ1S1MtivcS3He/LQbbTCx3rbWJYCtWD5XXyJ53KlNPT7O1VI9sCW3xMtgFe8XpQ== +msw@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.3.tgz#750d6c53ac611732b243c82a87dad960ed832a37" + integrity sha512-Fqy/TaLKR32x4IkMwudJHJysBzVM/v/lSoMPS9f3QaHLOmb3xHN9YurSUnRt+2eEvNXLjVPij1wMBQtLmTbKsg== dependencies: "@mswjs/cookies" "^0.2.2" "@mswjs/interceptors" "^0.17.5" From 78500c3ce7064eac74d6e8ea245f14d725bb637e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 25 Jul 2023 16:10:48 +0900 Subject: [PATCH 051/185] =?UTF-8?q?[BE]=20feat:=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B6=84=EA=B8=B0?= =?UTF-8?q?=EC=B2=98=EB=A6=AC,=20=EC=83=81=ED=92=88=20=ED=8F=89=EA=B7=A0?= =?UTF-8?q?=20=ED=8F=89=EC=A0=90=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20(#94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 리뷰작성시 별점 1점대로만 허용 수정 * feat: 리뷰 등록될 때마다 평균 평점 업데이트 구현 * feat: 이미지 업로드 안할시 분기처리 구현 * refactor: productId 삭제 --- .../com/funeat/product/domain/Product.java | 7 ++++- .../review/application/ReviewService.java | 28 ++++++++++++------- .../java/com/funeat/review/domain/Review.java | 18 +++++++----- .../review/presentation/ReviewController.java | 11 ++------ .../presentation/dto/RankingReviewDto.java | 6 ++-- .../presentation/dto/ReviewCreateRequest.java | 6 ++-- .../presentation/dto/SortingReviewDto.java | 7 ++--- .../product/ProductAcceptanceTest.java | 6 ++-- .../review/ReviewAcceptanceTest.java | 22 +++++++-------- .../ReviewFavoriteRepositoryTest.java | 2 +- .../funeat/product/domain/ProductTest.java | 27 ++++++++++++++++++ .../review/application/ReviewServiceTest.java | 20 ++++++------- .../persistence/ReviewRepositoryTest.java | 24 ++++++++-------- .../persistence/ReviewTagRepositoryTest.java | 4 +-- 14 files changed, 113 insertions(+), 75 deletions(-) create mode 100644 backend/src/test/java/com/funeat/product/domain/ProductTest.java diff --git a/backend/src/main/java/com/funeat/product/domain/Product.java b/backend/src/main/java/com/funeat/product/domain/Product.java index 16d19ef3..ed0be7d3 100644 --- a/backend/src/main/java/com/funeat/product/domain/Product.java +++ b/backend/src/main/java/com/funeat/product/domain/Product.java @@ -25,7 +25,7 @@ public class Product { private String content; - private Double averageRating; + private Double averageRating = 0.0; @ManyToOne @JoinColumn(name = "category_id") @@ -49,6 +49,11 @@ public Product(final String name, final Long price, final String image, final St this.category = category; } + public void updateAverageRating(final Long rating, final Long count) { + double calculatedRating = ((count - 1) * averageRating + rating) / count; + this.averageRating = Math.round(calculatedRating * 10.0) / 10.0; + } + public Long getId() { return id; } diff --git a/backend/src/main/java/com/funeat/review/application/ReviewService.java b/backend/src/main/java/com/funeat/review/application/ReviewService.java index f6a58acb..7399ad09 100644 --- a/backend/src/main/java/com/funeat/review/application/ReviewService.java +++ b/backend/src/main/java/com/funeat/review/application/ReviewService.java @@ -19,15 +19,15 @@ import com.funeat.review.presentation.dto.SortingReviewsResponse; import com.funeat.tag.domain.Tag; import com.funeat.tag.persistence.TagRepository; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import java.util.List; -import java.util.stream.Collectors; - @Service @Transactional(readOnly = true) public class ReviewService { @@ -61,9 +61,17 @@ public void create(final Long productId, final MultipartFile image, final Review final Product findProduct = productRepository.findById(productId) .orElseThrow(IllegalArgumentException::new); - final Review savedReview = reviewRepository.save( - new Review(findMember, findProduct, image.getOriginalFilename(), reviewRequest.getRating(), - reviewRequest.getContent(), reviewRequest.getReBuy())); + final Review savedReview; + if (Objects.isNull(image)) { + savedReview = reviewRepository.save( + new Review(findMember, findProduct, reviewRequest.getRating(), reviewRequest.getContent(), + reviewRequest.getReBuy())); + } else { + savedReview = reviewRepository.save( + new Review(findMember, findProduct, image.getOriginalFilename(), reviewRequest.getRating(), + reviewRequest.getContent(), reviewRequest.getReBuy())); + imageService.upload(image); + } final List findTags = tagRepository.findTagsByIdIn(reviewRequest.getTagIds()); @@ -71,16 +79,16 @@ public void create(final Long productId, final MultipartFile image, final Review .map(findTag -> ReviewTag.createReviewTag(savedReview, findTag)) .collect(Collectors.toList()); + final Long countByProduct = reviewRepository.countByProduct(findProduct); + + findProduct.updateAverageRating(savedReview.getRating(), countByProduct); reviewTagRepository.saveAll(reviewTags); - imageService.upload(image); } @Transactional - public void likeReview(final Long productId, final Long reviewId, final ReviewFavoriteRequest request) { + public void likeReview(final Long reviewId, final ReviewFavoriteRequest request) { final Member findMember = memberRepository.findById(request.getMemberId()) .orElseThrow(IllegalArgumentException::new); - final Product findProduct = productRepository.findById(productId) - .orElseThrow(IllegalArgumentException::new); final Review findReview = reviewRepository.findById(reviewId) .orElseThrow(IllegalArgumentException::new); diff --git a/backend/src/main/java/com/funeat/review/domain/Review.java b/backend/src/main/java/com/funeat/review/domain/Review.java index a3b5ecc0..6dd41c40 100644 --- a/backend/src/main/java/com/funeat/review/domain/Review.java +++ b/backend/src/main/java/com/funeat/review/domain/Review.java @@ -3,7 +3,8 @@ import com.funeat.member.domain.Member; import com.funeat.member.domain.favorite.ReviewFavorite; import com.funeat.product.domain.Product; - +import java.util.ArrayList; +import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; @@ -11,8 +12,6 @@ import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; -import java.util.ArrayList; -import java.util.List; @Entity public class Review { @@ -23,7 +22,7 @@ public class Review { private String image; - private Double rating; + private Long rating; private String content; @@ -48,7 +47,12 @@ public class Review { protected Review() { } - public Review(final Member member, final Product findProduct, final String image, final Double rating, + public Review(final Member member, final Product findProduct, final Long rating, final String content, + final Boolean reBuy) { + this(member, findProduct, null, rating, content, reBuy); + } + + public Review(final Member member, final Product findProduct, final String image, final Long rating, final String content, final Boolean reBuy) { this.member = member; this.product = findProduct; @@ -58,7 +62,7 @@ public Review(final Member member, final Product findProduct, final String image this.reBuy = reBuy; } - public Review(final Member member, final Product findProduct, final String image, final Double rating, + public Review(final Member member, final Product findProduct, final String image, final Long rating, final String content, final Boolean reBuy, final Long favoriteCount) { this.member = member; this.product = findProduct; @@ -85,7 +89,7 @@ public String getImage() { return image; } - public Double getRating() { + public Long getRating() { return rating; } diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index 8f55950b..8487fe64 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -1,13 +1,11 @@ package com.funeat.review.presentation; import com.funeat.review.application.ReviewService; - -import com.funeat.review.domain.Review; -import com.funeat.review.presentation.dto.RankingReviewDto; import com.funeat.review.presentation.dto.RankingReviewsResponse; import com.funeat.review.presentation.dto.ReviewCreateRequest; import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewsResponse; +import java.net.URI; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.MediaType; @@ -20,9 +18,6 @@ import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import java.net.URI; -import java.util.List; -import java.util.stream.Collectors; @RestController public class ReviewController { @@ -43,9 +38,9 @@ public ResponseEntity writeReview(@PathVariable Long productId, @RequestPa } @PatchMapping("/api/products/{productId}/reviews/{reviewId}") - public ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, + public ResponseEntity toggleLikeReview(@PathVariable Long reviewId, @RequestBody ReviewFavoriteRequest request) { - reviewService.likeReview(productId, reviewId, request); + reviewService.likeReview(reviewId, request); return ResponseEntity.noContent().build(); diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java index d3ca4620..fcbaaed5 100644 --- a/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java +++ b/backend/src/main/java/com/funeat/review/presentation/dto/RankingReviewDto.java @@ -8,14 +8,14 @@ public class RankingReviewDto { private final Long productId; private final String productName; private final String content; - private final Double rating; + private final Long rating; private final Long favoriteCount; public RankingReviewDto(final Long reviewId, final Long productId, final String productName, final String content, - final Double rating, + final Long rating, final Long favoriteCount) { this.reviewId = reviewId; this.productId = productId; @@ -52,7 +52,7 @@ public String getContent() { return content; } - public Double getRating() { + public Long getRating() { return rating; } diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java index 93a55377..7fc4ca18 100644 --- a/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java +++ b/backend/src/main/java/com/funeat/review/presentation/dto/ReviewCreateRequest.java @@ -4,13 +4,13 @@ public class ReviewCreateRequest { - private final Double rating; + private final Long rating; private final List tagIds; private final String content; private final Boolean reBuy; private final Long memberId; - public ReviewCreateRequest(final Double rating, final List tagIds, final String content, final Boolean reBuy, + public ReviewCreateRequest(final Long rating, final List tagIds, final String content, final Boolean reBuy, final Long memberId) { this.rating = rating; this.tagIds = tagIds; @@ -19,7 +19,7 @@ public ReviewCreateRequest(final Double rating, final List tagIds, final S this.memberId = memberId; } - public Double getRating() { + public Long getRating() { return rating; } diff --git a/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java index 1d825ad2..5f493ee7 100644 --- a/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java +++ b/backend/src/main/java/com/funeat/review/presentation/dto/SortingReviewDto.java @@ -4,7 +4,6 @@ import com.funeat.review.domain.Review; import com.funeat.review.domain.ReviewTag; import com.funeat.tag.domain.Tag; - import java.util.List; import java.util.stream.Collectors; @@ -14,7 +13,7 @@ public class SortingReviewDto { private final String userName; private final String profileImage; private final String image; - private final Double rating; + private final Long rating; private final List tags; private final String content; private final boolean rebuy; @@ -25,7 +24,7 @@ public SortingReviewDto(final Long id, final String userName, final String profileImage, final String image, - final Double rating, + final Long rating, final List tags, final String content, final boolean rebuy, @@ -91,7 +90,7 @@ public String getImage() { return image; } - public Double getRating() { + public Long getRating() { return rating; } diff --git a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java index 812f8c61..8f09fdf1 100644 --- a/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/product/ProductAcceptanceTest.java @@ -109,11 +109,11 @@ class ProductAcceptanceTest extends AcceptanceTest { final Tag tag3 = 태그_추가_요청(new Tag("3번")); final MultiPartSpecification image = 리뷰_사진_명세_요청(); - final ReviewCreateRequest request1 = new ReviewCreateRequest(4.5, + final ReviewCreateRequest request1 = new ReviewCreateRequest(4L, List.of(tag1.getId(), tag2.getId(), tag3.getId()), "request1", true, memberId); - final ReviewCreateRequest request2 = new ReviewCreateRequest(4.0, List.of(tag2.getId(), tag3.getId()), + final ReviewCreateRequest request2 = new ReviewCreateRequest(4L, List.of(tag2.getId(), tag3.getId()), "request2", true, memberId); - final ReviewCreateRequest request3 = new ReviewCreateRequest(3.0, List.of(tag2.getId()), "request3", true, + final ReviewCreateRequest request3 = new ReviewCreateRequest(3L, List.of(tag2.getId()), "request3", true, memberId); 리뷰_추가_요청(productId, image, request1); 리뷰_추가_요청(productId, image, request2); diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index 8e9d3ade..7d1d8c57 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -42,7 +42,7 @@ class ReviewAcceptanceTest extends AcceptanceTest { final List savedTagIds = 태그_추가_요청(); final MultiPartSpecification image = 리뷰_사진_명세_요청(); - final var request = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + final var request = new ReviewCreateRequest(4L, savedTagIds, "test content", true, savedMemberId); // when final var response = 리뷰_추가_요청(savedProductId, image, request); @@ -58,7 +58,7 @@ class ReviewAcceptanceTest extends AcceptanceTest { final Long savedProductId = 상품_추가_요청(); final List savedTagIds = 태그_추가_요청(); final MultiPartSpecification image = 리뷰_사진_명세_요청(); - final var reviewRequest = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + final var reviewRequest = new ReviewCreateRequest(4L, savedTagIds, "test content", true, savedMemberId); final var favoriteRequest = new ReviewFavoriteRequest(true, savedMemberId); 리뷰_추가_요청(savedProductId, image, reviewRequest); @@ -81,7 +81,7 @@ class ReviewAcceptanceTest extends AcceptanceTest { final Long savedProductId = 상품_추가_요청(); final List savedTagIds = 태그_추가_요청(); final MultiPartSpecification image = 리뷰_사진_명세_요청(); - final var reviewRequest = new ReviewCreateRequest(4.5, savedTagIds, "test content", true, savedMemberId); + final var reviewRequest = new ReviewCreateRequest(4L, savedTagIds, "test content", true, savedMemberId); final var favoriteRequest = new ReviewFavoriteRequest(true, savedMemberId); final var favoriteCancelRequest = new ReviewFavoriteRequest(false, savedMemberId); @@ -139,9 +139,9 @@ class ReviewAcceptanceTest extends AcceptanceTest { final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); final var productId = 상품_추가_요청(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); @@ -237,11 +237,11 @@ class ReviewAcceptanceTest extends AcceptanceTest { final var products = List.of(product1, product2); 복수_상품_추가_요청(products); - final var review1 = new Review(member1, product1, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); - final var review2 = new Review(member2, product1, "review2.jpg", 4.5, "역삼역", true, 351L); - final var review3 = new Review(member3, product1, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); - final var review4 = new Review(member2, product2, "review4.jpg", 5.0, "ㅁㅜㄹ", true, 247L); - final var review5 = new Review(member3, product2, "review5.jpg", 1.5, "ㄴㄴ", false, 83L); + final var review1 = new Review(member1, product1, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product1, "review2.jpg", 4L, "역삼역", true, 351L); + final var review3 = new Review(member3, product1, "review3.jpg", 3L, "ㅇㅇ", false, 130L); + final var review4 = new Review(member2, product2, "review4.jpg", 5L, "ㅁㅜㄹ", true, 247L); + final var review5 = new Review(member3, product2, "review5.jpg", 1L, "ㄴㄴ", false, 83L); final var reviews = List.of(review1, review2, review3, review4, review5); 복수_리뷰_추가(reviews); diff --git a/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java b/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java index bd048e43..e4d3b341 100644 --- a/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java +++ b/backend/src/test/java/com/funeat/member/persistence/ReviewFavoriteRepositoryTest.java @@ -66,7 +66,7 @@ class ReviewFavoriteRepositoryTest { private Review 리뷰_추가_요청(final Member member, final Product product) { final var image = 리뷰_사진_명세_요청(); - return reviewRepository.save(new Review(member, product, image.getFileName(), 4.5, "content", true)); + return reviewRepository.save(new Review(member, product, image.getFileName(), 4L, "content", true)); } private MultiPartSpecification 리뷰_사진_명세_요청() { diff --git a/backend/src/test/java/com/funeat/product/domain/ProductTest.java b/backend/src/test/java/com/funeat/product/domain/ProductTest.java new file mode 100644 index 00000000..1d167c12 --- /dev/null +++ b/backend/src/test/java/com/funeat/product/domain/ProductTest.java @@ -0,0 +1,27 @@ +package com.funeat.product.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayNameGeneration; +import org.junit.jupiter.api.DisplayNameGenerator.ReplaceUnderscores; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("NonAsciiCharacters") +@DisplayNameGeneration(ReplaceUnderscores.class) +class ProductTest { + + @Test + void 평균_평점_업데이트를_할_수_있다() { + final var product = new Product("testName", 1000L, "testImage", "testContent", null); + final var reviewRating1 = 4L; + final var reviewRating2 = 2L; + final var reviewCount1 = 1L; + final var reviewCount2 = 2L; + + product.updateAverageRating(reviewRating1, reviewCount1); + assertThat(product.getAverageRating()).isEqualTo(4.0); + + product.updateAverageRating(reviewRating2, reviewCount2); + assertThat(product.getAverageRating()).isEqualTo(3.0); + } +} diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java index 5e2bab2a..b592985a 100644 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -79,7 +79,7 @@ void init() { .map(Tag::getId) .collect(Collectors.toList()); final var image = 리뷰_페이크_사진_요청(); - final var request = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + final var request = new ReviewCreateRequest(4L, tagIds, "review", true, member.getId()); // when reviewService.create(product.getId(), image, request); @@ -90,7 +90,7 @@ void init() { .ignoringExpectedNullFields() .comparingOnlyFields("member", "product", "image", "rating", "content", "reBuy") .isEqualTo( - new Review(member, product, image.getOriginalFilename(), 4.5, "review", true) + new Review(member, product, image.getOriginalFilename(), 4L, "review", true) ); } @@ -104,14 +104,14 @@ void init() { .map(Tag::getId) .collect(Collectors.toList()); final var image = 리뷰_페이크_사진_요청(); - final var reviewCreaterequest = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + final var reviewCreaterequest = new ReviewCreateRequest(4L, tagIds, "review", true, member.getId()); reviewService.create(product.getId(), image, reviewCreaterequest); final var savedReview = reviewRepository.findAll().get(0); // when final var favoriteRequest = new ReviewFavoriteRequest(true, member.getId()); - reviewService.likeReview(product.getId(), savedReview.getId(), favoriteRequest); + reviewService.likeReview(savedReview.getId(), favoriteRequest); final var reviewFavoriteResult = reviewFavoriteRepository.findAll().get(0); final var reviewResult = reviewRepository.findAll().get(0); @@ -134,17 +134,17 @@ void init() { .map(Tag::getId) .collect(Collectors.toList()); final var image = 리뷰_페이크_사진_요청(); - final var reviewCreaterequest = new ReviewCreateRequest(4.5, tagIds, "review", true, member.getId()); + final var reviewCreaterequest = new ReviewCreateRequest(4L, tagIds, "review", true, member.getId()); reviewService.create(product.getId(), image, reviewCreaterequest); final var savedReview = reviewRepository.findAll().get(0); final var favoriteRequest = new ReviewFavoriteRequest(true, member.getId()); - reviewService.likeReview(product.getId(), savedReview.getId(), favoriteRequest); + reviewService.likeReview(savedReview.getId(), favoriteRequest); // when final var cancelFavoriteRequest = new ReviewFavoriteRequest(false, member.getId()); - reviewService.likeReview(product.getId(), savedReview.getId(), cancelFavoriteRequest); + reviewService.likeReview(savedReview.getId(), cancelFavoriteRequest); final var reviewFavoriteResult = reviewFavoriteRepository.findAll().get(0); final var reviewResult = reviewRepository.findAll().get(0); @@ -193,9 +193,9 @@ class sortingReviews_페이징_테스트 { final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); 상품_추가(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java index 7b86bb3c..64eff015 100644 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewRepositoryTest.java @@ -51,10 +51,10 @@ class ReviewRepositoryTest { Product product1 = productRepository.save(new Product("삼각김밥", 1000L, "image.png", "맛있는 삼각김밥", category)); Product product2 = productRepository.save(new Product("라면", 2000L, "image.png", "맛있는 라면", category)); - reviewRepository.save(new Review(member, product1, "review.png", 4.5, "이 삼각김밥은 최고!!", true)); - reviewRepository.save(new Review(member, product1, "review.png", 3.0, "이 삼각김밥은 별로", false)); - reviewRepository.save(new Review(member, product1, "review.png", 4.0, "이 삼각김밥은 쏘쏘", true)); - reviewRepository.save(new Review(member, product2, "review.png", 3.0, "이 라면은 맛있다", true)); + reviewRepository.save(new Review(member, product1, "review.png", 4L, "이 삼각김밥은 최고!!", true)); + reviewRepository.save(new Review(member, product1, "review.png", 3L, "이 삼각김밥은 별로", false)); + reviewRepository.save(new Review(member, product1, "review.png", 4L, "이 삼각김밥은 쏘쏘", true)); + reviewRepository.save(new Review(member, product2, "review.png", 3L, "이 라면은 맛있다", true)); // when assertThat(reviewRepository.countByProduct(product1)).isEqualTo(3); @@ -73,9 +73,9 @@ class ReviewRepositoryTest { final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); productRepository.save(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); reviewRepository.saveAll(reviews); @@ -106,11 +106,11 @@ class ReviewRepositoryTest { final var products = List.of(product1, product2); productRepository.saveAll(products); - final var review1 = new Review(member1, product1, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); - final var review2 = new Review(member2, product1, "review2.jpg", 4.5, "역삼역", true, 351L); - final var review3 = new Review(member3, product1, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); - final var review4 = new Review(member2, product2, "review4.jpg", 5.0, "ㅁㅜㄹ", true, 247L); - final var review5 = new Review(member3, product2, "review5.jpg", 1.5, "ㄴㄴ", false, 83L); + final var review1 = new Review(member1, product1, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product1, "review2.jpg", 4L, "역삼역", true, 351L); + final var review3 = new Review(member3, product1, "review3.jpg", 3L, "ㅇㅇ", false, 130L); + final var review4 = new Review(member2, product2, "review4.jpg", 5L, "ㅁㅜㄹ", true, 247L); + final var review5 = new Review(member3, product2, "review5.jpg", 1L, "ㄴㄴ", false, 83L); final var reviews = List.of(review1, review2, review3, review4, review5); reviewRepository.saveAll(reviews); diff --git a/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java b/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java index f8b70102..c1a19a22 100644 --- a/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java +++ b/backend/src/test/java/com/funeat/review/persistence/ReviewTagRepositoryTest.java @@ -60,8 +60,8 @@ class ReviewTagRepositoryTest { final var tag4 = new Tag("4번"); tagRepository.saveAll(List.of(tag1, tag2, tag3, tag4)); - final var review1 = new Review(member, product, "review1.png", 5.0, "최고의 망고", true, 25L); - final var review2 = new Review(member, product, "review2.png", 3.0, "그럭저럭 망고", false, 10L); + final var review1 = new Review(member, product, "review1.png", 5L, "최고의 망고", true, 25L); + final var review2 = new Review(member, product, "review2.png", 3L, "그럭저럭 망고", false, 10L); reviewRepository.saveAll(List.of(review1, review2)); final var reviewTag1 = ReviewTag.createReviewTag(review1, tag1); From d8b92635b45b5bcd36c3257c1510d3834638810b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 25 Jul 2023 16:12:24 +0900 Subject: [PATCH 052/185] =?UTF-8?q?[ALL]=20api=20=EB=AC=B8=EC=84=9C=20swag?= =?UTF-8?q?ger=20=EC=A0=81=EC=9A=A9=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: swagger 의존성 추가 * chore: swagger 설정 추가 * refactor: test 제외한 profile 일때 imageUploader 빈 등록 * feat: swagger 적용 * refactor: matching-strategy 삭제 * chore: 의존성 testRuntimeOnly 로 수정 * refactor: interface, class 네이밍 수정 --- backend/build.gradle | 2 + .../java/com/funeat/common/OpenApiConfig.java | 22 +++++ .../presentation/CategoryApiController.java | 31 +++++++ .../presentation/CategoryController.java | 30 +++---- .../presentation/ProductApiController.java | 38 +++++++++ .../presentation/ProductController.java | 46 +++++----- .../review/application/ImageUploader.java | 2 +- .../presentation/ReviewApiController.java | 63 ++++++++++++++ .../review/presentation/ReviewController.java | 85 +++++++++---------- backend/src/main/resources/application.yml | 6 ++ 10 files changed, 235 insertions(+), 90 deletions(-) create mode 100644 backend/src/main/java/com/funeat/common/OpenApiConfig.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java create mode 100644 backend/src/main/java/com/funeat/product/presentation/ProductApiController.java create mode 100644 backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java diff --git a/backend/build.gradle b/backend/build.gradle index 8b6233ca..b5ef211e 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -22,6 +22,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:4.4.0' testRuntimeOnly 'com.h2database:h2' + + implementation 'org.springdoc:springdoc-openapi-ui:1.7.0' } tasks.named('test') { diff --git a/backend/src/main/java/com/funeat/common/OpenApiConfig.java b/backend/src/main/java/com/funeat/common/OpenApiConfig.java new file mode 100644 index 00000000..ef3b45b2 --- /dev/null +++ b/backend/src/main/java/com/funeat/common/OpenApiConfig.java @@ -0,0 +1,22 @@ +package com.funeat.common; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.context.annotation.Configuration; + +@OpenAPIDefinition( + info = @Info( + title = "펀잇 API 명세서", + description = "펀잇팀 API 명세서입니다.", + version = "v1" + ), + tags = { + @Tag(name = "01.Product", description = "상품 기능"), + @Tag(name = "02.Category", description = "카테고리 기능"), + @Tag(name = "03.Review", description = "리뷰 기능"), + } +) +@Configuration +public class OpenApiConfig { +} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java new file mode 100644 index 00000000..f084fc17 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryApiController.java @@ -0,0 +1,31 @@ +package com.funeat.product.presentation; + +import com.funeat.product.application.CategoryService; +import com.funeat.product.domain.CategoryType; +import com.funeat.product.dto.CategoryResponse; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/categories") +public class CategoryApiController implements CategoryController { + + private final CategoryService categoryService; + + public CategoryApiController(final CategoryService categoryService) { + this.categoryService = categoryService; + } + + @GetMapping + public ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type) { + final List responses = categoryService.findAllCategoriesByType(type).stream() + .map(CategoryResponse::toResponse) + .collect(Collectors.toList()); + return ResponseEntity.ok(responses); + } +} diff --git a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java index a594edb6..53625ffb 100644 --- a/backend/src/main/java/com/funeat/product/presentation/CategoryController.java +++ b/backend/src/main/java/com/funeat/product/presentation/CategoryController.java @@ -1,31 +1,23 @@ package com.funeat.product.presentation; -import com.funeat.product.application.CategoryService; import com.funeat.product.domain.CategoryType; import com.funeat.product.dto.CategoryResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; -import java.util.stream.Collectors; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -@RestController -@RequestMapping("/api/categories") -public class CategoryController { - - private final CategoryService categoryService; - - public CategoryController(final CategoryService categoryService) { - this.categoryService = categoryService; - } +@Tag(name = "02.Category", description = "카테고리 기능") +public interface CategoryController { + @Operation(summary = "해당 type 카테고리 전체 조회", description = "FOOD 또는 STORE 를 받아 해당 카테고리를 전체 조회한다.") + @ApiResponse( + responseCode = "200", + description = "해당 type 카테고리 전체 조회 성공." + ) @GetMapping - public ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type) { - final List responses = categoryService.findAllCategoriesByType(type).stream() - .map(CategoryResponse::toResponse) - .collect(Collectors.toList()); - return ResponseEntity.ok(responses); - } + ResponseEntity> getAllCategoriesByType(@RequestParam final CategoryType type); } diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java b/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java new file mode 100644 index 00000000..83de4928 --- /dev/null +++ b/backend/src/main/java/com/funeat/product/presentation/ProductApiController.java @@ -0,0 +1,38 @@ +package com.funeat.product.presentation; + +import com.funeat.product.application.ProductService; +import com.funeat.product.dto.ProductResponse; +import com.funeat.product.dto.ProductsInCategoryResponse; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +public class ProductApiController implements ProductController { + + private final ProductService productService; + + public ProductApiController(final ProductService productService) { + this.productService = productService; + } + + @GetMapping("/categories/{category_id}/products") + public ResponseEntity getAllProductsInCategory( + @PathVariable(name = "category_id") final Long categoryId, + @PageableDefault Pageable pageable + ) { + final ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); + return ResponseEntity.ok(response); + } + + @GetMapping("/products/{productId}") + public ResponseEntity getProductDetail(@PathVariable final Long productId) { + final ProductResponse response = productService.findProductDetail(productId); + return ResponseEntity.ok(response); + } +} diff --git a/backend/src/main/java/com/funeat/product/presentation/ProductController.java b/backend/src/main/java/com/funeat/product/presentation/ProductController.java index 7e231bd8..7171d0dd 100644 --- a/backend/src/main/java/com/funeat/product/presentation/ProductController.java +++ b/backend/src/main/java/com/funeat/product/presentation/ProductController.java @@ -1,38 +1,34 @@ package com.funeat.product.presentation; -import com.funeat.product.application.ProductService; import com.funeat.product.dto.ProductResponse; import com.funeat.product.dto.ProductsInCategoryResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -@RestController -@RequestMapping("/api") -public class ProductController { +@Tag(name = "01.Product", description = "상품 기능") +public interface ProductController { - private final ProductService productService; + @Operation(summary = "해당 카테고리 상품 조회", description = "해당 카테고리의 상품을 조회한다.") + @ApiResponse( + responseCode = "200", + description = "해당 카테고리 상품 조회 성공." + ) + @GetMapping + ResponseEntity getAllProductsInCategory( + @PathVariable(name = "category_id") final Long categoryId, @PageableDefault Pageable pageable + ); - public ProductController(final ProductService productService) { - this.productService = productService; - } - - @GetMapping("/categories/{category_id}/products") - public ResponseEntity getAllProductsInCategory( - @PathVariable(name = "category_id") final Long categoryId, - @PageableDefault Pageable pageable - ) { - final ProductsInCategoryResponse response = productService.getAllProductsInCategory(categoryId, pageable); - return ResponseEntity.ok(response); - } - - @GetMapping("/products/{productId}") - public ResponseEntity getProductDetail(@PathVariable final Long productId) { - final ProductResponse response = productService.findProductDetail(productId); - return ResponseEntity.ok(response); - } + @Operation(summary = "해당 상품 상세 조회", description = "해당 상품 상세정보를 조회한다.") + @ApiResponse( + responseCode = "200", + description = "해당 상품 상세 조회 성공." + ) + @GetMapping + ResponseEntity getProductDetail(@PathVariable final Long productId); } diff --git a/backend/src/main/java/com/funeat/review/application/ImageUploader.java b/backend/src/main/java/com/funeat/review/application/ImageUploader.java index 6d5103e2..f7958e3b 100644 --- a/backend/src/main/java/com/funeat/review/application/ImageUploader.java +++ b/backend/src/main/java/com/funeat/review/application/ImageUploader.java @@ -10,7 +10,7 @@ import org.springframework.web.multipart.MultipartFile; @Component -@Profile("dev") +@Profile("!test") public class ImageUploader implements ImageService { @Value("${review.image.path}") diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java new file mode 100644 index 00000000..10fa4cd4 --- /dev/null +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java @@ -0,0 +1,63 @@ +package com.funeat.review.presentation; + +import com.funeat.review.application.ReviewService; +import com.funeat.review.presentation.dto.RankingReviewsResponse; +import com.funeat.review.presentation.dto.ReviewCreateRequest; +import com.funeat.review.presentation.dto.ReviewFavoriteRequest; +import com.funeat.review.presentation.dto.SortingReviewsResponse; +import java.net.URI; +import org.springframework.data.domain.Pageable; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@RestController +public class ReviewApiController implements ReviewController { + + private final ReviewService reviewService; + + public ReviewApiController(final ReviewService reviewService) { + this.reviewService = reviewService; + } + + @PostMapping(value = "/api/products/{productId}/reviews", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, + MediaType.APPLICATION_JSON_VALUE}) + public ResponseEntity writeReview(@PathVariable Long productId, @RequestPart MultipartFile image, + @RequestPart ReviewCreateRequest reviewRequest) { + reviewService.create(productId, image, reviewRequest); + + return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); + } + + @PatchMapping("/api/products/{productId}/reviews/{reviewId}") + public ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, + @RequestBody ReviewFavoriteRequest request) { + reviewService.likeReview(productId, reviewId, request); + + return ResponseEntity.noContent().build(); + + } + + @GetMapping(value = "/api/products/{productId}/reviews") + public ResponseEntity getSortingReviews(@PathVariable Long productId, + @PageableDefault Pageable pageable) { + final SortingReviewsResponse response = reviewService.sortingReviews(productId, pageable); + + return ResponseEntity.ok(response); + } + + @GetMapping("/api/ranks/reviews") + public ResponseEntity getRankingReviews() { + final RankingReviewsResponse response = reviewService.getTopReviews(); + + return ResponseEntity.ok(response); + } +} diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index 8487fe64..b89a9b54 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -1,14 +1,14 @@ package com.funeat.review.presentation; -import com.funeat.review.application.ReviewService; import com.funeat.review.presentation.dto.RankingReviewsResponse; import com.funeat.review.presentation.dto.ReviewCreateRequest; import com.funeat.review.presentation.dto.ReviewFavoriteRequest; import com.funeat.review.presentation.dto.SortingReviewsResponse; -import java.net.URI; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -16,48 +16,43 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -@RestController -public class ReviewController { - - private final ReviewService reviewService; - - public ReviewController(final ReviewService reviewService) { - this.reviewService = reviewService; - } - - @PostMapping(value = "/api/products/{productId}/reviews", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, - MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity writeReview(@PathVariable Long productId, @RequestPart MultipartFile image, - @RequestPart ReviewCreateRequest reviewRequest) { - reviewService.create(productId, image, reviewRequest); - - return ResponseEntity.created(URI.create("/api/products/" + productId)).build(); - } - - @PatchMapping("/api/products/{productId}/reviews/{reviewId}") - public ResponseEntity toggleLikeReview(@PathVariable Long reviewId, - @RequestBody ReviewFavoriteRequest request) { - reviewService.likeReview(reviewId, request); - - return ResponseEntity.noContent().build(); - - } - - @GetMapping(value = "/api/products/{productId}/reviews") - public ResponseEntity getSortingReviews(@PathVariable Long productId, - @PageableDefault Pageable pageable) { - final SortingReviewsResponse response = reviewService.sortingReviews(productId, pageable); - - return ResponseEntity.ok(response); - } - - @GetMapping("/api/ranks/reviews") - public ResponseEntity getRankingReviews() { - final RankingReviewsResponse response = reviewService.getTopReviews(); - - return ResponseEntity.ok(response); - } +@Tag(name = "03.Review", description = "리뷰관련 API 입니다.") +public interface ReviewController { + + @Operation(summary = "리뷰 추가", description = "리뷰를 작성한다.") + @ApiResponse( + responseCode = "201", + description = "리뷰 작성 성공." + ) + @PostMapping + ResponseEntity writeReview(@PathVariable Long productId, @RequestPart MultipartFile image, + @RequestPart ReviewCreateRequest reviewRequest); + + @Operation(summary = "리뷰 좋아요", description = "리뷰에 좋아요 또는 취소를 한다.") + @ApiResponse( + responseCode = "204", + description = "리뷰 좋아요(취소) 성공." + ) + @PatchMapping + ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, + @RequestBody ReviewFavoriteRequest request); + + @Operation(summary = "리뷰를 정렬후 조회", description = "리뷰를 정렬후 조회한다.") + @ApiResponse( + responseCode = "200", + description = "리뷰 정렬후 조회 성공." + ) + @GetMapping + ResponseEntity getSortingReviews(@PathVariable Long productId, + @PageableDefault Pageable pageable); + + @Operation(summary = "리뷰 랭킹 Top3 조회", description = "리뷰 랭킹 Top3 조회한다.") + @ApiResponse( + responseCode = "200", + description = "리뷰 랭킹 Top3 조회 성공." + ) + @GetMapping + ResponseEntity getRankingReviews(); } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 97e5c0cf..27bdd344 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -1,3 +1,9 @@ spring: profiles: active: { DEPLOY_ACTIVE:dev } + +springdoc: + swagger-ui: + path: /funeat-api + enabled: true + tags-sorter: alpha From ac668ef8f1364c392bccbd863621d3bb9118bd36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 25 Jul 2023 16:19:51 +0900 Subject: [PATCH 053/185] =?UTF-8?q?[BE]=20fix:=20productId=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=9C=EA=B1=B0=20=EC=B6=A9=EB=8F=8C=EB=A1=9C=20bui?= =?UTF-8?q?ld=20=EC=8B=A4=ED=8C=A8=20=EC=88=98=EC=A0=95=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/funeat/review/presentation/ReviewApiController.java | 4 ++-- .../java/com/funeat/review/presentation/ReviewController.java | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java index 10fa4cd4..971b4eb4 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewApiController.java @@ -38,9 +38,9 @@ public ResponseEntity writeReview(@PathVariable Long productId, @RequestPa } @PatchMapping("/api/products/{productId}/reviews/{reviewId}") - public ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, + public ResponseEntity toggleLikeReview(@PathVariable Long reviewId, @RequestBody ReviewFavoriteRequest request) { - reviewService.likeReview(productId, reviewId, request); + reviewService.likeReview(reviewId, request); return ResponseEntity.noContent().build(); diff --git a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java index b89a9b54..f419d714 100644 --- a/backend/src/main/java/com/funeat/review/presentation/ReviewController.java +++ b/backend/src/main/java/com/funeat/review/presentation/ReviewController.java @@ -36,8 +36,7 @@ ResponseEntity writeReview(@PathVariable Long productId, @RequestPart Mult description = "리뷰 좋아요(취소) 성공." ) @PatchMapping - ResponseEntity toggleLikeReview(@PathVariable Long productId, @PathVariable Long reviewId, - @RequestBody ReviewFavoriteRequest request); + ResponseEntity toggleLikeReview(@PathVariable Long reviewId, @RequestBody ReviewFavoriteRequest request); @Operation(summary = "리뷰를 정렬후 조회", description = "리뷰를 정렬후 조회한다.") @ApiResponse( From ee14e1aba73ab0b9dd0ed16e422f0a117e4166e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9A=B0=EA=B0=80?= Date: Tue, 25 Jul 2023 16:31:52 +0900 Subject: [PATCH 054/185] =?UTF-8?q?[BE]=20fix:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=ED=8F=89=EC=A0=90=20=EC=9A=94=EC=B2=AD=201=EC=A0=90=EB=8C=80?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=ED=95=9C=20=EB=B6=80=EB=B6=84=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95=20(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 리뷰작성시 평점 1점대로 받는 부분 Long 으로 수정 --- .../acceptance/review/ReviewAcceptanceTest.java | 12 ++++++------ .../funeat/review/application/ReviewServiceTest.java | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java index 7d1d8c57..8d6c5808 100644 --- a/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java +++ b/backend/src/test/java/com/funeat/acceptance/review/ReviewAcceptanceTest.java @@ -171,9 +171,9 @@ class ReviewAcceptanceTest extends AcceptanceTest { final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); final var productId = 상품_추가_요청(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); @@ -203,9 +203,9 @@ class ReviewAcceptanceTest extends AcceptanceTest { final var product = new Product("삼각김밥1", 1000L, "image.png", "김밥", category); final var productId = 상품_추가_요청(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 5L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 351L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 2L, "이 김밥은 재밌습니다", true, 5L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 351L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); diff --git a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java index b592985a..7037eed3 100644 --- a/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java +++ b/backend/src/test/java/com/funeat/review/application/ReviewServiceTest.java @@ -224,9 +224,9 @@ class sortingReviews_페이징_테스트 { final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); 상품_추가(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 2L, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); @@ -255,9 +255,9 @@ class sortingReviews_페이징_테스트 { final var product = new Product("김밥", 1000L, "kimbap.png", "우영우가 먹은 그 김밥", null); 상품_추가(product); - final var review1 = new Review(member1, product, "review1.jpg", 3.0, "이 김밥은 재밌습니다", true, 351L); - final var review2 = new Review(member2, product, "review2.jpg", 4.5, "역삼역", true, 24L); - final var review3 = new Review(member3, product, "review3.jpg", 3.5, "ㅇㅇ", false, 130L); + final var review1 = new Review(member1, product, "review1.jpg", 3L, "이 김밥은 재밌습니다", true, 351L); + final var review2 = new Review(member2, product, "review2.jpg", 4L, "역삼역", true, 24L); + final var review3 = new Review(member3, product, "review3.jpg", 3L, "ㅇㅇ", false, 130L); final var reviews = List.of(review1, review2, review3); 복수_리뷰_추가(reviews); From 3e877a66a84b138badc1b04d9e44ee7ef61b9aac Mon Sep 17 00:00:00 2001 From: Leejin Yang Date: Tue, 25 Jul 2023 16:37:56 +0900 Subject: [PATCH 055/185] =?UTF-8?q?[FE]=20feat:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 펀잇 디자인 시스템 버전 업데이트 * feat: ReviewTextarea 컴포넌트 추가 * feat: placeholder 추가 --- frontend/package.json | 2 +- .../ReviewTextarea/ReviewTextarea.stories.tsx | 13 +++++ .../Review/ReviewTextarea/ReviewTextarea.tsx | 49 +++++++++++++++++++ frontend/yarn.lock | 8 +-- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx create mode 100644 frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx diff --git a/frontend/package.json b/frontend/package.json index 72eab287..794f5139 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "build-storybook": "storybook build" }, "dependencies": { - "@fun-eat/design-system": "^0.2.2", + "@fun-eat/design-system": "^0.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.2", diff --git a/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx b/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx new file mode 100644 index 00000000..d51a623b --- /dev/null +++ b/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.stories.tsx @@ -0,0 +1,13 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import ReviewTextarea from './ReviewTextarea'; + +const meta: Meta = { + title: 'review/ReviewTextarea', + component: ReviewTextarea, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx b/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx new file mode 100644 index 00000000..3da619bd --- /dev/null +++ b/frontend/src/components/Review/ReviewTextarea/ReviewTextarea.tsx @@ -0,0 +1,49 @@ +import { Heading, Spacing, Text, Textarea, useTheme } from '@fun-eat/design-system'; +import type { ChangeEventHandler } from 'react'; +import { useState } from 'react'; +import styled from 'styled-components'; + +const MAX_LENGTH = 200; + +const ReviewTextarea = () => { + const [reviewValue, setReviewValue] = useState(''); + const theme = useTheme(); + + const handleReviewInput: ChangeEventHandler = (event) => { + setReviewValue(event.currentTarget.value); + }; + + return ( + + + 리뷰를 작성해주세요. (200자) + + +