This is an automated email from the ASF dual-hosted git repository. abeizn pushed a commit to branch release-v1.0 in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
commit db544eafdc544f45039b09fc42edffbfd55beb0a Author: 青湛 <[email protected]> AuthorDate: Mon Mar 25 16:55:49 2024 +1300 feat: add onboard process (#7199) --- config-ui/package.json | 4 +- config-ui/public/onboard/assets/github/1.png | Bin 0 -> 18549 bytes config-ui/public/onboard/assets/github/2.png | Bin 0 -> 25492 bytes config-ui/public/onboard/assets/github/3.png | Bin 0 -> 53740 bytes config-ui/public/onboard/assets/gitlab/1.png | Bin 0 -> 22729 bytes config-ui/public/onboard/assets/gitlab/2.png | Bin 0 -> 40874 bytes config-ui/public/onboard/assets/gitlab/3.png | Bin 0 -> 23739 bytes config-ui/public/onboard/step-1/azuredevops.md | 25 + config-ui/public/onboard/step-1/bitbucket.md | 25 + config-ui/public/onboard/step-1/default.md | 21 + config-ui/public/onboard/step-1/github.md | 25 + config-ui/public/onboard/step-1/gitlab.md | 25 + config-ui/public/onboard/step-2/azuredevops.md | 16 + config-ui/public/onboard/step-2/bitbucket.md | 16 + config-ui/public/onboard/step-2/github.md | 44 + config-ui/public/onboard/step-2/gitlab.md | 16 + config-ui/public/onboard/step-3/azuredevops.md | 16 + config-ui/public/onboard/step-3/bitbucket.md | 16 + config-ui/public/onboard/step-3/github.md | 34 + config-ui/public/onboard/step-3/gitlab.md | 16 + config-ui/src/api/index.ts | 4 +- .../project/home/styled.ts => api/store/index.ts} | 17 +- config-ui/src/app/routrer.tsx | 23 +- .../components/connection-form/fields/password.tsx | 1 - .../components/connection-form/fields/token.tsx | 1 - .../components/connection-form/fields/username.tsx | 18 - .../plugins/components/connection-select/index.tsx | 71 ++ .../data-scope-remote/data-scope-remote.tsx | 56 +- .../components/data-scope-remote/search-local.tsx | 4 +- .../components/data-scope-remote/search-remote.tsx | 5 +- config-ui/src/plugins/components/index.ts | 1 + .../register/github/connection-fields/token.tsx | 2 +- config-ui/src/routes/index.ts | 1 + config-ui/src/routes/layout/config.tsx | 10 +- config-ui/src/routes/layout/layout.tsx | 3 +- config-ui/src/routes/layout/loader.ts | 7 + config-ui/src/routes/onboard/card.tsx | 54 ++ .../home/styled.ts => onboard/context.tsx} | 38 +- config-ui/src/routes/onboard/index.tsx | 121 +++ config-ui/src/routes/onboard/step-0.tsx | 106 +++ config-ui/src/routes/onboard/step-1.tsx | 126 +++ config-ui/src/routes/onboard/step-2.tsx | 183 ++++ config-ui/src/routes/onboard/step-3.tsx | 161 ++++ .../{project/home/styled.ts => onboard/step-4.tsx} | 39 +- config-ui/src/routes/onboard/styled.ts | 149 ++++ config-ui/src/routes/project/home/index.tsx | 61 +- config-ui/vite.config.ts | 2 +- config-ui/yarn.lock | 934 ++++++++++++++++++++- 48 files changed, 2360 insertions(+), 137 deletions(-) diff --git a/config-ui/package.json b/config-ui/package.json index a5aa294f2..d3d2ee115 100644 --- a/config-ui/package.json +++ b/config-ui/package.json @@ -34,15 +34,17 @@ "dayjs": "^1.11.10", "file-saver": "^2.0.5", "lodash": "^4.17.21", - "miller-columns-select": "1.3.2", + "miller-columns-select": "1.4.0", "react": "^18.2.0", "react-copy-to-clipboard": "^5.1.0", "react-dom": "^18.2.0", "react-is": "^18.2.0", + "react-markdown": "^9.0.1", "react-redux": "^9.1.0", "react-router-dom": "^6.22.2", "react-transition-group": "^4.4.5", "redux": "^5.0.1", + "rehype-raw": "^7.0.0", "styled-components": "^6.1.8" }, "devDependencies": { diff --git a/config-ui/public/onboard/assets/github/1.png b/config-ui/public/onboard/assets/github/1.png new file mode 100644 index 000000000..f1766e453 Binary files /dev/null and b/config-ui/public/onboard/assets/github/1.png differ diff --git a/config-ui/public/onboard/assets/github/2.png b/config-ui/public/onboard/assets/github/2.png new file mode 100644 index 000000000..568440f29 Binary files /dev/null and b/config-ui/public/onboard/assets/github/2.png differ diff --git a/config-ui/public/onboard/assets/github/3.png b/config-ui/public/onboard/assets/github/3.png new file mode 100644 index 000000000..2e0f94a39 Binary files /dev/null and b/config-ui/public/onboard/assets/github/3.png differ diff --git a/config-ui/public/onboard/assets/gitlab/1.png b/config-ui/public/onboard/assets/gitlab/1.png new file mode 100644 index 000000000..11504a763 Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/1.png differ diff --git a/config-ui/public/onboard/assets/gitlab/2.png b/config-ui/public/onboard/assets/gitlab/2.png new file mode 100644 index 000000000..1c950ebba Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/2.png differ diff --git a/config-ui/public/onboard/assets/gitlab/3.png b/config-ui/public/onboard/assets/gitlab/3.png new file mode 100644 index 000000000..483286d6c Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/3.png differ diff --git a/config-ui/public/onboard/step-1/azuredevops.md b/config-ui/public/onboard/step-1/azuredevops.md new file mode 100644 index 000000000..6018ef364 --- /dev/null +++ b/config-ui/public/onboard/step-1/azuredevops.md @@ -0,0 +1,25 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### Azure DevOps tells you: + +1. How much code have you submitted? +  +2. How are your Pull Requests handled? +  +3. How fast is your Code Review process? +  diff --git a/config-ui/public/onboard/step-1/bitbucket.md b/config-ui/public/onboard/step-1/bitbucket.md new file mode 100644 index 000000000..745c454e7 --- /dev/null +++ b/config-ui/public/onboard/step-1/bitbucket.md @@ -0,0 +1,25 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### Bitbucket tells you: + +1. How much code have you submitted? +  +2. How are your Pull Requests handled? +  +3. How fast is your Code Review process? +  diff --git a/config-ui/public/onboard/step-1/default.md b/config-ui/public/onboard/step-1/default.md new file mode 100644 index 000000000..f2183b8ba --- /dev/null +++ b/config-ui/public/onboard/step-1/default.md @@ -0,0 +1,21 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### What is a data connection? + +- On a high level, a project can be viewed as a real-world project or product line. It represents a specific initiative or endeavor within the software development domain. +- On a lower level, a project is a way of organizing and grouping data from different domains. diff --git a/config-ui/public/onboard/step-1/github.md b/config-ui/public/onboard/step-1/github.md new file mode 100644 index 000000000..154fe51bd --- /dev/null +++ b/config-ui/public/onboard/step-1/github.md @@ -0,0 +1,25 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### GitHub tells you: + +1. What is your issue lead time? +  +2. How fast is your Code Review process? +  +3. How is your CI/CD process? +  diff --git a/config-ui/public/onboard/step-1/gitlab.md b/config-ui/public/onboard/step-1/gitlab.md new file mode 100644 index 000000000..1878dc998 --- /dev/null +++ b/config-ui/public/onboard/step-1/gitlab.md @@ -0,0 +1,25 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### GitLab tells you: + +1. How much code have you submitted? +  +2. How are your Merge Requests handled? +  +3. How fast is your Code Review process? +  diff --git a/config-ui/public/onboard/step-2/azuredevops.md b/config-ui/public/onboard/step-2/azuredevops.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-2/azuredevops.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/public/onboard/step-2/bitbucket.md b/config-ui/public/onboard/step-2/bitbucket.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-2/bitbucket.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/public/onboard/step-2/github.md b/config-ui/public/onboard/step-2/github.md new file mode 100644 index 000000000..74ac669c5 --- /dev/null +++ b/config-ui/public/onboard/step-2/github.md @@ -0,0 +1,44 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### Q1. How to create a classic GitHub personal access token? + +1. Log in to [github.com](https://github.com). +2. In the upper-right corner of any page, click your profile photo, then click Settings. +3. In the left sidebar, click <> Developer settings. +4. In the left sidebar, under 'Personal access tokens', click Tokens (classic). +5. Choose the following scopes: repo:status, repo:deployment, read:user and read:org. + +Check [this doc](https://devlake.apache.org/docs/Configuration/GitHub/#personal-access-tokens) for more details. + +##### Q2. What token scopes should I choose? + +Normally, only the following scopes are required: +`repo:status` `repo:deployment` `read:user` `read:org` + +However, if you want to collect data from private repositories, you need to give full permission to repo: +`repo` `read:user` `read:org` + +##### Q3. Can I connect to the GitHub server? + +Sure, you could + +1. Go to the Connections page. +2. Click Create a New Connection. +3. Choose 'GitHub server' and finish the connection. + +GitHub server is not supported to simplify this onboard process. diff --git a/config-ui/public/onboard/step-2/gitlab.md b/config-ui/public/onboard/step-2/gitlab.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-2/gitlab.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/public/onboard/step-3/azuredevops.md b/config-ui/public/onboard/step-3/azuredevops.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-3/azuredevops.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/public/onboard/step-3/bitbucket.md b/config-ui/public/onboard/step-3/bitbucket.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-3/bitbucket.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/public/onboard/step-3/github.md b/config-ui/public/onboard/step-3/github.md new file mode 100644 index 000000000..388751d2f --- /dev/null +++ b/config-ui/public/onboard/step-3/github.md @@ -0,0 +1,34 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> + +##### Q1. What GitHub data will be collected? + +`issues` `repos` `commits` `branches` `pull requests` `pr comments` `workflow runs` `job runs` `deployments` `users` + +Check [this doc](https://devlake.apache.org/docs/Overview/SupportedDataSources/#data-collection-scope-by-each-plugin) for more details. + +##### Q2. The data from which time range will be collected? + +Only the data from last 14 days will be collected to speed up the sync up time. You can always change the time range in the project details page later. + +##### Q3. Can I do transformations on the collected data? + +Yes. You can do transformations by adding a Scope Config to the repositories you choose later. + +##### Q4. What is the frequency to sync up data? + +The data will be synced daily, you can change it in the project details page later. diff --git a/config-ui/public/onboard/step-3/gitlab.md b/config-ui/public/onboard/step-3/gitlab.md new file mode 100644 index 000000000..449e8a88c --- /dev/null +++ b/config-ui/public/onboard/step-3/gitlab.md @@ -0,0 +1,16 @@ +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> diff --git a/config-ui/src/api/index.ts b/config-ui/src/api/index.ts index 60456a198..f211e5640 100644 --- a/config-ui/src/api/index.ts +++ b/config-ui/src/api/index.ts @@ -26,6 +26,7 @@ import plugin from './plugin'; import * as project from './project'; import * as scope from './scope'; import * as scopeConfig from './scope-config'; +import * as store from './store'; import * as task from './task'; const migrate = () => request('/proceed-db-migration'); @@ -39,8 +40,9 @@ export const API = { pipeline, plugin, project, - scopeConfig, scope, + scopeConfig, + store, task, migrate, ping, diff --git a/config-ui/src/routes/project/home/styled.ts b/config-ui/src/api/store/index.ts similarity index 74% copy from config-ui/src/routes/project/home/styled.ts copy to config-ui/src/api/store/index.ts index 3c73c29b7..ae479cb00 100644 --- a/config-ui/src/routes/project/home/styled.ts +++ b/config-ui/src/api/store/index.ts @@ -16,19 +16,8 @@ * */ -import styled from 'styled-components'; +import { request } from '@/utils'; -export const DialogWrapper = styled.div``; +export const get = (key: string) => request(`/store/${key}`); -export const Label = styled.label` - font-size: 16px; - font-weight: 600; -`; - -export const LabelInfo = styled.i` - color: #ff8b8b; -`; - -export const LabelDescription = styled.p` - margin: 0; -`; +export const set = (key: string, value: any) => request(`/store/${key}`, { method: 'PUT', data: value }); diff --git a/config-ui/src/app/routrer.tsx b/config-ui/src/app/routrer.tsx index e8d6ce94c..ac3113db7 100644 --- a/config-ui/src/app/routrer.tsx +++ b/config-ui/src/app/routrer.tsx @@ -20,6 +20,7 @@ import { createBrowserRouter, Navigate } from 'react-router-dom'; import { DBMigrate, + Onboard, Error, Layout, layoutLoader, @@ -47,6 +48,10 @@ export const router = createBrowserRouter([ path: `${PATH_PREFIX}/db-migrate`, element: <DBMigrate />, }, + { + path: `${PATH_PREFIX}/onboard`, + element: <Onboard />, + }, { path: `${PATH_PREFIX}`, element: <Layout />, @@ -55,15 +60,7 @@ export const router = createBrowserRouter([ children: [ { index: true, - element: <Navigate to="connections" />, - }, - { - path: 'connections', - element: <Connections />, - }, - { - path: 'connections/:plugin/:id', - element: <Connection />, + element: <Navigate to="projects" />, }, { path: 'projects', @@ -77,6 +74,14 @@ export const router = createBrowserRouter([ path: 'projects/:pname/:unique', element: <BlueprintConnectionDetailPage />, }, + { + path: 'connections', + element: <Connections />, + }, + { + path: 'connections/:plugin/:id', + element: <Connection />, + }, { path: 'advanced', children: [ diff --git a/config-ui/src/plugins/components/connection-form/fields/password.tsx b/config-ui/src/plugins/components/connection-form/fields/password.tsx index de6e7f190..6c8312911 100644 --- a/config-ui/src/plugins/components/connection-form/fields/password.tsx +++ b/config-ui/src/plugins/components/connection-form/fields/password.tsx @@ -26,7 +26,6 @@ interface Props { label?: string; subLabel?: string; placeholder?: string; - name: string; initialValue: string; value: string; error: string; diff --git a/config-ui/src/plugins/components/connection-form/fields/token.tsx b/config-ui/src/plugins/components/connection-form/fields/token.tsx index 1882a45f3..1788ed395 100644 --- a/config-ui/src/plugins/components/connection-form/fields/token.tsx +++ b/config-ui/src/plugins/components/connection-form/fields/token.tsx @@ -42,7 +42,6 @@ interface Props { type: 'create' | 'update'; label?: string; subLabel?: string; - name: string; initialValue: string; value: string; error: string; diff --git a/config-ui/src/plugins/components/connection-form/fields/username.tsx b/config-ui/src/plugins/components/connection-form/fields/username.tsx index e69b43ad8..9c294eadb 100644 --- a/config-ui/src/plugins/components/connection-form/fields/username.tsx +++ b/config-ui/src/plugins/components/connection-form/fields/username.tsx @@ -15,23 +15,6 @@ * limitations under the License. * */ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ import { useEffect } from 'react'; import { Input } from 'antd'; @@ -42,7 +25,6 @@ interface Props { label?: string; subLabel?: string; placeholder?: string; - name: string; initialValue: string; value: string; error: string; diff --git a/config-ui/src/plugins/components/connection-select/index.tsx b/config-ui/src/plugins/components/connection-select/index.tsx new file mode 100644 index 000000000..71c7dab37 --- /dev/null +++ b/config-ui/src/plugins/components/connection-select/index.tsx @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { theme, Select, SelectProps } from 'antd'; +import styled from 'styled-components'; + +import { getPluginConfig } from '@/plugins'; + +const Option = styled.div` + display: flex; + align-items: center; + + .icon { + display: inline-block; + width: 24px; + height: 24px; + + & > svg { + width: 100%; + height: 100%; + } + } + + .name { + margin-left: 8px; + max-width: 90%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } +`; + +interface Props extends Omit<SelectProps, 'optionRender'> {} + +export const ConnectionSelect = ({ ...props }: Props) => { + const { + token: { colorPrimary }, + } = theme.useToken(); + + return ( + <Select + style={{ width: 384 }} + placeholder="Select..." + optionRender={(option) => { + const plugin = getPluginConfig(option.data.plugin); + return ( + <Option> + <span className="icon">{plugin.icon({ color: colorPrimary })}</span> + <span className="name">{option.label}</span> + </Option> + ); + }} + {...props} + /> + ); +}; diff --git a/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx b/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx index 9e80307f8..6bf150916 100644 --- a/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx +++ b/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx @@ -16,7 +16,7 @@ * */ -import { useState, useMemo } from 'react'; +import { useState, useEffect, useMemo } from 'react'; import { Flex, Button } from 'antd'; import API from '@/api'; @@ -27,17 +27,35 @@ import { SearchLocal } from './search-local'; import { SearchRemote } from './search-remote'; interface Props { + mode?: 'single' | 'multiple'; plugin: string; connectionId: ID; + selectedScope?: any[]; disabledScope?: any[]; - onCancel: () => void; - onSubmit: (origin: any) => void; + onChangeSelectedScope?: (scope: any[]) => void; + footer?: React.ReactNode; + onCancel?: () => void; + onSubmit?: (origin: any) => void; } -export const DataScopeRemote = ({ plugin, connectionId, disabledScope, onCancel, onSubmit }: Props) => { +export const DataScopeRemote = ({ + mode = 'multiple', + plugin, + connectionId, + disabledScope, + onChangeSelectedScope, + footer, + onCancel, + onSubmit, + ...props +}: Props) => { const [selectedScope, setSelectedScope] = useState<any[]>([]); const [operating, setOperating] = useState(false); + useEffect(() => { + setSelectedScope(props.selectedScope ?? []); + }, [props.selectedScope]); + const config = useMemo(() => getPluginConfig(plugin).dataScope, [plugin]); const handleSubmit = async () => { @@ -50,7 +68,7 @@ export const DataScopeRemote = ({ plugin, connectionId, disabledScope, onCancel, ); if (success) { - onSubmit(res); + onSubmit?.(res); } }; @@ -61,35 +79,41 @@ export const DataScopeRemote = ({ plugin, connectionId, disabledScope, onCancel, connectionId, disabledItems: disabledScope?.map((it) => ({ id: it.id })), selectedItems: selectedScope, - onChangeSelectedItems: setSelectedScope, + onChangeSelectedItems: onChangeSelectedScope ?? setSelectedScope, }) ) : config.localSearch ? ( <SearchLocal + mode={mode} plugin={plugin} connectionId={connectionId} config={config} disabledScope={disabledScope ?? []} selectedScope={selectedScope} - onChange={setSelectedScope} + onChange={onChangeSelectedScope ?? setSelectedScope} /> ) : ( <SearchRemote + mode={mode} plugin={plugin} connectionId={connectionId} config={config} disabledScope={disabledScope ?? []} selectedScope={selectedScope} - onChange={setSelectedScope} + onChange={onChangeSelectedScope ?? setSelectedScope} /> )} - <Flex style={{ marginTop: 16 }} justify="flex-end" gap="small"> - <Button disabled={operating} onClick={onCancel}> - Cancel - </Button> - <Button type="primary" loading={operating} disabled={!selectedScope.length} onClick={handleSubmit}> - Save - </Button> - </Flex> + {footer !== undefined ? ( + footer + ) : ( + <Flex style={{ marginTop: 16 }} justify="flex-end" gap="small"> + <Button disabled={operating} onClick={onCancel}> + Cancel + </Button> + <Button type="primary" loading={operating} disabled={!selectedScope.length} onClick={handleSubmit}> + Save + </Button> + </Flex> + )} </Flex> ); }; diff --git a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx index 244b72de2..70660c71f 100644 --- a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx +++ b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx @@ -31,6 +31,7 @@ import * as T from './types'; import * as S from './styled'; interface Props { + mode: 'single' | 'multiple'; plugin: string; connectionId: ID; config: IPluginConfig['dataScope']; @@ -41,7 +42,7 @@ interface Props { let canceling = false; -export const SearchLocal = ({ plugin, connectionId, config, disabledScope, selectedScope, onChange }: Props) => { +export const SearchLocal = ({ mode, plugin, connectionId, config, disabledScope, selectedScope, onChange }: Props) => { const [miller, setMiller] = useState<{ items: McsItem<T.ResItem>[]; loadedIds: ID[]; @@ -235,6 +236,7 @@ export const SearchLocal = ({ plugin, connectionId, config, disabledScope, selec <Input prefix={<SearchOutlined />} value={query} onChange={(e) => setQuery(e.target.value)} /> )} <MillerColumnsSelect + mode={mode} items={scopes} columnCount={search ? 1 : config.millerColumn?.columnCount ?? 1} columnHeight={300} diff --git a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx index f49a10244..4679b8564 100644 --- a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx +++ b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx @@ -32,6 +32,7 @@ import * as T from './types'; import * as S from './styled'; interface Props { + mode: 'single' | 'multiple'; plugin: string; connectionId: ID; config: IPluginConfig['dataScope']; @@ -40,7 +41,7 @@ interface Props { onChange: (selectedScope: any[]) => void; } -export const SearchRemote = ({ plugin, connectionId, config, disabledScope, selectedScope, onChange }: Props) => { +export const SearchRemote = ({ mode, plugin, connectionId, config, disabledScope, selectedScope, onChange }: Props) => { const [miller, setMiller] = useState<{ items: McsItem<T.ResItem>[]; loadedIds: ID[]; @@ -180,6 +181,7 @@ export const SearchRemote = ({ plugin, connectionId, config, disabledScope, sele /> {!searchDebounce ? ( <MillerColumnsSelect + mode={mode} items={miller.items} columnCount={config.millerColumn?.columnCount ?? 1} columnHeight={300} @@ -202,6 +204,7 @@ export const SearchRemote = ({ plugin, connectionId, config, disabledScope, sele /> ) : ( <MillerColumnsSelect + mode={mode} items={search.currentItems} columnCount={1} columnHeight={300} diff --git a/config-ui/src/plugins/components/index.ts b/config-ui/src/plugins/components/index.ts index 82d08d3be..2f4049cab 100644 --- a/config-ui/src/plugins/components/index.ts +++ b/config-ui/src/plugins/components/index.ts @@ -18,6 +18,7 @@ export * from './connection-form'; export * from './connection-list'; +export * from './connection-select'; export * from './connection-status'; export * from './data-scope-remote'; export * from './data-scope-select'; diff --git a/config-ui/src/plugins/register/github/connection-fields/token.tsx b/config-ui/src/plugins/register/github/connection-fields/token.tsx index b6f835349..f4a20d05a 100644 --- a/config-ui/src/plugins/register/github/connection-fields/token.tsx +++ b/config-ui/src/plugins/register/github/connection-fields/token.tsx @@ -41,7 +41,7 @@ interface Props { initialValue: string; value: string; error: string; - setValue: (value?: string) => void; + setValue: (value: string) => void; setError: (error?: string) => void; } diff --git a/config-ui/src/routes/index.ts b/config-ui/src/routes/index.ts index b640be499..025a0792f 100644 --- a/config-ui/src/routes/index.ts +++ b/config-ui/src/routes/index.ts @@ -23,5 +23,6 @@ export * from './db-migrate'; export * from './error'; export * from './layout'; export * from './not-found'; +export * from './onboard'; export * from './pipeline'; export * from './project'; diff --git a/config-ui/src/routes/layout/config.tsx b/config-ui/src/routes/layout/config.tsx index 0e0fd050f..48cf81595 100644 --- a/config-ui/src/routes/layout/config.tsx +++ b/config-ui/src/routes/layout/config.tsx @@ -40,16 +40,16 @@ type MenuItem = { }; export const menuItems: MenuItem[] = [ - { - key: `${PATH_PREFIX}/connections`, - label: 'Connections', - icon: <AppstoreOutlined />, - }, { key: `${PATH_PREFIX}/projects`, label: 'Projects', icon: <ProjectOutlined />, }, + { + key: `${PATH_PREFIX}/connections`, + label: 'Connections', + icon: <AppstoreOutlined />, + }, { key: `${PATH_PREFIX}/advanced`, label: 'Advanced', diff --git a/config-ui/src/routes/layout/layout.tsx b/config-ui/src/routes/layout/layout.tsx index e33a74576..51f5c630c 100644 --- a/config-ui/src/routes/layout/layout.tsx +++ b/config-ui/src/routes/layout/layout.tsx @@ -37,7 +37,6 @@ import { import { useAppDispatch, useAppSelector } from '@/hooks'; import { operator } from '@/utils'; -import { layoutLoader } from './loader'; import { menuItems, menuItemsMatch, headerItems } from './config'; import * as S from './styled'; import './tips-transition.css'; @@ -49,7 +48,7 @@ export const Layout = () => { const [selectedKeys, setSelectedKeys] = useState<string[]>([]); const [operating, setOperating] = useState(false); - const { version, plugins } = useLoaderData() as Awaited<ReturnType<typeof layoutLoader>>; + const { version, plugins } = useLoaderData() as { version: string; plugins: string[] }; const navigate = useNavigate(); const { pathname } = useLocation(); diff --git a/config-ui/src/routes/layout/loader.ts b/config-ui/src/routes/layout/loader.ts index 2bba5a86a..b52021b41 100644 --- a/config-ui/src/routes/layout/loader.ts +++ b/config-ui/src/routes/layout/loader.ts @@ -16,6 +16,7 @@ * */ +import { redirect } from 'react-router-dom'; import { intersection } from 'lodash'; import API from '@/api'; @@ -26,6 +27,12 @@ type Props = { }; export const layoutLoader = async ({ request }: Props) => { + const onboard = await API.store.get('onboard'); + + if (!onboard) { + return redirect('/onboard'); + } + let fePlugins = getRegisterPlugins(); const bePlugins = await API.plugin.list(); diff --git a/config-ui/src/routes/onboard/card.tsx b/config-ui/src/routes/onboard/card.tsx new file mode 100644 index 000000000..0dc9639c2 --- /dev/null +++ b/config-ui/src/routes/onboard/card.tsx @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useNavigate } from 'react-router-dom'; +import { Card, Flex, Progress, Button } from 'antd'; + +import API from '@/api'; +import { useRefreshData } from '@/hooks'; + +interface Props { + style?: React.CSSProperties; +} + +export const OnboardCard = ({ style }: Props) => { + const navigate = useNavigate(); + + const { ready, data } = useRefreshData(() => API.store.get('onboard'), []); + + if (!ready || !data || data.done) { + return null; + } + + return ( + <Card style={style}> + <Flex align="center" justify="space-between"> + <Flex align="center"> + <Progress type="circle" size={30} format={() => `${data.step}/4`} percent={(data.step / 4) * 100} /> + <div style={{ marginLeft: 16 }}> + <h4>Onboard Session</h4> + <h5>You are not far from connecting to your first tool. Continue to finish it..</h5> + </div> + </Flex> + <Button type="primary" onClick={() => navigate('/onboard')}> + Continue + </Button> + </Flex> + </Card> + ); +}; diff --git a/config-ui/src/routes/project/home/styled.ts b/config-ui/src/routes/onboard/context.tsx similarity index 57% copy from config-ui/src/routes/project/home/styled.ts copy to config-ui/src/routes/onboard/context.tsx index 3c73c29b7..7f472d94f 100644 --- a/config-ui/src/routes/project/home/styled.ts +++ b/config-ui/src/routes/onboard/context.tsx @@ -16,19 +16,29 @@ * */ -import styled from 'styled-components'; +import { createContext } from 'react'; -export const DialogWrapper = styled.div``; +const initialValue: { + step: number; + records: Array<{ + plugin: string; + connectionId: ID; + }>; + done: boolean; + projectName?: string; + plugin?: string; + setStep: (value: number) => void; + setRecords: (value: Array<{ plugin: string; connectionId: ID }>) => void; + setProjectName: (value: string) => void; + setPlugin: (value: string) => void; +} = { + step: 0, + records: [], + done: false, + setStep: () => {}, + setRecords: () => {}, + setProjectName: () => {}, + setPlugin: () => {}, +}; -export const Label = styled.label` - font-size: 16px; - font-weight: 600; -`; - -export const LabelInfo = styled.i` - color: #ff8b8b; -`; - -export const LabelDescription = styled.p` - margin: 0; -`; +export const Context = createContext(initialValue); diff --git a/config-ui/src/routes/onboard/index.tsx b/config-ui/src/routes/onboard/index.tsx new file mode 100644 index 000000000..d2b4d1e6e --- /dev/null +++ b/config-ui/src/routes/onboard/index.tsx @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useState, useEffect } from 'react'; +import { CloseOutlined } from '@ant-design/icons'; +import { theme, Layout } from 'antd'; + +import API from '@/api'; +import { PageLoading } from '@/components'; +import { useRefreshData } from '@/hooks'; + +import { Context } from './context'; +import { Step0 } from './step-0'; +import { Step1 } from './step-1'; +import { Step2 } from './step-2'; +import { Step3 } from './step-3'; +import { Step4 } from './step-4'; +import * as S from './styled'; + +const steps = [ + { + step: 1, + title: 'Create Project', + }, + { + step: 2, + title: 'Configure Connection', + }, + { + step: 3, + title: 'Add data scope', + }, +]; + +export const Onboard = () => { + const [step, setStep] = useState(0); + const [records, setRecords] = useState<Array<{ plugin: string; connectionId: ID }>>([]); + const [projectName, setProjectName] = useState<string>(); + const [plugin, setPlugin] = useState<string>(); + + const { + token: { colorPrimary }, + } = theme.useToken(); + + const { ready, data } = useRefreshData(() => API.store.get('onboard')); + + useEffect(() => { + if (ready && data) { + setStep(data.step); + setRecords(data.records); + setProjectName(data.projectName); + setPlugin(data.plugin); + } + }, [ready, data]); + + if (!ready) { + return <PageLoading />; + } + + return ( + <Context.Provider + value={{ + step, + records, + done: false, + projectName, + plugin, + setStep, + setRecords, + setProjectName: setProjectName, + setPlugin: setPlugin, + }} + > + <Layout style={{ height: '100vh' }}> + <S.Inner> + {step === 0 ? ( + <Step0 /> + ) : ( + <> + <S.Header> + <h1>Connect to your first repository</h1> + <CloseOutlined style={{ fontSize: 18, color: '#70727F', cursor: 'pointer' }} /> + </S.Header> + <S.Content> + {[1, 2, 3].includes(step) && ( + <S.Step> + {steps.map((it) => ( + <S.StepItem key={it.step} $actived={it.step === step} $activedColor={colorPrimary}> + <span>{it.step}</span> + <span>{it.title}</span> + </S.StepItem> + ))} + </S.Step> + )} + {step === 1 && <Step1 />} + {step === 2 && <Step2 />} + {step === 3 && <Step3 />} + {step === 4 && <Step4 />} + </S.Content> + </> + )} + </S.Inner> + </Layout> + </Context.Provider> + ); +}; diff --git a/config-ui/src/routes/onboard/step-0.tsx b/config-ui/src/routes/onboard/step-0.tsx new file mode 100644 index 000000000..fb35d5078 --- /dev/null +++ b/config-ui/src/routes/onboard/step-0.tsx @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useState, useContext } from 'react'; +import { Button } from 'antd'; +import styled from 'styled-components'; + +import API from '@/api'; +import { operator } from '@/utils'; + +import { Context } from './context'; + +const Wrapper = styled.div` + margin-top: 100px; + text-align: center; + + .welcome { + display: flex; + align-items: center; + justify-content: center; + padding: 30px 0; + font-size: 40px; + + span.line { + display: inline-block; + width: 242px; + height: 1px; + background-color: #dbdcdf; + } + + span.content { + margin: 0 24px; + } + } + + .title { + margin: 15px 0; + padding: 16px 0; + font-size: 60px; + font-weight: 600; + } + + .subTitle { + font-size: 16px; + } + + .action { + margin: 0 auto; + width: 268px; + margin-top: 64px; + } +`; + +export const Step0 = () => { + const [operating, setOperating] = useState(false); + + const { step, records, done, projectName, plugin, setStep } = useContext(Context); + + const handleSubmit = async () => { + const [success] = await operator( + async () => API.store.set('onboard', { step: 1, records, done, projectName, plugin }), + { + setOperating, + hideToast: true, + }, + ); + + if (success) { + setStep(step + 1); + } + }; + + return ( + <Wrapper> + <div className="welcome"> + <span className="line" /> + <span className="content">Welcome</span> + <span className="line" /> + </div> + <div className="title">Connect to your first repository</div> + <div className="subTitle"> + Integrate your first Git tool and observe engineering metrics with just a few clicks. + </div> + <div className="action"> + <Button block type="primary" loading={operating} onClick={handleSubmit}> + Start + </Button> + </div> + </Wrapper> + ); +}; diff --git a/config-ui/src/routes/onboard/step-1.tsx b/config-ui/src/routes/onboard/step-1.tsx new file mode 100644 index 000000000..73850c488 --- /dev/null +++ b/config-ui/src/routes/onboard/step-1.tsx @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useState, useEffect, useContext } from 'react'; +import { Input, Flex, Button, message } from 'antd'; +import Markdown from 'react-markdown'; +import rehypeRaw from 'rehype-raw'; + +import API from '@/api'; +import { Block } from '@/components'; +import { ConnectionSelect } from '@/plugins'; +import { validName } from '@/routes/project'; +import { operator } from '@/utils'; + +import { Context } from './context'; +import * as S from './styled'; + +export const Step1 = () => { + const [QA, setQA] = useState(''); + const [operating, setOperating] = useState(false); + + const { step, records, done, projectName, plugin, setStep, setProjectName, setPlugin } = useContext(Context); + + useEffect(() => { + fetch(`/onboard/step-1/${plugin ? plugin : 'default'}.md`) + .then((res) => res.text()) + .then((text) => setQA(text)); + }, [plugin]); + + const handleSubmit = async () => { + if (!projectName || !validName(projectName)) { + message.error('Please enter alphanumeric or underscore'); + return; + } + + if (!plugin) { + message.error('Please select a plugin'); + return; + } + + const [success] = await operator(() => API.store.set('onboard', { step: 2, records, done, projectName, plugin }), { + setOperating, + hideToast: true, + }); + + if (success) { + setStep(step + 1); + } + }; + + return ( + <> + <S.StepContent> + <div className="content"> + <Block + title="Project Name" + description="Give your project a unique name with letters, numbers, -, _ or /" + required + > + <Input + style={{ width: 386 }} + placeholder="Your Project Name" + value={projectName} + onChange={(e) => setProjectName(e.target.value)} + /> + </Block> + <Block title="Data Connections" description="You can only choose one data connection" required> + <ConnectionSelect + placeholder="Select a Data Connection" + options={[ + { + plugin: 'github', + value: 'github', + label: 'GitHub', + }, + { + plugin: 'gitlab', + value: 'gitlab', + label: 'GitLab', + }, + { + plugin: 'bitbucket', + value: 'bitbucket', + label: 'Bitbucket', + }, + { + plugin: 'azuredevops', + value: 'azuredevops', + label: 'Azure DevOps', + }, + ]} + value={plugin} + onChange={setPlugin} + /> + </Block> + </div> + <Markdown className="qa" rehypePlugins={[rehypeRaw]}> + {QA} + </Markdown> + </S.StepContent> + <Flex style={{ marginTop: 64 }} justify="space-between"> + <Button ghost type="primary" loading={operating} onClick={() => setStep(step - 1)}> + Previous Step + </Button> + <Button type="primary" loading={operating} disabled={!projectName || !plugin} onClick={handleSubmit}> + Next Step + </Button> + </Flex> + </> + ); +}; diff --git a/config-ui/src/routes/onboard/step-2.tsx b/config-ui/src/routes/onboard/step-2.tsx new file mode 100644 index 000000000..be4552463 --- /dev/null +++ b/config-ui/src/routes/onboard/step-2.tsx @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useState, useContext, useEffect, useMemo } from 'react'; +import { Flex, Button } from 'antd'; +import Markdown from 'react-markdown'; +import rehypeRaw from 'rehype-raw'; + +import API from '@/api'; +import { getPluginConfig } from '@/plugins'; +import { ConnectionToken } from '@/plugins/components/connection-form/fields/token'; +import { ConnectionUsername } from '@/plugins/components/connection-form/fields/username'; +import { ConnectionPassword } from '@/plugins/components/connection-form/fields/password'; +import { operator } from '@/utils'; + +import { Context } from './context'; +import * as S from './styled'; + +const paramsMap: Record<string, any> = { + github: { + authMethod: 'AccessToken', + endpoint: 'https://api.github.com/', + }, + gitlab: { + endpoint: 'https://gitlab.com/api/v4/', + }, + bitbucket: { + endpoint: 'https://api.bitbucket.org/2.0/', + }, + azuredevops: {}, +}; + +export const Step2 = () => { + const [QA, setQA] = useState(''); + const [operating, setOperating] = useState(false); + const [testing, setTesting] = useState(false); + const [testStaus, setTestStatus] = useState(false); + const [payload, setPayload] = useState<any>({}); + + const { step, records, done, projectName, plugin, setStep, setRecords } = useContext(Context); + + const config = useMemo(() => getPluginConfig(plugin as string), [plugin]); + + useEffect(() => { + fetch(`/onboard/step-2/${plugin}.md`) + .then((res) => res.text()) + .then((text) => setQA(text)); + }, [plugin]); + + const handleTest = async () => { + if (!plugin) { + return; + } + + const [success] = await operator( + async () => + await API.connection.testOld(plugin, { + ...paramsMap[plugin], + ...payload, + }), + { + setOperating: setTesting, + }, + ); + + if (success) { + setTestStatus(true); + } + }; + + const handleSubmit = async () => { + if (!plugin) { + return; + } + + const [success] = await operator( + async () => { + const connection = await API.connection.create(plugin, { + name: `${plugin}-${Date.now()}`, + ...paramsMap[plugin], + ...payload, + }); + + const newRecords = [...records, { plugin, connectionId: connection.id }]; + + setRecords(newRecords); + + await API.store.set('onboard', { + step: 3, + records: newRecords, + done, + projectName, + plugin, + }); + }, + { + setOperating, + hideToast: true, + }, + ); + + if (success) { + setStep(step + 1); + } + }; + + if (!plugin) { + return null; + } + + return ( + <> + <S.StepContent> + {['github', 'gitlab', 'azuredevops'].includes(plugin) && ( + <div className="content"> + <ConnectionToken + type="create" + label="Personal Access Token" + subLabel={`Create a personal access token in ${config.name}`} + initialValue="" + value={payload.token} + setValue={(token) => setPayload({ ...payload, token })} + error="" + setError={() => {}} + /> + <Button style={{ marginTop: 16 }} disabled={!payload.token} loading={testing} onClick={handleTest}> + Connect + </Button> + </div> + )} + {['bitbucket'].includes(plugin) && ( + <div className="content"> + <ConnectionUsername + initialValue="" + value={payload.username} + setValue={(username) => setPayload({ ...payload, username })} + error="" + setError={() => {}} + /> + <ConnectionPassword + type="create" + label="App Password" + initialValue="" + value={payload.password} + setValue={(password) => setPayload({ ...payload, password })} + error="" + setError={() => {}} + /> + <Button disabled={!payload.username || !payload.password} loading={testing} onClick={handleTest}> + Connect + </Button> + </div> + )} + <Markdown className="qa" rehypePlugins={[rehypeRaw]}> + {QA} + </Markdown> + </S.StepContent> + <Flex style={{ marginTop: 36 }} justify="space-between"> + <Button ghost type="primary" loading={operating} onClick={() => setStep(step - 1)}> + Previous Step + </Button> + <Button type="primary" loading={operating} disabled={!testStaus} onClick={handleSubmit}> + Next Step + </Button> + </Flex> + </> + ); +}; diff --git a/config-ui/src/routes/onboard/step-3.tsx b/config-ui/src/routes/onboard/step-3.tsx new file mode 100644 index 000000000..c66e3f9ab --- /dev/null +++ b/config-ui/src/routes/onboard/step-3.tsx @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { useState, useContext, useEffect, useMemo } from 'react'; +import { Flex, Button } from 'antd'; +import dayjs from 'dayjs'; +import Markdown from 'react-markdown'; +import rehypeRaw from 'rehype-raw'; + +import API from '@/api'; +import { cronPresets } from '@/config'; +import { IBPMode } from '@/types'; +import { DataScopeRemote, getPluginScopeId } from '@/plugins'; +import { operator, formatTime } from '@/utils'; + +import { Context } from './context'; +import * as S from './styled'; + +export const Step3 = () => { + const [QA, setQA] = useState(''); + const [operating, setOperating] = useState(false); + const [scopes, setScopes] = useState<any[]>([]); + + const { step, records, done, projectName, plugin, setStep } = useContext(Context); + + useEffect(() => { + fetch(`/onboard/step-3/${plugin}.md`) + .then((res) => res.text()) + .then((text) => setQA(text)); + }, [plugin]); + + const presets = useMemo(() => cronPresets.map((preset) => preset.config), []); + const connectionId = useMemo(() => { + const record = records.find((it) => it.plugin === plugin); + return record?.connectionId ?? null; + }, [plugin, records]); + + const handleSubmit = async () => { + if (!projectName || !plugin || !connectionId) { + return; + } + + const [success] = await operator( + async () => { + // 1. create a new project + await API.project.create({ + name: projectName, + description: '', + metrics: [ + { + pluginName: 'dora', + pluginOption: '', + enable: false, + }, + ], + }); + + // 2. add a data scope to the connection + await API.scope.batch(plugin, connectionId, { data: scopes.map((it) => it.data) }); + + // 3. create a new blueprint + const blueprint = await API.blueprint.create({ + name: `${projectName}-Blueprint`, + projectName, + mode: IBPMode.NORMAL, + enable: true, + cronConfig: presets[0], + isManual: false, + skipOnFail: true, + timeAfter: formatTime(dayjs().subtract(6, 'month').startOf('day').toDate(), 'YYYY-MM-DD[T]HH:mm:ssZ'), + connections: [ + { + pluginName: plugin, + connectionId, + scopes: scopes.map((it) => ({ + scopeId: getPluginScopeId(plugin, it.data), + })), + }, + ], + }); + + // 4. trigger this blueprint + await API.blueprint.trigger(blueprint.id, { skipCollectors: false, fullSync: false }); + + const newRecords = records.map((it) => + it.plugin !== plugin + ? it + : { + ...it, + scopeId: getPluginScopeId(plugin, scopes[0].data), + scopeName: scopes[0]?.fullName ?? scopes[0].name, + }, + ); + + // 5. update store + await API.store.set('onboard', { + step: 4, + records: newRecords, + done, + projectName, + plugin, + }); + }, + { + setOperating, + hideToast: true, + }, + ); + + if (success) { + setStep(step + 1); + } + }; + + if (!plugin || !connectionId) { + return null; + } + + return ( + <> + <S.StepContent> + <div className="content"> + <DataScopeRemote + mode="single" + plugin={plugin} + connectionId={connectionId} + selectedScope={scopes} + onChangeSelectedScope={setScopes} + footer={null} + /> + </div> + <Markdown className="qa" rehypePlugins={[rehypeRaw]}> + {QA} + </Markdown> + </S.StepContent> + <Flex style={{ marginTop: 36 }} justify="space-between"> + <Button ghost type="primary" loading={operating} onClick={() => setStep(step - 1)}> + Previous Step + </Button> + <Button type="primary" loading={operating} onClick={handleSubmit}> + Next Step + </Button> + </Flex> + </> + ); +}; diff --git a/config-ui/src/routes/project/home/styled.ts b/config-ui/src/routes/onboard/step-4.tsx similarity index 54% rename from config-ui/src/routes/project/home/styled.ts rename to config-ui/src/routes/onboard/step-4.tsx index 3c73c29b7..e2d51cfed 100644 --- a/config-ui/src/routes/project/home/styled.ts +++ b/config-ui/src/routes/onboard/step-4.tsx @@ -16,19 +16,38 @@ * */ +import { SmileFilled } from '@ant-design/icons'; +import { theme } from 'antd'; import styled from 'styled-components'; -export const DialogWrapper = styled.div``; +import * as S from './styled'; -export const Label = styled.label` - font-size: 16px; - font-weight: 600; -`; +const Top = styled.div` + display: flex; + align-items: center; + justify-content: center; + margin-top: 100px; + margin-bottom: 24px; + height: 70px; -export const LabelInfo = styled.i` - color: #ff8b8b; + span.text { + margin-left: 8px; + font-size: 20px; + } `; -export const LabelDescription = styled.p` - margin: 0; -`; +export const Step4 = () => { + const { + token: { green5 }, + } = theme.useToken(); + + return ( + <> + <Top> + <SmileFilled style={{ fontSize: 36, color: green5 }} /> + <span className="text">Congratulations!You have successfully connected to your first repository!</span> + </Top> + <S.StepContent style={{ padding: 24 }}></S.StepContent> + </> + ); +}; diff --git a/config-ui/src/routes/onboard/styled.ts b/config-ui/src/routes/onboard/styled.ts new file mode 100644 index 000000000..976060653 --- /dev/null +++ b/config-ui/src/routes/onboard/styled.ts @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import styled from 'styled-components'; + +export const Wrapper = styled.div` + width: 100%; + height: 100vh; + background-color: #f9f9fa; +`; + +export const Inner = styled.div` + margin: 0 auto; + padding: 36px 0; + width: 1200px; +`; + +export const Header = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const Content = styled.div` + margin: 0 auto; + width: 860px; +`; + +export const Step = styled.ul` + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 100px; + margin-bottom: 50px; +`; + +export const StepItem = styled.li<{ $actived: boolean; $activedColor: string }>` + display: flex; + align-items: center; + position: relative; + + span:first-child { + display: flex; + align-items: center; + justify-content: center; + margin-right: 8px; + width: 32px; + height: 32px; + color: rgba(0, 0, 0, 0.25); + border: 1px solid rgba(0, 0, 0, 0.25); + border-radius: 50%; + + ${({ $actived, $activedColor }) => ($actived ? `color: #fff; background-color: ${$activedColor};` : '')} + } + + span:last-child { + ${({ $actived }) => + $actived + ? ` + font-size: 24px; + font-weight: 600;` + : ''} + } + + &::before { + content: ''; + position: absolute; + top: 18px; + left: -150px; + width: 100px; + height: 1px; + background-color: rgba(0, 0, 0, 0.25); + } + + &:first-child::before { + display: none; + } +`; + +export const StepContent = styled.div` + display: flex; + height: 450px; + background-color: #fff; + box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px 0px rgba(0, 0, 0, 0.07); + + .content { + flex: 0 0 540px; + padding: 24px; + } + + .qa { + flex: auto; + padding: 24px; + font-size: 14px; + border-left: 1px solid #f0f0f0; + overflow-y: auto; + + img { + width: 100%; + } + + h5 { + margin-top: 16px; + margin-bottom: 16px; + } + + ul { + padding-left: 1em; + list-style: disc; + } + + ol { + padding-left: 1.5em; + } + + li { + line-height: 20px; + } + + p { + color: #6c6c6c; + } + + code { + padding: 2px; + font-size: 12px; + font-family: Menlo; + line-height: 20px; + border-radius: 3px; + border: #f0f0f0; + background: #f5f5f5; + } + } +`; diff --git a/config-ui/src/routes/project/home/index.tsx b/config-ui/src/routes/project/home/index.tsx index 69dc22e56..27474a002 100644 --- a/config-ui/src/routes/project/home/index.tsx +++ b/config-ui/src/routes/project/home/index.tsx @@ -28,14 +28,13 @@ import { getCron, cronPresets, PATHS } from '@/config'; import { ConnectionName } from '@/features'; import { useRefreshData } from '@/hooks'; import { DOC_URL } from '@/release'; +import { OnboardCard } from '@/routes/onboard/card'; import { formatTime, operator } from '@/utils'; import { PipelineStatus } from '@/routes/pipeline'; import { IBlueprint, IBPMode } from '@/types'; import { validName } from '../utils'; -import * as S from './styled'; - export const ProjectHomePage = () => { const [version, setVersion] = useState(1); const [page, setPage] = useState(1); @@ -119,6 +118,7 @@ export const ProjectHomePage = () => { return ( <PageHeader breadcrumbs={[{ name: 'Projects', path: PATHS.PROJECTS() }]}> + <OnboardCard style={{ marginBottom: 32 }} /> <Flex style={{ marginBottom: 16 }} justify="flex-end"> <Button type="primary" icon={<PlusOutlined />} onClick={handleShowDialog}> New Project @@ -218,36 +218,33 @@ export const ProjectHomePage = () => { onOk={handleCreate} onCancel={handleHideDialog} > - <S.DialogWrapper> - <Block - title="Project Name" - description="Give your project a unique name with letters, numbers, -, _ or /" - required - > - <Input - style={{ width: 386 }} - placeholder="Your Project Name" - value={name} - maxLength={100} - onChange={(e) => setName(e.target.value)} - /> - </Block> - <Block - title="Project Settings" - description={ - <> - <ExternalLink link={DOC_URL.DORA}>DORA metrics</ExternalLink> - <span style={{ marginLeft: 4 }}> - are four widely-adopted metrics for measuring software delivery performance. - </span> - </> - } - > - <Checkbox checked={enableDora} onChange={(e) => setEnableDora(e.target.checked)}> - Enable DORA Metrics - </Checkbox> - </Block> - </S.DialogWrapper> + <Block + title="Project Name" + description="Give your project a unique name with letters, numbers, -, _ or /" + required + > + <Input + style={{ width: 386 }} + placeholder="Your Project Name" + value={name} + onChange={(e) => setName(e.target.value)} + /> + </Block> + <Block + title="Project Settings" + description={ + <> + <ExternalLink link={DOC_URL.DORA}>DORA metrics</ExternalLink> + <span style={{ marginLeft: 4 }}> + are four widely-adopted metrics for measuring software delivery performance. + </span> + </> + } + > + <Checkbox checked={enableDora} onChange={(e) => setEnableDora(e.target.checked)}> + Enable DORA Metrics + </Checkbox> + </Block> </Modal> </PageHeader> ); diff --git a/config-ui/vite.config.ts b/config-ui/vite.config.ts index cf3899f0a..bbcb76b4d 100644 --- a/config-ui/vite.config.ts +++ b/config-ui/vite.config.ts @@ -23,7 +23,7 @@ import svgr from 'vite-plugin-svgr'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svgr(), react()], + plugins: [react(), svgr()], envPrefix: 'DEVLAKE_', diff --git a/config-ui/yarn.lock b/config-ui/yarn.lock index 8174fd80d..27acc8d19 100644 --- a/config-ui/yarn.lock +++ b/config-ui/yarn.lock @@ -2511,7 +2511,25 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": +"@types/debug@npm:^4.0.0": + version: 4.1.12 + resolution: "@types/debug@npm:4.1.12" + dependencies: + "@types/ms": "*" + checksum: 47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053 + languageName: node + linkType: hard + +"@types/estree-jsx@npm:^1.0.0": + version: 1.0.5 + resolution: "@types/estree-jsx@npm:1.0.5" + dependencies: + "@types/estree": "*" + checksum: a028ab0cd7b2950168a05c6a86026eb3a36a54a4adfae57f13911d7b49dffe573d9c2b28421b2d029b49b3d02fcd686611be2622dc3dad6d9791166c083f6008 + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0": version: 1.0.5 resolution: "@types/estree@npm:1.0.5" checksum: dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a @@ -2525,6 +2543,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:^3.0.0": + version: 3.0.4 + resolution: "@types/hast@npm:3.0.4" + dependencies: + "@types/unist": "*" + checksum: 7a973e8d16fcdf3936090fa2280f408fb2b6a4f13b42edeb5fbd614efe042b82eac68e298e556d50f6b4ad585a3a93c353e9c826feccdc77af59de8dd400d044 + languageName: node + linkType: hard + "@types/history@npm:^4.7.11": version: 4.7.11 resolution: "@types/history@npm:4.7.11" @@ -2563,6 +2590,22 @@ __metadata: languageName: node linkType: hard +"@types/mdast@npm:^4.0.0": + version: 4.0.3 + resolution: "@types/mdast@npm:4.0.3" + dependencies: + "@types/unist": "*" + checksum: 345c5a22fccf05f35239ea6313ee4aaf6ebed5927c03ac79744abccb69b9ba5e692f9b771e36a012b79e17429082cada30f579e9c43b8a54e0ffb365431498b6 + languageName: node + linkType: hard + +"@types/ms@npm:*": + version: 0.7.34 + resolution: "@types/ms@npm:0.7.34" + checksum: f38d36e7b6edecd9badc9cf50474159e9da5fa6965a75186cceaf883278611b9df6669dc3a3cc122b7938d317b68a9e3d573d316fcb35d1be47ec9e468c6bd8a + languageName: node + linkType: hard + "@types/node@npm:^18.19.20": version: 18.19.20 resolution: "@types/node@npm:18.19.20" @@ -2677,6 +2720,20 @@ __metadata: languageName: node linkType: hard +"@types/unist@npm:*, @types/unist@npm:^3.0.0": + version: 3.0.2 + resolution: "@types/unist@npm:3.0.2" + checksum: 3d04d0be69316e5f14599a0d993a208606c12818cf631fd399243d1dc7a9bd8a3917d6066baa6abc290814afbd744621484756803c80cba892c39cd4b4a85616 + languageName: node + linkType: hard + +"@types/unist@npm:^2.0.0": + version: 2.0.10 + resolution: "@types/unist@npm:2.0.10" + checksum: e2924e18dedf45f68a5c6ccd6015cd62f1643b1b43baac1854efa21ae9e70505db94290434a23da1137d9e31eb58e54ca175982005698ac37300a1c889f6c4aa + languageName: node + linkType: hard + "@types/use-sync-external-store@npm:^0.0.3": version: 0.0.3 resolution: "@types/use-sync-external-store@npm:0.0.3" @@ -2816,7 +2873,7 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.2.0": +"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 @@ -3309,6 +3366,13 @@ __metadata: languageName: node linkType: hard +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: aab4e8ccdc8d762bf3fdfce8e706601695620c0c2eda256dd85088dc0be3cfd7ff126f6e99c2bee1f24f5d418414aacf09d7f9702f16d6963df2fa488cda8824 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -3419,6 +3483,13 @@ __metadata: languageName: node linkType: hard +"ccount@npm:^2.0.0": + version: 2.0.1 + resolution: "ccount@npm:2.0.1" + checksum: 48193dada54c9e260e0acf57fc16171a225305548f9ad20d5471e0f7a8c026aedd8747091dccb0d900cde7df4e4ddbd235df0d8de4a64c71b12f0d3303eeafd4 + languageName: node + linkType: hard + "chalk@npm:5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" @@ -3447,6 +3518,34 @@ __metadata: languageName: node linkType: hard +"character-entities-html4@npm:^2.0.0": + version: 2.1.0 + resolution: "character-entities-html4@npm:2.1.0" + checksum: 7034aa7c7fa90309667f6dd50499c8a760c3d3a6fb159adb4e0bada0107d194551cdbad0714302f62d06ce4ed68565c8c2e15fdef2e8f8764eb63fa92b34b11d + languageName: node + linkType: hard + +"character-entities-legacy@npm:^3.0.0": + version: 3.0.0 + resolution: "character-entities-legacy@npm:3.0.0" + checksum: 7582af055cb488b626d364b7d7a4e46b06abd526fb63c0e4eb35bcb9c9799cc4f76b39f34fdccef2d1174ac95e53e9ab355aae83227c1a2505877893fce77731 + languageName: node + linkType: hard + +"character-entities@npm:^2.0.0": + version: 2.0.2 + resolution: "character-entities@npm:2.0.2" + checksum: cf1643814023697f725e47328fcec17923b8f1799102a8a79c1514e894815651794a2bffd84bb1b3a4b124b050154e4529ed6e81f7c8068a734aecf07a6d3def + languageName: node + linkType: hard + +"character-reference-invalid@npm:^2.0.0": + version: 2.0.1 + resolution: "character-reference-invalid@npm:2.0.1" + checksum: 98d3b1a52ae510b7329e6ee7f6210df14f1e318c5415975d4c9e7ee0ef4c07875d47c6e74230c64551f12f556b4a8ccc24d9f3691a2aa197019e72a95e9297ee + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -3535,6 +3634,13 @@ __metadata: languageName: node linkType: hard +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.3 + resolution: "comma-separated-tokens@npm:2.0.3" + checksum: e3bf9e0332a5c45f49b90e79bcdb4a7a85f28d6a6f0876a94f1bb9b2bfbdbbb9292aac50e1e742d8c0db1e62a0229a106f57917e2d067fca951d81737651700d + languageName: node + linkType: hard + "commander@npm:11.0.0": version: 11.0.0 resolution: "commander@npm:11.0.0" @@ -3588,16 +3694,18 @@ __metadata: husky: ^8.0.0 lint-staged: ^13.1.0 lodash: ^4.17.21 - miller-columns-select: 1.3.2 + miller-columns-select: 1.4.0 prettier: ^2.7.1 react: ^18.2.0 react-copy-to-clipboard: ^5.1.0 react-dom: ^18.2.0 react-is: ^18.2.0 + react-markdown: ^9.0.1 react-redux: ^9.1.0 react-router-dom: ^6.22.2 react-transition-group: ^4.4.5 redux: ^5.0.1 + rehype-raw: ^7.0.0 styled-components: ^6.1.8 typescript: ^5.1.6 vite: ^5.1.4 @@ -3742,7 +3850,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -3763,6 +3871,15 @@ __metadata: languageName: node linkType: hard +"decode-named-character-reference@npm:^1.0.0": + version: 1.0.2 + resolution: "decode-named-character-reference@npm:1.0.2" + dependencies: + character-entities: ^2.0.0 + checksum: f4c71d3b93105f20076052f9cb1523a22a9c796b8296cd35eef1ca54239c78d182c136a848b83ff8da2071e3ae2b1d300bf29d00650a6d6e675438cc31b11d78 + languageName: node + linkType: hard + "decode-uri-component@npm:^0.2.0": version: 0.2.2 resolution: "decode-uri-component@npm:0.2.2" @@ -3806,13 +3923,22 @@ __metadata: languageName: node linkType: hard -"dequal@npm:^2.0.3": +"dequal@npm:^2.0.0, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 languageName: node linkType: hard +"devlop@npm:^1.0.0, devlop@npm:^1.1.0": + version: 1.1.0 + resolution: "devlop@npm:1.1.0" + dependencies: + dequal: ^2.0.0 + checksum: d2ff650bac0bb6ef08c48f3ba98640bb5fec5cce81e9957eb620408d1bab1204d382a45b785c6b3314dc867bb0684936b84c6867820da6db97cbb5d3c15dd185 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -4491,6 +4617,13 @@ __metadata: languageName: node linkType: hard +"estree-util-is-identifier-name@npm:^3.0.0": + version: 3.0.0 + resolution: "estree-util-is-identifier-name@npm:3.0.0" + checksum: ea3909f0188ea164af0aadeca87c087e3e5da78d76da5ae9c7954ff1340ea3e4679c4653bbf4299ffb70caa9b322218cc1128db2541f3d2976eb9704f9857787 + languageName: node + linkType: hard + "estree-walker@npm:^2.0.2": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" @@ -4536,6 +4669,13 @@ __metadata: languageName: node linkType: hard +"extend@npm:^3.0.0": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -4965,6 +5105,112 @@ __metadata: languageName: node linkType: hard +"hast-util-from-parse5@npm:^8.0.0": + version: 8.0.1 + resolution: "hast-util-from-parse5@npm:8.0.1" + dependencies: + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + devlop: ^1.0.0 + hastscript: ^8.0.0 + property-information: ^6.0.0 + vfile: ^6.0.0 + vfile-location: ^5.0.0 + web-namespaces: ^2.0.0 + checksum: fdd1ab8b03af13778ecb94ef9a58b1e3528410cdfceb3d6bb7600508967d0d836b451bc7bc3baf66efb7c730d3d395eea4bb1b30352b0162823d9f0de976774b + languageName: node + linkType: hard + +"hast-util-parse-selector@npm:^4.0.0": + version: 4.0.0 + resolution: "hast-util-parse-selector@npm:4.0.0" + dependencies: + "@types/hast": ^3.0.0 + checksum: 76087670d3b0b50b23a6cb70bca53a6176d6608307ccdbb3ed18b650b82e7c3513bfc40348f1389dc0c5ae872b9a768851f4335f44654abd7deafd6974c52402 + languageName: node + linkType: hard + +"hast-util-raw@npm:^9.0.0": + version: 9.0.2 + resolution: "hast-util-raw@npm:9.0.2" + dependencies: + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + "@ungap/structured-clone": ^1.0.0 + hast-util-from-parse5: ^8.0.0 + hast-util-to-parse5: ^8.0.0 + html-void-elements: ^3.0.0 + mdast-util-to-hast: ^13.0.0 + parse5: ^7.0.0 + unist-util-position: ^5.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 + web-namespaces: ^2.0.0 + zwitch: ^2.0.0 + checksum: 27fd7c723b3b1e06481cd85ca20b447d58d340c53abd2bd61f4a502982109d16aa17b3d71db2ef7c9d24bd627e306ad81cbcaf98c146a3641ba150db731e644c + languageName: node + linkType: hard + +"hast-util-to-jsx-runtime@npm:^2.0.0": + version: 2.3.0 + resolution: "hast-util-to-jsx-runtime@npm:2.3.0" + dependencies: + "@types/estree": ^1.0.0 + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + comma-separated-tokens: ^2.0.0 + devlop: ^1.0.0 + estree-util-is-identifier-name: ^3.0.0 + hast-util-whitespace: ^3.0.0 + mdast-util-mdx-expression: ^2.0.0 + mdast-util-mdx-jsx: ^3.0.0 + mdast-util-mdxjs-esm: ^2.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + style-to-object: ^1.0.0 + unist-util-position: ^5.0.0 + vfile-message: ^4.0.0 + checksum: 599a97c6ec61c1430776813d7fb42e6f96032bf4a04dfcbb8eceef3bc8d1845ecf242387a4426b9d3f52320dbbfa26450643b81124b3d6a0b9bbb0fff4d0ba83 + languageName: node + linkType: hard + +"hast-util-to-parse5@npm:^8.0.0": + version: 8.0.0 + resolution: "hast-util-to-parse5@npm:8.0.0" + dependencies: + "@types/hast": ^3.0.0 + comma-separated-tokens: ^2.0.0 + devlop: ^1.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + web-namespaces: ^2.0.0 + zwitch: ^2.0.0 + checksum: 137469209cb2b32b57387928878dc85310fbd5afa4807a8da69529199bb1d19044bfc95b50c3dc68d4fb2b6cb8bf99b899285597ab6ab318f50422eefd5599dd + languageName: node + linkType: hard + +"hast-util-whitespace@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-whitespace@npm:3.0.0" + dependencies: + "@types/hast": ^3.0.0 + checksum: 41d93ccce218ba935dc3c12acdf586193c35069489c8c8f50c2aa824c00dec94a3c78b03d1db40fa75381942a189161922e4b7bca700b3a2cc779634c351a1e4 + languageName: node + linkType: hard + +"hastscript@npm:^8.0.0": + version: 8.0.0 + resolution: "hastscript@npm:8.0.0" + dependencies: + "@types/hast": ^3.0.0 + comma-separated-tokens: ^2.0.0 + hast-util-parse-selector: ^4.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + checksum: ae3c20223e7b847320c0f98b6fb3c763ebe1bf3913c5805fbc176cf84553a9db1117ca34cf842a5235890b4b9ae0e94501bfdc9a9b870a5dbf5fc52426db1097 + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.3.0": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -4974,6 +5220,20 @@ __metadata: languageName: node linkType: hard +"html-url-attributes@npm:^3.0.0": + version: 3.0.0 + resolution: "html-url-attributes@npm:3.0.0" + checksum: 9f499d33e6ddff6c2d2766fd73d2f22f3c370b4e485a92b0b2938303665b306dc7f36b2724c9466764e8f702351c01f342f5ec933be41a31c1fa40b72087b91d + languageName: node + linkType: hard + +"html-void-elements@npm:^3.0.0": + version: 3.0.0 + resolution: "html-void-elements@npm:3.0.0" + checksum: 59be397525465a7489028afa064c55763d9cccd1d7d9f630cca47137317f0e897a9ca26cef7e745e7cff1abc44260cfa407742b243a54261dfacd42230e94fce + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -5081,6 +5341,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:0.2.2": + version: 0.2.2 + resolution: "inline-style-parser@npm:0.2.2" + checksum: 698893d6542d4e7c0377936a1c7daec34a197765bd77c5599384756a95ce8804e6b79347b783aa591d5e9c6f3d33dae74c6d4cad3a94647eb05f3a785e927a3f + languageName: node + linkType: hard + "internal-slot@npm:^1.0.5, internal-slot@npm:^1.0.7": version: 1.0.7 resolution: "internal-slot@npm:1.0.7" @@ -5109,6 +5376,23 @@ __metadata: languageName: node linkType: hard +"is-alphabetical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphabetical@npm:2.0.1" + checksum: 56207db8d9de0850f0cd30f4966bf731eb82cedfe496cbc2e97e7c3bacaf66fc54a972d2d08c0d93bb679cb84976a05d24c5ad63de56fabbfc60aadae312edaa + languageName: node + linkType: hard + +"is-alphanumerical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphanumerical@npm:2.0.1" + dependencies: + is-alphabetical: ^2.0.0 + is-decimal: ^2.0.0 + checksum: 87acc068008d4c9c4e9f5bd5e251041d42e7a50995c77b1499cf6ed248f971aadeddb11f239cabf09f7975ee58cac7a48ffc170b7890076d8d227b24a68663c9 + languageName: node + linkType: hard + "is-array-buffer@npm:^3.0.4": version: 3.0.4 resolution: "is-array-buffer@npm:3.0.4" @@ -5179,6 +5463,13 @@ __metadata: languageName: node linkType: hard +"is-decimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-decimal@npm:2.0.1" + checksum: 97132de7acdce77caa7b797632970a2ecd649a88e715db0e4dbc00ab0708b5e7574ba5903962c860cd4894a14fd12b100c0c4ac8aed445cf6f55c6cf747a4158 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -5227,6 +5518,13 @@ __metadata: languageName: node linkType: hard +"is-hexadecimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-hexadecimal@npm:2.0.1" + checksum: 66a2ea85994c622858f063f23eda506db29d92b52580709eb6f4c19550552d4dcf3fb81952e52f7cf972097237959e00adc7bb8c9400cd12886e15bf06145321 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -5271,6 +5569,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^4.0.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -5661,6 +5966,13 @@ __metadata: languageName: node linkType: hard +"longest-streak@npm:^3.0.0": + version: 3.1.0 + resolution: "longest-streak@npm:3.1.0" + checksum: d7f952ed004cbdb5c8bcfc4f7f5c3d65449e6c5a9e9be4505a656e3df5a57ee125f284286b4bf8ecea0c21a7b3bf2b8f9001ad506c319b9815ad6a63a47d0fd0 + languageName: node + linkType: hard + "loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -5732,6 +6044,127 @@ __metadata: languageName: node linkType: hard +"mdast-util-from-markdown@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-from-markdown@npm:2.0.0" + dependencies: + "@types/mdast": ^4.0.0 + "@types/unist": ^3.0.0 + decode-named-character-reference: ^1.0.0 + devlop: ^1.0.0 + mdast-util-to-string: ^4.0.0 + micromark: ^4.0.0 + micromark-util-decode-numeric-character-reference: ^2.0.0 + micromark-util-decode-string: ^2.0.0 + micromark-util-normalize-identifier: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + unist-util-stringify-position: ^4.0.0 + checksum: 4e8d8a46b4b588486c41b80c39da333a91593bc8d60cd7421c6cd3c22003b8e5a62478292fb7bc97b9255b6301a2250cca32340ef43c309156e215453c5b92be + languageName: node + linkType: hard + +"mdast-util-mdx-expression@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-mdx-expression@npm:2.0.0" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 4e1183000e183e07a7264e192889b4fd57372806103031c71b9318967f85fd50a5dd0f92ef14f42c331e77410808f5de3341d7bc8ad4ee91b7fa8f0a30043a8a + languageName: node + linkType: hard + +"mdast-util-mdx-jsx@npm:^3.0.0": + version: 3.1.2 + resolution: "mdast-util-mdx-jsx@npm:3.1.2" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + "@types/unist": ^3.0.0 + ccount: ^2.0.0 + devlop: ^1.1.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + parse-entities: ^4.0.0 + stringify-entities: ^4.0.0 + unist-util-remove-position: ^5.0.0 + unist-util-stringify-position: ^4.0.0 + vfile-message: ^4.0.0 + checksum: 33cb8a657702d5bb8d3f658d158f448c45147664cdb2475501a1c467e3a167d75842546296a06f758f07cce4d2a6ba1add405dbdb6caa145a6980c9782e411e2 + languageName: node + linkType: hard + +"mdast-util-mdxjs-esm@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-mdxjs-esm@npm:2.0.1" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 1f9dad04d31d59005332e9157ea9510dc1d03092aadbc607a10475c7eec1c158b475aa0601a3a4f74e13097ca735deb8c2d9d37928ddef25d3029fd7c9e14dc3 + languageName: node + linkType: hard + +"mdast-util-phrasing@npm:^4.0.0": + version: 4.1.0 + resolution: "mdast-util-phrasing@npm:4.1.0" + dependencies: + "@types/mdast": ^4.0.0 + unist-util-is: ^6.0.0 + checksum: 3a97533e8ad104a422f8bebb34b3dde4f17167b8ed3a721cf9263c7416bd3447d2364e6d012a594aada40cac9e949db28a060bb71a982231693609034ed5324e + languageName: node + linkType: hard + +"mdast-util-to-hast@npm:^13.0.0": + version: 13.1.0 + resolution: "mdast-util-to-hast@npm:13.1.0" + dependencies: + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + "@ungap/structured-clone": ^1.0.0 + devlop: ^1.0.0 + micromark-util-sanitize-uri: ^2.0.0 + trim-lines: ^3.0.0 + unist-util-position: ^5.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 + checksum: 640bc897286af8fe760cd477fb04bbf544a5a897cdc2220ce36fe2f892f067b483334610387aeb969511bd78a2d841a54851079cd676ac513d6a5ff75852514e + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^2.0.0": + version: 2.1.0 + resolution: "mdast-util-to-markdown@npm:2.1.0" + dependencies: + "@types/mdast": ^4.0.0 + "@types/unist": ^3.0.0 + longest-streak: ^3.0.0 + mdast-util-phrasing: ^4.0.0 + mdast-util-to-string: ^4.0.0 + micromark-util-decode-string: ^2.0.0 + unist-util-visit: ^5.0.0 + zwitch: ^2.0.0 + checksum: 3a2cf3957e23b34e2e092e6e76ae72ee0b8745955bd811baba6814cf3a3d916c3fd52264b4b58f3bb3d512a428f84a1e998b6fc7e28434e388a9ae8fb6a9c173 + languageName: node + linkType: hard + +"mdast-util-to-string@npm:^4.0.0": + version: 4.0.0 + resolution: "mdast-util-to-string@npm:4.0.0" + dependencies: + "@types/mdast": ^4.0.0 + checksum: 35489fb5710d58cbc2d6c8b6547df161a3f81e0f28f320dfb3548a9393555daf07c310c0c497708e67ed4dfea4a06e5655799e7d631ca91420c288b4525d6c29 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -5746,6 +6179,242 @@ __metadata: languageName: node linkType: hard +"micromark-core-commonmark@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-core-commonmark@npm:2.0.0" + dependencies: + decode-named-character-reference: ^1.0.0 + devlop: ^1.0.0 + micromark-factory-destination: ^2.0.0 + micromark-factory-label: ^2.0.0 + micromark-factory-space: ^2.0.0 + micromark-factory-title: ^2.0.0 + micromark-factory-whitespace: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-chunked: ^2.0.0 + micromark-util-classify-character: ^2.0.0 + micromark-util-html-tag-name: ^2.0.0 + micromark-util-normalize-identifier: ^2.0.0 + micromark-util-resolve-all: ^2.0.0 + micromark-util-subtokenize: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 9c12fb580cf4ce71f60872043bd2794efe129f44d7b2b73afa155bbc0a66b7bc35655ba8cef438a6bd068441837ed3b6dc6ad7e5a18f815462c1750793e03a42 + languageName: node + linkType: hard + +"micromark-factory-destination@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-factory-destination@npm:2.0.0" + dependencies: + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: d36e65ed1c072ff4148b016783148ba7c68a078991154625723e24bda3945160268fb91079fb28618e1613c2b6e70390a8ddc544c45410288aa27b413593071a + languageName: node + linkType: hard + +"micromark-factory-label@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-factory-label@npm:2.0.0" + dependencies: + devlop: ^1.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: c021dbd0ed367610d35f2bae21209bc804d1a6d1286ffce458fd6a717f4d7fe581a7cba7d5c2d7a63757c44eb927c80d6a571d6ea7969fae1b48ab6461d109c4 + languageName: node + linkType: hard + +"micromark-factory-space@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-factory-space@npm:2.0.0" + dependencies: + micromark-util-character: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 4ffdcdc2f759887bbb356500cb460b3915ecddcb5d85c3618d7df68ad05d13ed02b1153ee1845677b7d8126df8f388288b84fcf0d943bd9c92bcc71cd7222e37 + languageName: node + linkType: hard + +"micromark-factory-title@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-factory-title@npm:2.0.0" + dependencies: + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 39e1ac23af3554e6e652e56065579bc7faf21ade7b8704b29c175871b4152b7109b790bb3cae0f7e088381139c6bac9553b8400772c3d322e4fa635f813a3578 + languageName: node + linkType: hard + +"micromark-factory-whitespace@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-factory-whitespace@npm:2.0.0" + dependencies: + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 9587c2546d1a58b4d5472b42adf05463f6212d0449455285662d63cd8eaed89c6b159ac82713fcee5f9dd88628c24307d9533cccd8971a2f3f4d48702f8f850a + languageName: node + linkType: hard + +"micromark-util-character@npm:^2.0.0": + version: 2.1.0 + resolution: "micromark-util-character@npm:2.1.0" + dependencies: + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 36ee910f84077cf16626fa618cfe46ac25956b3242e3166b8e8e98c5a8c524af7e5bf3d70822264b1fd2d297a36104a7eb7e3462c19c28353eaca7b0d8717594 + languageName: node + linkType: hard + +"micromark-util-chunked@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-chunked@npm:2.0.0" + dependencies: + micromark-util-symbol: ^2.0.0 + checksum: 324f95cccdae061332a8241936eaba6ef0782a1e355bac5c607ad2564fd3744929be7dc81651315a2921535747a33243e6a5606bcb64b7a56d49b6d74ea1a3d4 + languageName: node + linkType: hard + +"micromark-util-classify-character@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-classify-character@npm:2.0.0" + dependencies: + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 086e52904deffebb793fb1c08c94aabb8901f76958142dfc3a6282890ebaa983b285e69bd602b9d507f1b758ed38e75a994d2ad9fbbefa7de2584f67a16af405 + languageName: node + linkType: hard + +"micromark-util-combine-extensions@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-combine-extensions@npm:2.0.0" + dependencies: + micromark-util-chunked: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 107c47700343f365b4ed81551e18bc3458b573c500e56ac052b2490bd548adc475216e41d2271633a8867fac66fc22ba3e0a2d74a31ed79b9870ca947eb4e3ba + languageName: node + linkType: hard + +"micromark-util-decode-numeric-character-reference@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.1" + dependencies: + micromark-util-symbol: ^2.0.0 + checksum: 9512507722efd2033a9f08715eeef787fbfe27e23edf55db21423d46d82ab46f76c89b4f960be3f5e50a2d388d89658afc0647989cf256d051e9ea01277a1adb + languageName: node + linkType: hard + +"micromark-util-decode-string@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-decode-string@npm:2.0.0" + dependencies: + decode-named-character-reference: ^1.0.0 + micromark-util-character: ^2.0.0 + micromark-util-decode-numeric-character-reference: ^2.0.0 + micromark-util-symbol: ^2.0.0 + checksum: a75daf32a4a6b549e9f19b4d833ebfeb09a32a9a1f9ce50f35dec6b6a3e4f9f121f49024ba7f9c91c55ebe792f7c7a332fc9604795181b6a612637df0df5b959 + languageName: node + linkType: hard + +"micromark-util-encode@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-encode@npm:2.0.0" + checksum: 853a3f33fce72aaf4ffa60b7f2b6fcfca40b270b3466e1b96561b02185d2bd8c01dd7948bc31a24ac014f4cc854e545ca9a8e9cf7ea46262f9d24c9e88551c66 + languageName: node + linkType: hard + +"micromark-util-html-tag-name@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-html-tag-name@npm:2.0.0" + checksum: d786d4486f93eb0ac5b628779809ca97c5dc60f3c9fc03eb565809831db181cf8cb7f05f9ac76852f3eb35461af0f89fa407b46f3a03f4f97a96754d8dc540d8 + languageName: node + linkType: hard + +"micromark-util-normalize-identifier@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-normalize-identifier@npm:2.0.0" + dependencies: + micromark-util-symbol: ^2.0.0 + checksum: b36da2d3fd102053dadd953ce5c558328df12a63a8ac0e5aad13d4dda8e43b6a5d4a661baafe0a1cd8a260bead4b4a8e6e0e74193dd651e8484225bd4f4e68aa + languageName: node + linkType: hard + +"micromark-util-resolve-all@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-resolve-all@npm:2.0.0" + dependencies: + micromark-util-types: ^2.0.0 + checksum: 31fe703b85572cb3f598ebe32750e59516925c7ff1f66cfe6afaebe0771a395a9eaa770787f2523d3c46082ea80e6c14f83643303740b3d650af7c96ebd30ccc + languageName: node + linkType: hard + +"micromark-util-sanitize-uri@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-sanitize-uri@npm:2.0.0" + dependencies: + micromark-util-character: ^2.0.0 + micromark-util-encode: ^2.0.0 + micromark-util-symbol: ^2.0.0 + checksum: ea4c28bbffcf2430e9aff2d18554296789a8b0a1f54ac24020d1dde76624a7f93e8f2a83e88cd5a846b6d2c4287b71b1142d1b89fa7f1b0363a9b33711a141fe + languageName: node + linkType: hard + +"micromark-util-subtokenize@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-subtokenize@npm:2.0.0" + dependencies: + devlop: ^1.0.0 + micromark-util-chunked: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 77d9c7d59c05a20468d49ce2a3640e9cb268c083ccad02322f26c84e1094c25b44f4b8139ef0a247ca11a4fef7620c5bf82fbffd98acdb2989e79cbe7bd8f1db + languageName: node + linkType: hard + +"micromark-util-symbol@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-symbol@npm:2.0.0" + checksum: fa4a05bff575d9fbf0ad96a1013003e3bb6087ed6b34b609a141b6c0d2137b57df594aca409a95f4c5fda199f227b56a7d8b1f82cea0768df161d8a3a3660764 + languageName: node + linkType: hard + +"micromark-util-types@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-util-types@npm:2.0.0" + checksum: 819fef3ab5770c37893d2a60381fb2694396c8d22803b6e103c830c3a1bc1490363c2b0470bb2acaaddad776dfbc2fc1fcfde39cb63c4f54d95121611672e3d0 + languageName: node + linkType: hard + +"micromark@npm:^4.0.0": + version: 4.0.0 + resolution: "micromark@npm:4.0.0" + dependencies: + "@types/debug": ^4.0.0 + debug: ^4.0.0 + decode-named-character-reference: ^1.0.0 + devlop: ^1.0.0 + micromark-core-commonmark: ^2.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-chunked: ^2.0.0 + micromark-util-combine-extensions: ^2.0.0 + micromark-util-decode-numeric-character-reference: ^2.0.0 + micromark-util-encode: ^2.0.0 + micromark-util-normalize-identifier: ^2.0.0 + micromark-util-resolve-all: ^2.0.0 + micromark-util-sanitize-uri: ^2.0.0 + micromark-util-subtokenize: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: b84ab5ab1a0b28c063c52e9c2c9d7d44b954507235c10c9492d66e0b38f7de24bf298f914a1fbdf109f2a57a88cf0412de217c84cfac5fd60e3e42a74dbac085 + languageName: node + linkType: hard + "micromatch@npm:4.0.5, micromatch@npm:^4.0.4": version: 4.0.5 resolution: "micromatch@npm:4.0.5" @@ -5756,9 +6425,9 @@ __metadata: languageName: node linkType: hard -"miller-columns-select@npm:1.3.2": - version: 1.3.2 - resolution: "miller-columns-select@npm:1.3.2" +"miller-columns-select@npm:1.4.0": + version: 1.4.0 + resolution: "miller-columns-select@npm:1.4.0" dependencies: classnames: ^2.3.2 react-infinite-scroll-component: ^6.1.0 @@ -5766,7 +6435,7 @@ __metadata: peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 - checksum: 57a3e2ae0ac28738f83d94a3ca8691639222d2cd62a3ac286ee97ce7e7161f8417d8254ccaa4be4ee8b4e26cc5688444ba8851820f6a3e852a42a6f946e47de0 + checksum: f8f4c32c546bd5cbaf73dad608b6527c7608417d5b54245fe83ff1004e78c7ed0e8a53a09f913b9cc1b6e05a7c31f9dbdc3cf943b309c9a5c401337fa17c2832 languageName: node linkType: hard @@ -6192,6 +6861,22 @@ __metadata: languageName: node linkType: hard +"parse-entities@npm:^4.0.0": + version: 4.0.1 + resolution: "parse-entities@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + character-entities: ^2.0.0 + character-entities-legacy: ^3.0.0 + character-reference-invalid: ^2.0.0 + decode-named-character-reference: ^1.0.0 + is-alphanumerical: ^2.0.0 + is-decimal: ^2.0.0 + is-hexadecimal: ^2.0.0 + checksum: 32a6ff5b9acb9d2c4d71537308521fd265e685b9215691df73feedd9edfe041bb6da9f89bd0c35c4a2bc7d58e3e76e399bb6078c2fd7d2a343ff1dd46edbf1bd + languageName: node + linkType: hard + "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -6204,6 +6889,15 @@ __metadata: languageName: node linkType: hard +"parse5@npm:^7.0.0": + version: 7.1.2 + resolution: "parse5@npm:7.1.2" + dependencies: + entities: ^4.4.0 + checksum: 59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -6375,6 +7069,13 @@ __metadata: languageName: node linkType: hard +"property-information@npm:^6.0.0": + version: 6.4.1 + resolution: "property-information@npm:6.4.1" + checksum: d9eece5f14b6fea9e6a1fa65fba88554956a58825eb9a5c8327bffee06bcc265117eaeae901871e8e8a5caec8d5e05ce39ab6872d5cef3b49a6f07815b6ef285 + languageName: node + linkType: hard + "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -7000,6 +7701,27 @@ __metadata: languageName: node linkType: hard +"react-markdown@npm:^9.0.1": + version: 9.0.1 + resolution: "react-markdown@npm:9.0.1" + dependencies: + "@types/hast": ^3.0.0 + devlop: ^1.0.0 + hast-util-to-jsx-runtime: ^2.0.0 + html-url-attributes: ^3.0.0 + mdast-util-to-hast: ^13.0.0 + remark-parse: ^11.0.0 + remark-rehype: ^11.0.0 + unified: ^11.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 + peerDependencies: + "@types/react": ">=18" + react: ">=18" + checksum: ca1daa650d48b84a5a9771683cdb3f3d2d418247ce0faf73ede3207c65f2a21cdebb9df37afda67f6fc8f0f0a7b9ce00eb239781954a4d6c7ad88ea4df068add + languageName: node + linkType: hard + "react-redux@npm:^9.1.0": version: 9.1.0 resolution: "react-redux@npm:9.1.0" @@ -7177,6 +7899,42 @@ __metadata: languageName: node linkType: hard +"rehype-raw@npm:^7.0.0": + version: 7.0.0 + resolution: "rehype-raw@npm:7.0.0" + dependencies: + "@types/hast": ^3.0.0 + hast-util-raw: ^9.0.0 + vfile: ^6.0.0 + checksum: f9e28dcbf4c6c7d91a97c10a840310f18ef3268aa45abb3e0428b6b191ff3c4fa8f753b910d768588a2dac5c7da7e557b4ddc3f1b6cd252e8d20cb62d60c65ed + languageName: node + linkType: hard + +"remark-parse@npm:^11.0.0": + version: 11.0.0 + resolution: "remark-parse@npm:11.0.0" + dependencies: + "@types/mdast": ^4.0.0 + mdast-util-from-markdown: ^2.0.0 + micromark-util-types: ^2.0.0 + unified: ^11.0.0 + checksum: d83d245290fa84bb04fb3e78111f09c74f7417e7c012a64dd8dc04fccc3699036d828fbd8eeec8944f774b6c30cc1d925c98f8c46495ebcee7c595496342ab7f + languageName: node + linkType: hard + +"remark-rehype@npm:^11.0.0": + version: 11.1.0 + resolution: "remark-rehype@npm:11.1.0" + dependencies: + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + mdast-util-to-hast: ^13.0.0 + unified: ^11.0.0 + vfile: ^6.0.0 + checksum: f0c731f0ab92a122e7f9c9bcbd10d6a31fdb99f0ea3595d232ddd9f9d11a308c4ec0aff4d56e1d0d256042dfad7df23b9941e50b5038da29786959a5926814e1 + languageName: node + linkType: hard + "reselect@npm:^5.0.1": version: 5.1.0 resolution: "reselect@npm:5.1.0" @@ -7567,6 +8325,13 @@ __metadata: languageName: node linkType: hard +"space-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "space-separated-tokens@npm:2.0.2" + checksum: 202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990 + languageName: node + linkType: hard + "split-on-first@npm:^1.0.0": version: 1.1.0 resolution: "split-on-first@npm:1.1.0" @@ -7690,6 +8455,16 @@ __metadata: languageName: node linkType: hard +"stringify-entities@npm:^4.0.0": + version: 4.0.3 + resolution: "stringify-entities@npm:4.0.3" + dependencies: + character-entities-html4: ^2.0.0 + character-entities-legacy: ^3.0.0 + checksum: 59e8f523b403bf7d415690e72ae52982decd6ea5426bd8b3f5c66225ddde73e766c0c0d91627df082d0794e30b19dd907ffb5864cef3602e4098d6777d7ca3c2 + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -7729,6 +8504,15 @@ __metadata: languageName: node linkType: hard +"style-to-object@npm:^1.0.0": + version: 1.0.5 + resolution: "style-to-object@npm:1.0.5" + dependencies: + inline-style-parser: 0.2.2 + checksum: 6201063204b6a94645f81b189452b2ca3e63d61867ec48523f4d52609c81e96176739fa12020d97fbbf023efb57a6f7ec3a15fb3a7fb7eb3ffea0b52b9dd6b8c + languageName: node + linkType: hard + "styled-components@npm:^5.3.6": version: 5.3.11 resolution: "styled-components@npm:5.3.11" @@ -7875,6 +8659,20 @@ __metadata: languageName: node linkType: hard +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: e241da104682a0e0d807222cc1496b92e716af4db7a002f4aeff33ae6a0024fef93165d49eab11aa07c71e1347c42d46563f91dfaa4d3fb945aa535cdead53ed + languageName: node + linkType: hard + +"trough@npm:^2.0.0": + version: 2.2.0 + resolution: "trough@npm:2.2.0" + checksum: 6097df63169aca1f9b08c263b1b501a9b878387f46e161dde93f6d0bba7febba93c95f876a293c5ea370f6cb03bcb687b2488c8955c3cfb66c2c0161ea8c00f6 + languageName: node + linkType: hard + "tsconfig-paths@npm:^3.15.0": version: 3.15.0 resolution: "tsconfig-paths@npm:3.15.0" @@ -8064,6 +8862,21 @@ __metadata: languageName: node linkType: hard +"unified@npm:^11.0.0": + version: 11.0.4 + resolution: "unified@npm:11.0.4" + dependencies: + "@types/unist": ^3.0.0 + bail: ^2.0.0 + devlop: ^1.0.0 + extend: ^3.0.0 + is-plain-obj: ^4.0.0 + trough: ^2.0.0 + vfile: ^6.0.0 + checksum: cfb023913480ac2bd5e787ffb8c27782c43e6be4a55f8f1c288233fce46a7ebe7718ccc5adb80bf8d56b7ef85f5fc32239c7bfccda006f9f2382e0cc2e2a77e4 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -8082,6 +8895,64 @@ __metadata: languageName: node linkType: hard +"unist-util-is@npm:^6.0.0": + version: 6.0.0 + resolution: "unist-util-is@npm:6.0.0" + dependencies: + "@types/unist": ^3.0.0 + checksum: f630a925126594af9993b091cf807b86811371e465b5049a6283e08537d3e6ba0f7e248e1e7dab52cfe33f9002606acef093441137181b327f6fe504884b20e2 + languageName: node + linkType: hard + +"unist-util-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-position@npm:5.0.0" + dependencies: + "@types/unist": ^3.0.0 + checksum: f89b27989b19f07878de9579cd8db2aa0194c8360db69e2c99bd2124a480d79c08f04b73a64daf01a8fb3af7cba65ff4b45a0b978ca243226084ad5f5d441dde + languageName: node + linkType: hard + +"unist-util-remove-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-remove-position@npm:5.0.0" + dependencies: + "@types/unist": ^3.0.0 + unist-util-visit: ^5.0.0 + checksum: 8aabdb9d0e3e744141bc123d8f87b90835d521209ad3c6c4619d403b324537152f0b8f20dda839b40c3aa0abfbf1828b3635a7a8bb159c3ed469e743023510ee + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-stringify-position@npm:4.0.0" + dependencies: + "@types/unist": ^3.0.0 + checksum: e2e7aee4b92ddb64d314b4ac89eef7a46e4c829cbd3ee4aee516d100772b490eb6b4974f653ba0717a0071ca6ea0770bf22b0a2ea62c65fcba1d071285e96324 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^6.0.0": + version: 6.0.1 + resolution: "unist-util-visit-parents@npm:6.0.1" + dependencies: + "@types/unist": ^3.0.0 + unist-util-is: ^6.0.0 + checksum: 08927647c579f63b91aafcbec9966dc4a7d0af1e5e26fc69f4e3e6a01215084835a2321b06f3cbe7bf7914a852830fc1439f0fc3d7153d8804ac3ef851ddfa20 + languageName: node + linkType: hard + +"unist-util-visit@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-visit@npm:5.0.0" + dependencies: + "@types/unist": ^3.0.0 + unist-util-is: ^6.0.0 + unist-util-visit-parents: ^6.0.0 + checksum: 9ec42e618e7e5d0202f3c191cd30791b51641285732767ee2e6bcd035931032e3c1b29093f4d7fd0c79175bbc1f26f24f26ee49770d32be76f8730a652a857e6 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -8114,6 +8985,37 @@ __metadata: languageName: node linkType: hard +"vfile-location@npm:^5.0.0": + version: 5.0.2 + resolution: "vfile-location@npm:5.0.2" + dependencies: + "@types/unist": ^3.0.0 + vfile: ^6.0.0 + checksum: b61c048cedad3555b4f007f390412c6503f58a6a130b58badf4ee340c87e0d7421e9c86bbc1494c57dedfccadb60f5176cc60ba3098209d99fb3a3d8804e4c38 + languageName: node + linkType: hard + +"vfile-message@npm:^4.0.0": + version: 4.0.2 + resolution: "vfile-message@npm:4.0.2" + dependencies: + "@types/unist": ^3.0.0 + unist-util-stringify-position: ^4.0.0 + checksum: 964e7e119f4c0e0270fc269119c41c96da20afa01acb7c9809a88365c8e0c64aa692fafbd952669382b978002ecd7ad31ef4446d85e8a22cdb62f6df20186c2d + languageName: node + linkType: hard + +"vfile@npm:^6.0.0": + version: 6.0.1 + resolution: "vfile@npm:6.0.1" + dependencies: + "@types/unist": ^3.0.0 + unist-util-stringify-position: ^4.0.0 + vfile-message: ^4.0.0 + checksum: 05ccee73aeb00402bc8a5d0708af299e9f4a33f5132805449099295085e3ca3b0d018328bad9ff44cf2e6f4cd364f1d558d3fb9b394243a25b2739207edcb0ed + languageName: node + linkType: hard + "vite-plugin-svgr@npm:^4.2.0": version: 4.2.0 resolution: "vite-plugin-svgr@npm:4.2.0" @@ -8167,6 +9069,13 @@ __metadata: languageName: node linkType: hard +"web-namespaces@npm:^2.0.0": + version: 2.0.1 + resolution: "web-namespaces@npm:2.0.1" + checksum: b6d9f02f1a43d0ef0848a812d89c83801d5bbad57d8bb61f02eb6d7eb794c3736f6cc2e1191664bb26136594c8218ac609f4069722c6f56d9fc2d808fa9271c6 + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.0.2": version: 1.0.2 resolution: "which-boxed-primitive@npm:1.0.2" @@ -8325,3 +9234,10 @@ __metadata: checksum: 3f33850913a237fb7e59b0b5c71d2cc6bdb1d6b2b01fc24c53ec68b35ad2b95cd49f735532117136495cb57f0ec58f0a7e1f1c55fad685555c7114155170f1ea languageName: node linkType: hard + +"zwitch@npm:^2.0.0": + version: 2.0.4 + resolution: "zwitch@npm:2.0.4" + checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6 + languageName: node + linkType: hard
