diff --git a/frontend/package.json b/frontend/package.json index 987f4cb..1d53533 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,6 @@ "@types/react": "^17.0.5", "@types/react-dom": "^17.0.4", "@vitejs/plugin-react": "^1.0.9", - "bulma": "^0.9.2", "i18next": "^20.6.1", "pretty-ms": "^7.0.1", "react": "^17.0.2", diff --git a/frontend/src/locale/en/translation.json b/frontend/src/locale/en/translation.json index 1cef37b..0967ef4 100644 --- a/frontend/src/locale/en/translation.json +++ b/frontend/src/locale/en/translation.json @@ -1,260 +1 @@ -{ - "pages": { - "/": "Home", - "/http": "Web server", - "/twitch": "Twitch integration", - "/twitch/settings": "Module configuration", - "/twitch/bot/settings": "Bot configuration", - "/twitch/bot/commands": "Bot commands", - "/twitch/bot/timers": "Bot timers", - "/twitch/bot/alerts": "Chat alerts", - "/loyalty": "Loyalty points", - "/loyalty/settings": "Configuration", - "/loyalty/users": "Viewer points", - "/loyalty/queue": "Redempions", - "/loyalty/rewards": "Rewards", - "/loyalty/goals": "Goals", - "/stulbe": "Back-end integration", - "/stulbe/config": "Back-end configuration", - "/stulbe/webhooks": "Webhooks (for alerts)" - }, - "system": { - "menu-header": "Navigation", - "loading": "Loading…", - "connection-lost": "Connection to server was lost, retrying...", - "pagination": { - "page": "Page {{page}}", - "gotopage": "Go to page {{page}}" - } - }, - "debug": { - "read-key": "Read key", - "read-btn": "Read", - "write-key": "Write key", - "write-btn": "Write", - "fix-json-btn": "Fix JSON", - "console-header": "Console operations", - "get-keys": "Get all keys", - "get-all": "DUMP ALL", - "friendly-greeting": "WELCOME TO HELL" - }, - "actions": { - "save": "Save", - "edit": "Edit", - "delete": "Delete", - "disable": "Disable", - "enable": "Enable", - "create": "Create", - "cancel": "Cancel", - "ok": "OK", - "test": "Test" - }, - "http": { - "header": "Web server configuration", - "server-bind": "HTTP server bind", - "static-content": "Static content", - "enable-static": "Enable static server", - "static-root-path": "Static content root path", - "kv-password": "Kilovolt password" - }, - "backend": { - "config": { - "header": "Back-end integration settings", - "enable": "Enable back-end (stulbe) integration", - "endpoint": "Stulbe Endpoint", - "username": "User name", - "username-placeholder": "myUserName", - "auth-key": "Authorization key", - "auth-key-placeholder": "key goes here", - "authenticated": "Authenticated as", - "auth-success-message": "Connection/Authentication was successful!" - }, - "webhook": { - "header": "Webhook subscriptions", - "err-not-enabled": "Back-end integration must be enabled for this to work", - "loading": "Querying user data from backend…", - "err-no-user": "No twitch user is currently associated (and therefore webhooks are disabled!)", - "current-status": "Current status:", - "auth-message": "Click the following button to authenticate the back-end with your Twitch account:", - "auth-button": "Authenticate with Twitch", - "fake-header": "Simulate events", - "sim-channel.update": "Channel update", - "sim-channel.follow": "New follow", - "sim-channel.subscribe": "New sub", - "sim-channel.subscription.gift": "Gift sub", - "sim-channel.subscription.message": "Re-sub with message", - "sim-channel.cheer": "Cheer", - "sim-channel.raid": "Raid" - } - }, - "loyalty": { - "goals": { - "reached": "Reached!", - "contributors": "Contributors:", - "no-contributors": "No one has contributed yet :(", - "id": "Goal ID", - "id-placeholder": "goal_id_here", - "id-help": "Choose a simple name that can be referenced by other software. It will be auto-generated from the goal name if you leave it blank.", - "err-goalid-dup": "There is already a goal with this ID! Please choose a different one.", - "name": "Name", - "name-placeholder": "My dream goal", - "icon": "Icon", - "icon-placeholder": "Image URL", - "description": "Description", - "description-placeholder": "What's gonna happen when we reach this goal?", - "header": "Community goals", - "new": "New goal", - "search": "Search by name", - "modify": "Modify goal" - }, - "points": "Points", - "points-fallback": "points", - "queue": { - "header": "Redemption queue", - "search": "Search by username", - "reward-name": "Reward name", - "request": "Request", - "accept": "Accept", - "refund": "Refund", - "err-not-available": "Redemption queue is not available (loyalty disabled or no one has redeemed anything yet)" - }, - "rewards": { - "cooldown": "Cooldown", - "required-info": "Required info", - "id": "Reward ID", - "id-placeholder": "reward_id_here", - "err-rewid-dup": "There is already a reward with this ID! Please choose a different one.", - "id-help": "Choose a simple name that can be referenced by other software. It will be auto-generated from the reward name if you leave it blank.", - "name": "Name", - "name-placeholder": "My awesome reward", - "icon": "Icon", - "icon-placeholder": "Image URL", - "description": "Description", - "description-placeholder": "What's so cool about this reward?", - "cost": "Cost", - "requires-extra-info": "Requires viewer-specified details", - "extra-info": "Required info", - "extra-info-placeholder": "What extra detail to ask the viewer for", - "header": "Loyalty rewards", - "new-reward": "New reward", - "search": "Search by name", - "modify-reward": "Modify reward" - }, - "config": { - "header": "Loyalty system configuration", - "enable": "Enable loyalty points", - "err-twitchbot-disabled": "(Twitch bot must be enabled for this!)", - "currency-name": "Currency name", - "bonus-points": "Bonus points for active users", - "points-every": "every", - "give-points": "Give", - "point-reward-frequency": "How often to give {{points}}" - }, - "userlist": { - "give-points": "Give points to user", - "modify-balance": "Modify balance", - "give-button": "Give", - "userlist-header": "All viewers with {{currency}}", - "err-not-available": "Viewer list is not available (loyalty disabled or no one has points)" - } - }, - "form-common": { - "required": "Required", - "date": "Date", - "username": "Username", - "time": { - "seconds": "seconds", - "minutes": "minutes", - "hours": "hours" - }, - "add-new": "Add new" - }, - "twitch": { - "config": { - "header": "Twitch module configuration", - "enable": "Enable twitch integration", - "apiguide-1": "You will need to create an application, here's how:", - "apiguide-2": "Go to <1>https://dev.twitch.tv/console/apps/create", - "apiguide-3": "Use the following data for the required fields:", - "oauth-redir-uri": "OAuth Redirect URLs", - "apiguide-4": "Once created, create a <1>New Secret, then copy both fields below!", - "app-client-id": "App Client ID", - "app-client-secret": "App Client Secret" - }, - "bot": { - "header": "Twitch bot configuration", - "enable": "Enable twitch bot", - "err-module-disabled": "(Twitch integration must be enabled for this!)", - "channel-name": "Twitch channel name", - "username": "Bot username", - "username-expl": "must be a valid Twitch account", - "oauth-token": "Bot OAuth token", - "oauth-help": "You can get this by logging in with the bot account and going here: <1>https://twitchapps.com/tmi/", - "chat-keys": "Enable chat keys (for 3rd party chat integration)", - "chat-history": "Chat history", - "suf-messages": "messages" - }, - "commands": { - "command": "Command", - "response": "Response", - "response-help": "What does the bot reply to this command?", - "description": "Description", - "description-help": "What does this command do?", - "access-level": "Access level", - "access-everyone": "Everyone", - "access-subscribers": "Subscribers", - "access-vips": "VIPs", - "access-moderators": "Moderators", - "access-streamer": "Streamer only", - "access-level-help": "This specifies the minimum level, eg. if you choose VIPs, moderators and streamer can still use the command", - "header": "Bot commands", - "new-command": "New command", - "search": "Search by name", - "modify-command": "Modify command" - }, - "timers": { - "header": "Bot timers", - "new-timer": "New timer", - "modify-timer": "Modify timer", - "search": "Search by timer name", - "messages": "Messages", - "name-hint": "Timer name", - "name": "Name", - "message-help": "What to write in chat", - "minimum-delay": "Interval", - "minimum-delay-help": "How many time must pass between each repeat.", - "minimum-activity": "Minimum chat activity", - "minimum-activity-post": "messages in the last 5 minutes", - "condition-text": "every {{time}}, at least {{messages}} messages in the last 5 minutes", - "err-twitchbot-disabled": "Twitch bot must be enabled in order to use timers!", - "enable": "Enable timers" - }, - "bot-alerts": { - "header": "Chat alerts", - "err-twitchbot-disabled": "Twitch bot must be enabled in order to use chat alerts!", - "err-stulbe-disabled": "Back-end integration must be enabled in order to use chat alerts!", - "messages": "Messages", - "follow": "Follow", - "follow-enabled": "Enable follow notifications", - "subscription": "Subscription", - "sub-enabled": "Enable subscription notifications", - "gift-sub": "Gift Sub", - "gift-sub-enabled": "Enable gift sub notifications", - "cheer": "Cheer", - "cheer-enabled": "Enable cheer notifications", - "raid": "Raid", - "raid-enabled": "Enable raid notifications", - "variation-header": "Variations", - "add-variation": "Add variation", - "variation-condition": "Condition", - "min-months": "Subscription months (at least)", - "delete-variation": "Delete variation" - } - }, - "auth": { - "message": "Strimertül's database is protected by a password, please write it below to access the control panel. If the database has no password (for example, it was recently changed from having one to none), leave the field empty.", - "header": "Authorization needed", - "placeholder": "Password", - "button": "Authenticate" - } -} +{} diff --git a/frontend/src/overrides.css b/frontend/src/overrides.css deleted file mode 100644 index f33e8b8..0000000 --- a/frontend/src/overrides.css +++ /dev/null @@ -1,225 +0,0 @@ -.content { - padding: 1rem; -} - -/* Dark theme fixes */ -body .button.is-static { - background-color: #5d6b6b; -} -body .input, -body .select select, -body .textarea { - background-color: #222727; - color: white; -} - -body .input::placeholder { - color: #7e9292; -} - -body .input[disabled], -body .select select[disabled], -body .textarea[disabled], -body .input[disabled]::placeholder { - border-color: #5e6d6f; - color: #464e4e; - background-color: #6d7979; -} - -body .button.is-success { - background-color: #388859; -} - -.tabContent { - padding: 1rem; - border: 2px solid #5e6d6f; - border-top: 0; - border-radius: 0 0 0.4em 0.4em; -} - -/* Custom padding for content */ -.content-pad { - padding: 1rem 1.5rem; -} - -.copyblock { - padding-bottom: 1rem; -} - -/* Custom reward/goal classes */ -.reward-disabled, -.goal-disabled { - opacity: 0.5; - text-decoration: line-through; -} - -.goal-reached { - font-weight: bold; - color: #1fdb5e; -} -.goal-point-percent { - color: #879799; -} - -.customcommand .card-header-title code { - background-color: transparent; - padding-right: 1em; -} - -.customcommand .content blockquote { - padding: 10px 20px; - margin: 5px; -} - -/* Nice expand/contract icon without FontAwesome! */ -.icon.expand-on, -.icon.expand-off { - transition: all 50ms; - user-select: none; -} - -.icon.expand-on { - transform: rotate(90deg); -} - -.icon.expand-off { - transform: rotate(-90deg) translateY(-2px); -} - -/* Side menu tweaks */ -aside.menu { - padding: 1rem 0.25rem 0; - background-color: #272e2e; - position: fixed; - top: 0; - bottom: 0; - overflow: auto; -} - -div.app-content { - position: fixed; - right: 0; - bottom: 0; - top: 0; - overflow: auto; -} - -p.menu-label { - text-align: center; -} - -@media screen and (max-width: 768px) { - aside.menu { - position: inherit; - overflow: inherit; - flex: 0; - padding-bottom: 1rem; - } - div.app-content { - position: inherit; - flex: 1; - top: inherit; - bottom: inherit; - right: inherit; - width: 100%; - } - .main-content { - flex-direction: column; - } -} - -/* Fullheight fixes */ -html, -body { - height: 100vh; - display: flex; - flex: 1; - margin: 0; - padding: 0; -} - -html { - /* fuck you minireset */ - overflow-y: auto !important; -} - -#main, -section.main-content { - display: flex; - flex: 1; - margin: 0 !important; - padding: 0; -} - -/* Labels should be non-selectable */ -label { - user-select: none; -} - -/* Swap order of "is-active" to work with */ -.tabs.is-boxed li a.is-active { - background-color: #1f2424; -} -.tabs.is-boxed li a.is-active { - border-color: #5e6d6f; - border-bottom-color: transparent !important; -} -.tabs li a.is-active { - border-bottom-color: #1abc9c; - color: #1abc9c; -} - -.field { - padding-bottom: 0.5rem; -} - -.modal-card .field { - padding-bottom: 0; -} - -.subroute { - display: none; -} - -.is-active + .subroute { - display: inherit; -} - -/* Sorting for tables */ -.sort-icon { - margin-left: 0.5rem; -} - -span.sortable { - color: #6bc8b4; - cursor: pointer; - user-select: none; -} - -/* Custom sidebar */ -.sidebar .menu-list a.is-active { - background-color: #5e6d6f; -} - -/* Notifications */ -.notifications { - position: fixed; - bottom: 0; - right: 0; -} - -/* Inline definition lists */ -.inline-dl { - display: grid; - grid-template-columns: min-content 1fr; - padding: 0.5rem; -} -.inline-dl dt { - white-space: nowrap; - font-weight: bold; - text-align: right; - padding-right: 0.5rem; -} -.inline-dl dt:after { - content: ':'; -} diff --git a/frontend/src/ui/App.tsx b/frontend/src/ui/App.tsx index 516617c..84cb383 100644 --- a/frontend/src/ui/App.tsx +++ b/frontend/src/ui/App.tsx @@ -1,215 +1,5 @@ -import { Link, Redirect, Router, useLocation } from '@reach/router'; -import React, { useEffect, useRef, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useTranslation } from 'react-i18next'; +import React from 'react'; -import { RootState } from '../store'; -import { createWSClient } from '../store/api/reducer'; -import Home from './pages/Home'; -import HTTPPage from './pages/HTTP'; -import TwitchPage from './pages/twitch/Main'; -import StulbePage from './pages/stulbe/Main'; -import LoyaltyPage from './pages/loyalty/Main'; -import DebugPage from './pages/Debug'; -import LoyaltySettingPage from './pages/loyalty/Settings'; -import LoyaltyRewardsPage from './pages/loyalty/Rewards'; -import LoyaltyUserListPage from './pages/loyalty/UserList'; -import LoyaltyGoalsPage from './pages/loyalty/Goals'; -import LoyaltyRedeemQueuePage from './pages/loyalty/Queue'; -import TwitchSettingsPage from './pages/twitch/APISettings'; -import TwitchBotSettingsPage from './pages/twitch/BotSettings'; -import TwitchBotCommandsPage from './pages/twitch/Commands'; -import StulbeConfigPage from './pages/stulbe/Config'; -import StulbeWebhooksPage from './pages/stulbe/Webhook'; -import TwitchBotTimersPage from './pages/twitch/Timers'; -import { ConnectionStatus } from '../store/api/types'; -import Field from './components/Field'; -import TwitchBotAlertsPage from './pages/twitch/Alerts'; - -interface RouteItem { - name?: string; - route: string; - subroutes?: RouteItem[]; -} - -const menu: RouteItem[] = [ - { route: '/' }, - { route: '/http' }, - { - route: '/twitch', - subroutes: [ - { route: '/twitch/settings' }, - { route: '/twitch/bot/settings' }, - { route: '/twitch/bot/commands' }, - { route: '/twitch/bot/timers' }, - { route: '/twitch/bot/alerts' }, - ], - }, - { - route: '/loyalty', - subroutes: [ - { route: '/loyalty/settings' }, - { route: '/loyalty/users' }, - { route: '/loyalty/queue' }, - { route: '/loyalty/rewards' }, - { route: '/loyalty/goals' }, - ], - }, - { - route: '/stulbe', - subroutes: [{ route: '/stulbe/config' }, { route: '/stulbe/webhooks' }], - }, -]; - -function AuthModal(): React.ReactElement { - const { t } = useTranslation(); - const [password, setPassword] = useState(''); - const inputRef = useRef(); - - useEffect(() => { - if (inputRef?.current) { - inputRef.current.focus(); - } - }); - - const submit = () => { - localStorage.setItem('password', password); - window.location.reload(); - }; - - return ( -
-
-
-
-

{t('auth.header')}

-
-
- {t('auth.message')} - - setPassword(ev.target.value)} - onKeyUp={(ev) => { - if (ev.key === 'Enter' || ev.code === 'Enter') { - submit(); - } - }} - /> - -
-
- -
-
-
- ); -} - -export default function App(): React.ReactElement { - const loc = useLocation(); - const { t } = useTranslation(); - - const client = useSelector((state: RootState) => state.api.client); - const connected = useSelector( - (state: RootState) => state.api.connectionStatus, - ); - const dispatch = useDispatch(); - - // Create WS client - useEffect(() => { - if (!client) { - dispatch( - createWSClient({ - address: - process.env.NODE_ENV === 'development' - ? 'ws://localhost:4337/ws' - : `ws://${loc.host}/ws`, - password: localStorage.password, - }), - ); - } - }, []); - - if (connected === ConnectionStatus.AuthenticationNeeded) { - return ; - } - - if (!client) { - return
{t('system.loading')}
; - } - - const basepath = '/ui/'; - - const routeItem = ({ route, name, subroutes }: RouteItem) => ( -
  • - { - const active = isCurrent || (subroutes && isPartiallyCurrent); - return { - className: active ? 'is-active' : '', - }; - }} - to={`${basepath}${route}`.replace(/\/\//gi, '/')} - > - {name ?? t(`pages.${route}`)} - - {subroutes ? ( -
      {subroutes.map(routeItem)}
    - ) : null} -
  • - ); - - return ( -
    -
    - {connected !== ConnectionStatus.Connected ? ( -
    - {t('system.connection-lost')} -
    - ) : null} -
    - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -
    -
    - ); +export default function App(): JSX.Element { + return
    ; } diff --git a/frontend/src/ui/components/Field.tsx b/frontend/src/ui/components/Field.tsx deleted file mode 100644 index 869d2e2..0000000 --- a/frontend/src/ui/components/Field.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react'; - -export interface FieldProps { - name?: string; - className?: string; - horizontal?: boolean; -} - -function Field({ - name, - className, - horizontal, - children, -}: React.PropsWithChildren) { - let classes = className ?? ''; - if (horizontal) { - classes += ' is-horizontal'; - } - let nameEl = null; - if (name) { - nameEl = ; - if (horizontal) { - nameEl =
    {nameEl}
    ; - } - } - return ( -
    - {nameEl} - {children} -
    - ); -} - -export default React.memo(Field); diff --git a/frontend/src/ui/components/Interval.tsx b/frontend/src/ui/components/Interval.tsx deleted file mode 100644 index 6a8a578..0000000 --- a/frontend/src/ui/components/Interval.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { getInterval } from '../../lib/time-utils'; - -export interface TimeUnit { - multiplier: number; - unit: string; -} - -export const seconds = { multiplier: 1, unit: 'form-common.time.seconds' }; -export const minutes = { multiplier: 60, unit: 'form-common.time.minutes' }; -export const hours = { multiplier: 3600, unit: 'form-common.time.hours' }; - -export interface IntervalProps { - active: boolean; - value: number; - min?: number; - units?: TimeUnit[]; - onChange?: (value: number) => void; -} - -function Interval({ active, value, min, units, onChange }: IntervalProps) { - const { t } = useTranslation(); - - const timeUnits = units ?? [seconds, minutes, hours]; - - const [numInitialValue, multInitialValue] = getInterval(value); - const [num, setNum] = useState(numInitialValue); - const [mult, setMult] = useState(multInitialValue); - - useEffect(() => { - const total = num * mult; - if (min && total < min) { - const [minNum, minMult] = getInterval(min); - setNum(minNum); - setMult(minMult); - } - onChange(Math.max(min ?? 0, total)); - }, [num, mult]); - - return ( - <> -

    - { - const intNum = parseInt(ev.target.value, 10); - if (Number.isNaN(intNum)) { - return; - } - setNum(intNum); - }} - /> -

    -

    - - - -

    - - ); -} - -export default React.memo(Interval); diff --git a/frontend/src/ui/components/MessageArray.tsx b/frontend/src/ui/components/MessageArray.tsx deleted file mode 100644 index c272c35..0000000 --- a/frontend/src/ui/components/MessageArray.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -export interface MessageArrayProps { - placeholder?: string; - value: string[]; - onChange: (value: string[]) => void; -} - -function MessageArray({ value, placeholder, onChange }: MessageArrayProps) { - const { t } = useTranslation(); - - return ( -
    - {value.map((message, index) => ( -
    0 ? '0.5rem' : '' }} - > -

    - { - const newMessages = [...value]; - newMessages[index] = ev.target.value; - onChange(newMessages); - }} - value={message} - className={message !== '' ? 'input' : 'input is-danger'} - style={{ width: '28rem' }} - /> -

    - {value.length > 1 ? ( -

    - -

    - ) : null} -
    - ))} -
    -

    - -

    -
    -
    - ); -} - -export default React.memo(MessageArray); diff --git a/frontend/src/ui/components/Modal.tsx b/frontend/src/ui/components/Modal.tsx deleted file mode 100644 index d6f7152..0000000 --- a/frontend/src/ui/components/Modal.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -export interface ModalProps { - title: string; - active: boolean; - onClose?: () => void; - onConfirm: () => void; - confirmName?: string; - confirmClass?: string; - confirmEnabled?: boolean; - cancelName?: string; - cancelClass?: string; - showCancel?: boolean; - bgDismiss?: boolean; -} - -function Modal({ - active, - title, - onClose, - onConfirm, - confirmName, - confirmClass, - confirmEnabled, - cancelName, - cancelClass, - showCancel, - bgDismiss, - children, -}: React.PropsWithChildren): React.ReactElement { - const { t } = useTranslation(); - return ( -
    -
    onClose() : null} - /> -
    -
    -

    {title}

    - {showCancel ? ( -
    -
    {children}
    -
    - - {showCancel ? ( - - ) : null} -
    -
    -
    - ); -} - -export default React.memo(Modal); diff --git a/frontend/src/ui/components/PageList.tsx b/frontend/src/ui/components/PageList.tsx deleted file mode 100644 index 4afc6b1..0000000 --- a/frontend/src/ui/components/PageList.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -export interface PageListProps { - current: number; - max: number; - min: number; - itemsPerPage: number; - onSelectChange: (itemsPerPage: number) => void; - onPageChange: (page: number) => void; -} - -function PageList({ - current, - max, - min, - itemsPerPage, - onSelectChange, - onPageChange, -}: PageListProps): React.ReactElement { - const { t } = useTranslation(); - return ( - - ); -} - -export default React.memo(PageList); diff --git a/frontend/src/ui/components/TabbedView.tsx b/frontend/src/ui/components/TabbedView.tsx deleted file mode 100644 index 567b1ed..0000000 --- a/frontend/src/ui/components/TabbedView.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useEffect } from 'react'; - -function TabbedView({ - children, -}: // eslint-disable-next-line @typescript-eslint/ban-types -React.PropsWithChildren<{}>): React.ReactElement { - const [activeTab, setActiveTab] = React.useState(null); - - const tabs = React.Children.map(children, (elem, i) => { - const id = - (typeof elem === 'object' && 'props' in elem - ? elem.props['data-name'] - : null) ?? `TAB#${i}`; - return { - id, - tabContent: elem, - }; - }); - - useEffect(() => { - if (activeTab === null) { - setActiveTab(tabs[0].id); - } - }, [children, activeTab]); - - if (activeTab === null) { - return
    ; - } - - const active = tabs.find((elem) => elem.id === activeTab); - return ( - <> -
    - -
    -
    {active.tabContent}
    - - ); -} - -export default React.memo(TabbedView); diff --git a/frontend/src/ui/pages/Debug.tsx b/frontend/src/ui/pages/Debug.tsx deleted file mode 100644 index 895d49f..0000000 --- a/frontend/src/ui/pages/Debug.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { RouteComponentProps } from '@reach/router'; -import React, { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; -import { RootState } from '../../store'; - -export default function DebugPage( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - params: RouteComponentProps, -): React.ReactElement { - const { t } = useTranslation(); - const api = useSelector((state: RootState) => state.api.client); - const [readKey, setReadKey] = useState(''); - const [readValue, setReadValue] = useState(''); - const [writeKey, setWriteKey] = useState(''); - const [writeValue, setWriteValue] = useState(''); - const [writeErrorMsg, setWriteErrorMsg] = useState(null); - - const performRead = async () => { - const value = await api.getKey(readKey); - setReadValue(value); - }; - const performWrite = async () => { - const result = await api.putKey(writeKey, writeValue); - console.log(result); - }; - const fixJSON = () => { - try { - setWriteValue(JSON.stringify(JSON.parse(writeValue))); - setWriteErrorMsg(null); - } catch (e) { - setWriteErrorMsg(e.message); - } - }; - const dumpKeys = async () => { - console.log(await api.keyList()); - }; - const dumpAll = async () => { - console.log(await api.getKeysByPrefix('')); - }; - - return ( -
    -

    - {t('debug.friendly-greeting')} -

    -
    -
    - - - -
    -
    -
    -
    - -
    -
    - setReadKey(ev.target.value)} - placeholder="some-bucket/some-key" - /> -
    -
    - -
    -
    -
    -
    -