admin panel; footer module; select

This commit is contained in:
2021-02-23 01:10:19 +03:00
parent ecf88a8608
commit 6389ee8e55
28 changed files with 413 additions and 118 deletions
+1
View File
@@ -7,6 +7,7 @@
"@fortawesome/free-solid-svg-icons": "5.15.2",
"@fortawesome/react-fontawesome": "0.1.14",
"@sentry/react": "6.2.0",
"@sentry/tracing": "6.2.0",
"bbcode-to-react": "0.2.9",
"eruda": "2.4.1",
"eruda-dom": "2.0.0",
+3 -2
View File
@@ -61,7 +61,7 @@ const TranslatePlatform: { [id: string]: string } = {
win: 'Windows',
mac: 'macOS',
linux: 'Linux',
ios: 'IOS',
ios: 'iOS',
android: 'Andriod'
};
@@ -70,6 +70,7 @@ const TranslateExitStatus: { [id: string]: string } = {
};
export const getVersion = () => get<VersionResponse>('version');
export const version = '1.2';
export const version = '1.2.2';
export const build = 121;
export { get, post, put, del, TranslateStatus, TranslatePlatform, TranslateExitStatus };
+2 -1
View File
@@ -5,10 +5,11 @@ export const fetchNovels = (orderBy?: string) => get<NovelType[]>(orderBy ? `nov
export const createNovel = (novel: NovelType) => post<NovelType>('novels', { ...novel });
export const fetchNovel = (id: number) => get<NovelType>(`novels/${id}`);
export const updateNovel = (id: number, novel: NovelType) => put<NovelType>(`novels/${id}`, { ...novel });
export const updateNovel = (novel: NovelType) => put<NovelType>(`novels/${novel.id}`, novel);
export const fetchNovelCharacters = (id: number) => get<CharacterType[]>(`novels/${id}/characters`);
export const fetchGenres = () => get<GenreType[]>('genres');
export const fetchNovelGenres = (id: number) => get<GenreType[]>(`novels/${id}/genres`);
export const fetchNovelComments = (id: number, offset = 0, count = 5) => get<CommentsResponse>(`novels/${id}/comments?offset=${offset}&count=${count}`);
@@ -55,5 +55,8 @@
.Footer__Bottom
border-top: 1px solid var(--main-color)
display: flex
justify-content: center
flex-direction: column
padding: 2rem 0
text-align: center
text-align: center
+24 -14
View File
@@ -1,36 +1,46 @@
import React from 'react';
import './Footer.sass';
import React, { useMemo, useState } from 'react';
import styles from './Footer.module.sass';
import { Link } from "react-router-dom";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faDiscord, faTelegramPlane, faVk } from '@fortawesome/free-brands-svg-icons';
import { build, getVersion, version } from "../../api/api";
const Footer: React.FC = () => {
const [backend, setVersion] = useState<VersionResponse>({ version: '', build: 0 });
useMemo(() => {
getVersion().then(setVersion);
}, []);
return (
<div className='Footer'>
<footer className='Footer__Tabs'>
<div className="Footer__Tab">
<div className="Footer__Tab_Title">Команда</div>
<div className={styles.Footer}>
<footer className={styles.Footer__Tabs}>
<div className={styles.Footer__Tab}>
<div className={styles.Footer__Tab_Title}>Команда</div>
<Link to={'/user/1'} className={'Link'}>Nix13</Link>
<Link to={'/user/6'} className={'Link'}>RonFall</Link>
</div>
<div className="Footer__Tab">
<div className="Footer__Tab_Title">Соцсети</div>
<div className={styles.Footer__Tab}>
<div className={styles.Footer__Tab_Title}>Соцсети</div>
<div className="Links">
<div className={styles.Links}>
<a href="https://vk.com/unmei_site">
<div className="Link"><FontAwesomeIcon icon={faVk}/></div>
<div className={styles.Link}><FontAwesomeIcon icon={faVk}/></div>
</a>
<a href="https://discord.gg/4CA8Cju">
<div className="Link"><FontAwesomeIcon icon={faDiscord}/></div>
<div className={styles.Link}><FontAwesomeIcon icon={faDiscord}/></div>
</a>
<a href="https://t.me/unmei_site">
<div className="Link"><FontAwesomeIcon icon={faTelegramPlane}/></div>
<div className={styles.Link}><FontAwesomeIcon icon={faTelegramPlane}/></div>
</a>
</div>
</div>
</footer>
<div className="Footer__Bottom">
Unmei © 2019-2021
<div className={styles.Footer__Bottom}>
<div>Unmei © 2019-2021</div>
<div>
Frontend version: <span style={{ color: backend.build == build ? "green" : "red" }}>{version} ({build})</span>;
Backend version: <span style={{ color: backend.build == build ? "green" : "red" }}>{backend.version} ({backend.build})</span>
</div>
</div>
</div>
);
+7 -4
View File
@@ -10,13 +10,15 @@ import APAddPost from "./News/APAddPost";
import APNovels from "./Novels/APNovels";
import APUsers from "./Users/APUsers";
import APModifyUser from "./Users/APModifyUser";
import APModifyNovel from "./Novels/APModifyNovel";
import APMain from "./Main/APMain";
type Props = {
user: UserType
match: { path: string }
}
type State = {}
type State = {};
class AdminPanel extends React.Component<Props, State> {
render() {
@@ -26,8 +28,7 @@ class AdminPanel extends React.Component<Props, State> {
return (
<div className={'AdminPanel'}>
<div className="AdminPanel__Title">Админ панель <span style={{ color: 'var(--error-bg-color)' }}>(в разработке)</span>
</div>
<div className="AdminPanel__Title">Админ панель</div>
<div className="AdminPanel__Router">
<Link to={path}>Главная</Link>
<Link to={`${path}/users`}>Пользователи</Link>
@@ -36,13 +37,15 @@ class AdminPanel extends React.Component<Props, State> {
</div>
<Switch>
<Route exact path={path} component={APMain}/>
<Route exact path={`${path}/news`} component={APNews}/>
<Route exact path={`${path}/news/new`} component={APAddPost}/>
<Route exact path={`${path}/news/:id`} component={APModifyPost}/>
<Route exact path={`${path}/novels`} component={APNovels}/>
<Route exact path={`${path}/novels/new`} component={NotFoundError}/>
<Route exact path={`${path}/novels/:id`} component={NotFoundError}/>
<Route exact path={`${path}/novels/:id`} component={APModifyNovel}/>
<Route exact path={`${path}/users`} component={APUsers}/>
<Route exact path={`${path}/users/new`} component={NotFoundError}/>
+39
View File
@@ -0,0 +1,39 @@
import React from "react";
import Group from "../../../ui/Group/Group";
import { build, getVersion, version } from "../../../api/api";
import Loading from "../../../components/Loading";
type State = {
backend: VersionResponse | null
}
class APMain extends React.Component<any, State> {
state: State = {
backend: null
}
componentDidMount() {
getVersion().then(backend => this.setState({ backend }));
}
render() {
const { backend } = this.state;
if(!backend) return <Loading/>;
return (
<Group title={'Основное'}>
<div>
Текущая версия сайта: <span style={{ color: backend.build == build ? "green" : "red" }}>{version} (билд: {build})</span>
</div>
<div>
Текущая версия API: <span style={{ color: backend.build == build ? "green" : "red" }}>{backend.version} (билд: {backend.build})</span>
</div>
<div>
Совпадение версий сборок: <span style={{ color: backend.build == build ? "green" : "red" }}>{backend.build == build ? 'да' : 'нет'}</span>
</div>
</Group>
);
}
}
export default APMain;
@@ -0,0 +1,142 @@
import React, { ChangeEvent, FormEvent } from "react";
import Group from "../../../ui/Group/Group";
import NotFoundError from "../../../components/NotFoundError";
import './APNovels.sass';
import Input from "../../../ui/Input/Input";
import Button from "../../../ui/Button/Button";
import { connect } from "react-redux";
import { setModal } from "../../../store/ducks/modal";
import { addNotification } from "../../../store/ducks/notifications";
import { fetchGenres, fetchNovel, updateNovel } from "../../../api/novels";
// @ts-ignore
import parser from 'bbcode-to-react';
import TextField from "../../../ui/TextField/TextField";
import Select from "../../../ui/Select/Select";
type Props = {
match: { params: { id: number }, path: string }
history: { push: (path: string) => void }
setPopout: (modal: React.ReactNode | null) => void
addNotification: (notification: React.ReactNode) => void
};
type State = {
novel: NovelType | null
genres: GenreType[]
title: string
};
class APModifyNovel extends React.Component<Props, State> {
state: State = {
novel: null, genres: [],
title: ''
}
componentDidMount() {
const { match } = this.props;
fetchNovel(match.params.id).then(novel => {
novel.release_date = new Date(novel.release_date);
this.setState({ novel, title: novel.original_name });
});
fetchGenres().then(genres => this.setState({ genres }));
}
changeOriginalTitle = (e: ChangeEvent<HTMLInputElement>) => {
const { novel } = this.state;
if(!novel) return;
const newTitle = e.target.value;
document.title = `${newTitle} / Админ-панель`;
novel.original_name = newTitle;
this.setState({ novel });
}
changeLocalizedTitle = (e: ChangeEvent<HTMLInputElement>) => {
const { novel } = this.state;
if(!novel) return;
novel.localized_name = e.target.value;
this.setState({ novel });
}
changeGenres = (e: ChangeEvent<HTMLSelectElement>) => {
const { novel } = this.state;
if(!novel) return;
novel.genres = Array.from(e.target.selectedOptions).map(genre => {
return this.state.genres.filter(g => g.name === genre.value)[0];
});
this.setState({ novel });
}
saveNovel = (e: FormEvent) => {
e.preventDefault();
const { novel } = this.state;
if(!novel) return;
updateNovel(novel).catch(err => {
});
}
render() {
const { novel, genres, title } = this.state;
if(!novel) return <NotFoundError/>;
return (
<Group title={title}>
<form className={'APNovel'} onSubmit={this.saveNovel}>
<Input type="text" className={'APNovel_Input'} placeholder={'Оригинальное название'} value={novel.original_name} onChange={this.changeOriginalTitle}/>
<Input type="text" className={'APNovel_Input'} placeholder={'Название на русском'} value={novel.localized_name} onChange={this.changeLocalizedTitle}/>
<TextField className={'APNovel_TextField'} placeholder={'Описание'} style={{ height: 100 }} value={novel.description} onChange={(e) => {
novel.description = e.target.value;
this.setState({ novel });
}}/>
<label style={{ display: 'flex', alignItems: "center" }}>
<span style={{ marginRight: 8 }}>Жанры:</span>
<Select
multiple
onChange={this.changeGenres}
value={novel.genres.map(genre => genre.name)}
>
{genres.map(genre => (
<option
key={genre.id}
value={genre.name}
>
{genre.localized_name}
</option>
))}
</Select>
</label>
<label>
<span style={{ marginRight: 8 }}>Дата выхода:</span>
<input
type="date"
value={novel.release_date.toISOString().substring(0, 10)}
onChange={e => {
novel.release_date = new Date(e.target.value);
this.setState({ novel });
}}
/>
</label>
<div>
<Button style={{ marginRight: '1rem' }} className={'APNovel_Button'}>Сохранить</Button>
<Button style={{ color: 'var(--error-bg-color)' }} className={'APNovel_Button'} type={"button"}>Удалить</Button>
</div>
</form>
</Group>
);
}
}
export default connect(null,
dispatch => ({
setPopout: (modal: React.ReactNode | null) => dispatch(setModal(modal)),
addNotification: (notification: React.ReactNode) => dispatch(addNotification(notification))
})
)(APModifyNovel);
+10
View File
@@ -0,0 +1,10 @@
.APNovel
display: flex
flex-direction: column
&_Input, &_Button, &_TextField
font-size: 1.2rem
margin: .5rem 0
&_TextField
resize: vertical
+37 -6
View File
@@ -1,16 +1,47 @@
import React from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import Button from "../../../ui/Button/Button";
import Group from "../../../ui/Group/Group";
import { fetchNovels } from "../../../api/novels";
type Props = {};
type Props = {
match: { path: string }
};
type State = {};
type State = {
novels: NovelType[]
};
class APNovels extends React.Component<Props, State> {
render() {
return (
<div>Novels</div>
)
state: State = {
novels: []
}
componentDidMount() {
fetchNovels().then(novels => this.setState({ novels }));
}
render() {
const { match: { path } } = this.props;
const { novels } = this.state;
return (
<Group title={'Новелы'}>
<Link to={`${path}/new`}>
<Button>Добавить новелу</Button>
</Link>
{novels.sort((a, b) => a.id-b.id).map(novel => (
<Link to={`${path}/${novel.id}`} key={novel.id}>
<div style={{ display: "flex" }}>
<div style={{ marginRight: '.5rem', fontWeight: 600 }}>{novel.id}</div>
<div>{novel.original_name}</div>
</div>
</Link>
))}
</Group>
)
};
}
export default connect(
+5 -5
View File
@@ -21,12 +21,12 @@ import AdminPanel from "../AdminPanel/AdminPanel";
import PasswordRestoreGenerate from "../PasswordRestore/PasswordRestoreGenerate";
import PasswordRestore from "../PasswordRestore/PasswordRestore";
import Users from "../Users/Users";
import { getVersion, version } from "../../api/api";
import { build, getVersion, version } from "../../api/api";
import Settings from "../User/Settings/Settings";
import { setUser } from "../../store/ducks/currentUser";
import { setSettings } from "../../store/ducks/userSettings";
import { withProfiler } from "@sentry/react";
import { hasPermission } from "../../utils";
import { hasPermission, isMobile } from "../../utils";
import Club from "../Club/Club";
import Snowfall from "react-snowfall";
// @ts-ignore
@@ -91,7 +91,7 @@ class App extends React.Component<Props, State> {
document.body.setAttribute('theme', settings.theme);
}
});
if(hasPermission(user, 'mobile_debug'))
if(hasPermission(user, 'mobile_debug') && isMobile())
eruda.init();
}).catch((err: ApiError) => {
if(err.code !== 3 && err.text)
@@ -99,8 +99,8 @@ class App extends React.Component<Props, State> {
});
getVersion().then(res => {
if(version !== res.version && !/(dev\..+)/.test(window.location.href)) {
console.debug(`Current back-end version: ${res.version}. Clearing cache`);
if(build != res.build && !/(dev\..+)/.test(window.location.href)) {
console.debug(`Current back-end version: ${res.version} (${res.build}). Clearing cache...`);
caches.keys().then(names => {
names.forEach(name => caches.delete(name).catch(console.error))
+1 -1
View File
@@ -212,7 +212,7 @@ class Novel extends React.Component<Props, State> {
{hasPermission(currentUser, 'novel') && (
<div className="Novel__Main_Status_Mark">
<div className="Novel__Main_Status_Mark_Element">Изменить</div>
<Link to={`/kawaii__neko/novels/${novel.id}`} className="Novel__Main_Status_Mark_Element">Изменить</Link>
<div className="Novel__Main_Status_Mark_Element" onClick={() => setModal(<ConfirmModal onConfirm={() => console.log(123)}/>)}>Удалить</div>
</div>
)}
+12 -4
View File
@@ -62,6 +62,7 @@ class UserNovels extends React.Component<Props, State> {
const planned = novels.filter(n => n.status === 'planned');
const completed = novels.filter(n => n.status === 'completed');
const in_progress = novels.filter(n => n.status === 'in_progress');
return (
<div className='UserNovels'>
@@ -77,8 +78,7 @@ class UserNovels extends React.Component<Props, State> {
<Button onClick={this.getRandomNovel}>Рандомная новелла</Button>
</div>
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
{planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1}
viewType={viewType}/>)}
{planned.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1} viewType={viewType}/>)}
</div>
</div>}
@@ -87,8 +87,16 @@ class UserNovels extends React.Component<Props, State> {
Пройденные
</div>
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
{completed.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1}
viewType={viewType}/>)}
{completed.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1} viewType={viewType}/>)}
</div>
</div>}
{in_progress.length > 0 && <div className={'UserNovels__List'}>
<div className="UserNovels__List_Title">
Прохожу
</div>
<div className={generateClassName("UserNovels__List_Novels", capitalize(viewType) || 'Grid')}>
{in_progress.map((novel, id) => <NovelItem {...novel} key={novel.id} novelId={id + 1} viewType={viewType}/>)}
</div>
</div>}
</div>
+3 -3
View File
@@ -1,12 +1,12 @@
import { createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension";
import { devToolsEnhancer } from "redux-devtools-extension";
import reducers from "./ducks/index";
import * as Sentry from "@sentry/react";
const composeEnhancers = composeWithDevTools({ trace: true });
const composeEnhancers = devToolsEnhancer({ trace: true });
const sentryReduxEnhancer = Sentry.createReduxEnhancer({});
const store = process.env.NODE_ENV === 'development'
? createStore(reducers, composeEnhancers())
? createStore(reducers, composeEnhancers)
: createStore(reducers, sentryReduxEnhancer);
export default store;
+1
View File
@@ -22,6 +22,7 @@ type ApiResponse<T> = {
type VersionResponse = {
version: string
build: number
}
type CommentsResponse = {
+1
View File
@@ -31,6 +31,7 @@ type NovelType = {
duration: number
platforms: string
links: NovelLinkType[]
genres: GenreType[]
}
type CharacterType = {
+1 -1
View File
@@ -8,7 +8,7 @@ const Button = (restProps: ButtonHTMLAttributes<HTMLElement>) => {
<button className={classNames('Button', className)} {...props}>
{children}
</button>
)
);
}
export default Button;
+1 -1
View File
@@ -95,7 +95,7 @@ class NotificationMessage extends React.Component<Props, State> {
{...props}
>
{children}
<div className="Progress" style={{ width: `${time / (5000 || hideTime) * 100}%` }}/>
<div className={'Progress'} style={{ width: `${time / (5000 || hideTime) * 100}%` }}/>
</div>
);
}
+58 -58
View File
@@ -1,58 +1,58 @@
@import '../../Constants'
.NovelItem
background-color: var(--block-color)
padding-bottom: $blockPadding
border-radius: $borderRadius
margin: .5rem
@media (min-width: 601px)
width: $novelBlockSize
@media (max-width: 600px)
width: 100%
display: flex
&.Grid
flex-direction: column
align-items: center
&.Table
width: 100%
padding: .2rem
box-sizing: border-box
flex-direction: row
justify-content: space-between
align-items: center
&:not(:last-child)
margin: 0 0 .5rem
&:last-child
margin: 0
.NovelItem__Status
white-space: nowrap
&__Image
border-radius: $borderRadius $borderRadius 0 0
background-size: cover
background-position: center
width: 100%
height: 130px
&__Title
width: 100%
text-align: center
display: inline-block
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
padding: .2rem
box-sizing: border-box
font-weight: 400
&__Status
font-weight: 300
display: flex
@import '../../Constants'
.NovelItem
background-color: var(--block-color)
padding-bottom: $blockPadding
border-radius: $borderRadius
margin: .5rem
@media (min-width: 601px)
width: $novelBlockSize
@media (max-width: 600px)
width: 100%
display: flex
&.Grid
flex-direction: column
align-items: center
&.Table
width: 100%
padding: .2rem
box-sizing: border-box
flex-direction: row
justify-content: space-between
align-items: center
&:not(:last-child)
margin: 0 0 .5rem
&:last-child
margin: 0
.NovelItem__Status
white-space: nowrap
.NovelItem__Image
border-radius: $borderRadius $borderRadius 0 0
background-size: cover
background-position: center
width: 100%
height: 130px
.NovelItem__Title
width: 100%
text-align: center
display: inline-block
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
padding: .2rem
box-sizing: border-box
font-weight: 400
.NovelItem__Status
font-weight: 300
display: flex
+6 -5
View File
@@ -18,21 +18,22 @@ type Props = {
const NovelItem = ({ id, original_name, localized_name, image, status, mark, viewType, novelId }: Props) => (
<Link to={`/novels/${id}`} className={generateClassName("NovelItem", capitalize(viewType) || 'Grid')}>
{viewType === 'grid' && (<>
<div className='NovelItem__Image'
<div className={'NovelItem__Image'}
style={{ backgroundImage: `url(${image || '/static/img/no-image.png'})` }}/>
<div className="NovelItem__Title">
<div className={'NovelItem__Title'}>
{localized_name || original_name}
</div>
{status && <div className="NovelItem__Status">
{status && <div className={'NovelItem__Status'}>
{TranslateStatus[status]} {mark !== undefined && mark > 0 && mark}
</div>}
</>)}
{viewType === 'table' && (<>
<div className="NovelItem__Component">{novelId || ''}</div>
<div className="NovelItem__Title">
<div className={'NovelItem__Title'}>
{localized_name || original_name}
</div>
{status && <div className="NovelItem__Status">
{status && <div className={'NovelItem__Status'}>
{TranslateStatus[status]} {mark !== undefined && mark > 0 && mark}
</div>}
</>)}
+12
View File
@@ -0,0 +1,12 @@
.Select
overflow: auto
font-size: 1rem
background: var(--main-bg-color)
color: white
border: none
option
background: var(--sec-bg-color)
&:checked
background: var(--nav-bg-color)
+13
View File
@@ -0,0 +1,13 @@
import React from "react";
import './Select.sass';
import classNames from "../../classNames";
type Props = React.DetailedHTMLProps<React.SelectHTMLAttributes<HTMLSelectElement>, HTMLSelectElement>
const Select = ({ children, className, ...props }: Props) => (
<select {...props} className={classNames('Select', className)}>
{children}
</select>
);
export default Select;
+1 -1
View File
@@ -8,6 +8,6 @@ const TabItem = ({ children, selected, ...props }: Props) => (
<div className={selected ? 'TabItem Selected' : 'TabItem'} {...props}>
{children}
</div>
)
);
export default TabItem;
+1 -1
View File
@@ -5,6 +5,6 @@ const Tabs = ({ children }: { children: React.ReactNode[] }) => (
<div className={'Tabs'}>
{children}
</div>
)
);
export default Tabs;
+8 -8
View File
@@ -2,12 +2,12 @@ import React from "react";
import classNames from "../../classNames";
import './TextField.sass';
const TextField = React.forwardRef(
(restProps: React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>, ref: React.Ref<HTMLTextAreaElement>) => {
const { className, ...props } = restProps;
return (
<textarea className={classNames('TextField', className)} ref={ref} {...props} />
)
})
const TextField = (restProps: React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>, ref: React.Ref<HTMLTextAreaElement>) => {
const { className, ...props } = restProps;
export default TextField;
return (
<textarea className={classNames('TextField', className)} ref={ref} {...props} />
);
}
export default React.forwardRef(TextField);
+1 -1
View File
@@ -3,6 +3,6 @@ import './Title.sass';
const Title = ({ children }: any) => (
<div className={'Title'}>{children}</div>
)
);
export default Title;
+8 -1
View File
@@ -30,4 +30,11 @@ const getImage: (path: string) => string = (path: string) => {
return `${cdnUrl}/${path}`;
}
export { capitalize, getRandomInt, hasPermission, hasAccessToAdminPanel, generateClassName, getImage };
const isMobile = () => {
const ua = window.navigator.userAgent;
return ua.indexOf('Android') !== -1 || ua.indexOf('iPhone') !== -1 || ua.indexOf('iPad') !== -1;
}
export { capitalize, getRandomInt, hasPermission, hasAccessToAdminPanel, generateClassName, getImage, isMobile };
+11
View File
@@ -2113,6 +2113,17 @@
hoist-non-react-statics "^3.3.2"
tslib "^1.9.3"
"@sentry/tracing@6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.2.0.tgz#76c17e5dd3f1e61c8a4e3bd090f904a63d674765"
integrity sha512-pzgM1dePPJysVnzaFCMp+BKtjM5q46HZeyShiR+KcQYvneD3fmUPJigDkkcsB2DcrY3mFvDcswjoqxaTIW7ZBQ==
dependencies:
"@sentry/hub" "6.2.0"
"@sentry/minimal" "6.2.0"
"@sentry/types" "6.2.0"
"@sentry/utils" "6.2.0"
tslib "^1.9.3"
"@sentry/types@6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.2.0.tgz#ca020ff42913c6b9f88a9d0c375b5ee3965a2590"