diff --git a/frontend/src/ui/App.tsx b/frontend/src/ui/App.tsx index 4679d42..28a3efb 100644 --- a/frontend/src/ui/App.tsx +++ b/frontend/src/ui/App.tsx @@ -31,20 +31,20 @@ import { initializeServerInfo } from '~/store/server/reducer'; import LogViewer from './components/LogViewer'; import Sidebar, { RouteSection } from './components/Sidebar'; import Scrollbar from './components/utils/Scrollbar'; -import TwitchChatCommandsPage from './pages/ChatCommands'; -import TwitchChatTimersPage from './pages/ChatTimers'; -import ChatAlertsPage from './pages/ChatAlerts'; +import TwitchChatCommandsPage from './pages/twitch/ChatCommands'; +import TwitchChatTimersPage from './pages/twitch/ChatTimers'; +import ChatAlertsPage from './pages/twitch/ChatAlerts'; import Dashboard from './pages/Dashboard'; -import DebugPage from './pages/Debug'; -import LoyaltyConfigPage from './pages/LoyaltyConfig'; -import LoyaltyQueuePage from './pages/LoyaltyQueue'; -import LoyaltyRewardsPage from './pages/LoyaltyRewards'; +import DebugPage from './pages/system/Debug'; +import LoyaltyConfigPage from './pages/loyalty/LoyaltyConfig'; +import LoyaltyQueuePage from './pages/loyalty/LoyaltyQueue'; +import LoyaltyRewardsPage from './pages/loyalty/Rewards/Page'; import OnboardingPage from './pages/Onboarding'; -import ServerSettingsPage from './pages/ServerSettings'; -import StrimertulPage from './pages/Strimertul'; -import TwitchSettingsPage from './pages/TwitchSettings/Page'; -import UISettingsPage from './pages/UISettingsPage'; -import ExtensionsPage from './pages/Extensions'; +import ServerSettingsPage from './pages/system/ServerSettings'; +import StrimertulPage from './pages/system/Strimertul'; +import TwitchSettingsPage from './pages/twitch/TwitchSettings/Page'; +import UISettingsPage from './pages/system/UISettingsPage'; +import ExtensionsPage from './pages/system/Extensions'; import { getTheme, styled } from './theme'; import Loading from './components/Loading'; import InteractiveAuthDialog from './components/InteractiveAuthDialog'; diff --git a/frontend/src/ui/components/utils/Channels.tsx b/frontend/src/ui/components/utils/Channels.tsx index 2b886d5..4a2adc1 100644 --- a/frontend/src/ui/components/utils/Channels.tsx +++ b/frontend/src/ui/components/utils/Channels.tsx @@ -3,7 +3,11 @@ import { DiscordLogoIcon, EnvelopeClosedIcon, } from '@radix-ui/react-icons'; -import { ChannelList, Channel, ChannelLink } from '~/ui/pages/Strimertul'; +import { + ChannelList, + Channel, + ChannelLink, +} from '~/ui/pages/system/Strimertul'; export const Channels = ( diff --git a/frontend/src/ui/pages/LoyaltyConfig.tsx b/frontend/src/ui/pages/loyalty/LoyaltyConfig.tsx similarity index 97% rename from frontend/src/ui/pages/LoyaltyConfig.tsx rename to frontend/src/ui/pages/loyalty/LoyaltyConfig.tsx index 38d1302..a075507 100644 --- a/frontend/src/ui/pages/LoyaltyConfig.tsx +++ b/frontend/src/ui/pages/loyalty/LoyaltyConfig.tsx @@ -16,9 +16,9 @@ import { CheckboxIndicator, InputBox, FieldNote, -} from '../theme'; -import SaveButton from '../components/forms/SaveButton'; -import Interval from '../components/forms/Interval'; +} from '../../theme'; +import SaveButton from '../../components/forms/SaveButton'; +import Interval from '../../components/forms/Interval'; export default function LoyaltySettingsPage(): React.ReactElement { const { t } = useTranslation(); diff --git a/frontend/src/ui/pages/LoyaltyQueue.tsx b/frontend/src/ui/pages/loyalty/LoyaltyQueue.tsx similarity index 98% rename from frontend/src/ui/pages/LoyaltyQueue.tsx rename to frontend/src/ui/pages/loyalty/LoyaltyQueue.tsx index d0d1b13..a2d149e 100644 --- a/frontend/src/ui/pages/LoyaltyQueue.tsx +++ b/frontend/src/ui/pages/loyalty/LoyaltyQueue.tsx @@ -5,8 +5,8 @@ import { SortFunction } from '~/lib/types'; import { useAppDispatch } from '~/store'; import { modules, removeRedeem, setUserPoints } from '~/store/api/reducer'; import { LoyaltyRedeem } from '~/store/api/types'; -import { DataTable } from '../components/DataTable'; -import DialogContent from '../components/DialogContent'; +import { DataTable } from '../../components/DataTable'; +import DialogContent from '../../components/DialogContent'; import { Button, Dialog, @@ -24,8 +24,8 @@ import { TabContent, TabList, TextBlock, -} from '../theme'; -import { TableCell, TableRow } from '../theme/table'; +} from '../../theme'; +import { TableCell, TableRow } from '../../theme/table'; function RewardQueueRow({ data }: { data: LoyaltyRedeem & { date: Date } }) { const dispatch = useAppDispatch(); diff --git a/frontend/src/ui/pages/loyalty/Rewards/GoalsTab.tsx b/frontend/src/ui/pages/loyalty/Rewards/GoalsTab.tsx new file mode 100644 index 0000000..c765e12 --- /dev/null +++ b/frontend/src/ui/pages/loyalty/Rewards/GoalsTab.tsx @@ -0,0 +1,380 @@ +import { PlusIcon } from '@radix-ui/react-icons'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useModule } from '~/lib/react'; +import { useAppDispatch } from '~/store'; +import { modules } from '~/store/api/reducer'; +import { LoyaltyGoal } from '~/store/api/types'; +import AlertContent from '../../../components/AlertContent'; +import DialogContent from '../../../components/DialogContent'; +import { + Button, + ControlledInputBox, + Dialog, + DialogActions, + Field, + FieldNote, + FlexRow, + InputBox, + Label, + MultiButton, + NoneText, + styled, + Textarea, +} from '../../../theme'; +import { Alert, AlertTrigger } from '../../../theme/alert'; +import { + RewardActions, + RewardCost, + RewardDescription, + RewardHeader, + RewardID, + RewardIcon, + RewardItemContainer, + RewardName, +} from './theme'; + +const GoalList = styled('div', { marginTop: '1rem' }); + +interface GoalItemProps { + name: string; + item: LoyaltyGoal; + currency: string; + onToggle?: () => void; + onEdit?: () => void; + onDelete?: () => void; +} +function GoalItem({ + name, + item, + currency, + onToggle, + onEdit, + onDelete, +}: GoalItemProps): React.ReactElement { + const { t } = useTranslation(); + + return ( + + + + {item.image && ( + + )} + + + {item.name} ({name}) + + + {item.contributed} / {item.total} {currency} ( + {Math.round((item.contributed / item.total) * 100)}%) + + + + + + + + + + (onDelete ? onDelete() : null)} + /> + + + + + {item.description} + + ); +} + +export function GoalsTab() { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const [config] = useModule(modules.loyaltyConfig); + const [goals, setGoals] = useModule(modules.loyaltyGoals); + const [filter, setFilter] = useState(''); + const [dialogGoal, setDialogGoal] = useState<{ + open: boolean; + new: boolean; + goal: LoyaltyGoal; + }>({ open: false, new: false, goal: null }); + const filterLC = filter.toLowerCase(); + + const deleteGoal = (id: string): void => { + void dispatch(setGoals(goals?.filter((r) => r.id !== id) ?? [])); + }; + + const toggleGoal = (id: string): void => { + void dispatch( + setGoals( + goals?.map((r) => { + if (r.id === id) { + return { + ...r, + enabled: !r.enabled, + }; + } + return r; + }) ?? [], + ), + ); + }; + + return ( + <> + setDialogGoal({ ...dialogGoal, open: state })} + > + +
{ + e.preventDefault(); + if (!(e.target as HTMLFormElement).checkValidity()) { + return; + } + const { goal } = dialogGoal; + const index = goals?.findIndex((g) => g.id === goal.id); + if (index >= 0) { + const newGoals = goals.slice(0); + newGoals[index] = goal; + void dispatch(setGoals(newGoals)); + } else { + void dispatch(setGoals([...(goals ?? []), goal])); + } + setDialogGoal({ ...dialogGoal, open: false }); + }} + > + + + { + setDialogGoal({ + ...dialogGoal, + goal: { + ...dialogGoal?.goal, + id: + e.target.value + ?.toLowerCase() + .replace(/[^a-z0-9]/gi, '-') ?? '', + }, + }); + if ( + dialogGoal.new && + goals.find((r) => r.id === e.target.value) + ) { + (e.target as HTMLInputElement).setCustomValidity( + t('pages.loyalty-rewards.id-already-in-use'), + ); + } else { + (e.target as HTMLInputElement).setCustomValidity(''); + } + }} + /> + {t('pages.loyalty-rewards.goal-id-hint')} + + + + { + setDialogGoal({ + ...dialogGoal, + goal: { + ...dialogGoal?.goal, + name: e.target.value, + }, + }); + }} + /> + {t('pages.loyalty-rewards.goal-name-hint')} + + + + { + setDialogGoal({ + ...dialogGoal, + goal: { + ...dialogGoal?.goal, + image: e.target.value, + }, + }); + }} + /> + + + + + + + + { + setDialogGoal({ + ...dialogGoal, + goal: { + ...dialogGoal?.goal, + total: parseInt(e.target.value, 10), + }, + }); + }} + /> + + + + + +
+
+
+ + + + setFilter(e.target.value)} + /> + + + + {goals && goals.length > 0 ? ( + goals + ?.filter( + (r) => + r.name.toLowerCase().includes(filterLC) || + r.id.toLowerCase().includes(filterLC) || + r.description.toLowerCase().includes(filterLC), + ) + .map((r) => ( + + setDialogGoal({ + open: true, + new: false, + goal: r, + }) + } + onDelete={() => deleteGoal(r.id)} + onToggle={() => toggleGoal(r.id)} + /> + )) + ) : ( + {t('pages.loyalty-rewards.no-goals')} + )} + + + ); +} diff --git a/frontend/src/ui/pages/loyalty/Rewards/Page.tsx b/frontend/src/ui/pages/loyalty/Rewards/Page.tsx new file mode 100644 index 0000000..a4f255c --- /dev/null +++ b/frontend/src/ui/pages/loyalty/Rewards/Page.tsx @@ -0,0 +1,42 @@ +import { useTranslation } from 'react-i18next'; +import { + PageContainer, + PageHeader, + PageTitle, + TabButton, + TabContainer, + TabContent, + TabList, + TextBlock, +} from '../../../theme'; +import { GoalsTab } from './GoalsTab'; +import { RewardsTab } from './RewardsTab'; + +export default function LoyaltyRewardsPage(): React.ReactElement { + const { t } = useTranslation(); + + return ( + + + {t('pages.loyalty-rewards.title')} + {t('pages.loyalty-rewards.subtitle')} + + + + + {t('pages.loyalty-rewards.rewards-tab')} + + + {t('pages.loyalty-rewards.goals-tab')} + + + + + + + + + + + ); +} diff --git a/frontend/src/ui/pages/LoyaltyRewards.tsx b/frontend/src/ui/pages/loyalty/Rewards/RewardsTab.tsx similarity index 51% rename from frontend/src/ui/pages/LoyaltyRewards.tsx rename to frontend/src/ui/pages/loyalty/Rewards/RewardsTab.tsx index fa69c9e..da33f42 100644 --- a/frontend/src/ui/pages/LoyaltyRewards.tsx +++ b/frontend/src/ui/pages/loyalty/Rewards/RewardsTab.tsx @@ -4,10 +4,10 @@ import { useTranslation } from 'react-i18next'; import { useModule } from '~/lib/react'; import { useAppDispatch } from '~/store'; import { modules } from '~/store/api/reducer'; -import { LoyaltyGoal, LoyaltyReward } from '~/store/api/types'; -import AlertContent from '../components/AlertContent'; -import DialogContent from '../components/DialogContent'; -import Interval from '../components/forms/Interval'; +import { LoyaltyReward } from '~/store/api/types'; +import AlertContent from '../../../components/AlertContent'; +import DialogContent from '../../../components/DialogContent'; +import Interval from '../../../components/forms/Interval'; import { Button, Checkbox, @@ -22,88 +22,22 @@ import { Label, MultiButton, NoneText, - PageContainer, - PageHeader, - PageTitle, styled, - TabButton, - TabContainer, - TabContent, - TabList, Textarea, - TextBlock, -} from '../theme'; -import { Alert, AlertTrigger } from '../theme/alert'; +} from '../../../theme'; +import { Alert, AlertTrigger } from '../../../theme/alert'; +import { + RewardItemContainer, + RewardHeader, + RewardIcon, + RewardName, + RewardID, + RewardCost, + RewardActions, + RewardDescription, +} from './theme'; const RewardList = styled('div', { marginTop: '1rem' }); -const GoalList = styled('div', { marginTop: '1rem' }); -const RewardItemContainer = styled('article', { - backgroundColor: '$gray2', - margin: '0.5rem 0', - padding: '0.5rem', - borderLeft: '5px solid $teal8', - borderRadius: '0.25rem', - borderBottom: '1px solid $gray4', - transition: 'all 50ms', - '&:hover': { - backgroundColor: '$gray3', - }, - variants: { - status: { - enabled: {}, - disabled: { - borderLeftColor: '$red6', - backgroundColor: '$gray3', - color: '$gray10', - }, - }, - }, -}); -const RewardHeader = styled('header', { - display: 'flex', - gap: '0.5rem', - alignItems: 'center', - marginBottom: '0.4rem', -}); -const RewardName = styled('span', { - color: '$gray12', - flex: 1, - fontWeight: 'bold', - variants: { - status: { - enabled: {}, - disabled: { - color: '$gray9', - }, - }, - }, -}); -const RewardDescription = styled('span', { - flex: 1, - fontSize: '0.9rem', - color: '$gray11', -}); -const RewardActions = styled('div', { - display: 'flex', - alignItems: 'center', - gap: '0.25rem', -}); -const RewardID = styled('code', { - fontFamily: 'Space Mono', - color: '$teal11', -}); -const RewardCost = styled('div', { - fontSize: '0.9rem', - marginRight: '0.5rem', -}); -const RewardIcon = styled('div', { - width: '32px', - height: '32px', - backgroundColor: '$gray4', - borderRadius: '0.25rem', - display: 'flex', - alignItems: 'center', -}); interface RewardItemProps { name: string; @@ -184,87 +118,7 @@ function RewardItem({ ); } -interface GoalItemProps { - name: string; - item: LoyaltyGoal; - currency: string; - onToggle?: () => void; - onEdit?: () => void; - onDelete?: () => void; -} -function GoalItem({ - name, - item, - currency, - onToggle, - onEdit, - onDelete, -}: GoalItemProps): React.ReactElement { - const { t } = useTranslation(); - - return ( - - - - {item.image && ( - - )} - - - {item.name} ({name}) - - - {item.contributed} / {item.total} {currency} ( - {Math.round((item.contributed / item.total) * 100)}%) - - - - - - - - - - (onDelete ? onDelete() : null)} - /> - - - - - {item.description} - - ); -} - -function RewardsPage() { +export function RewardsTab() { const { t } = useTranslation(); const dispatch = useAppDispatch(); const [cursorPosition, setCursorPosition] = useState(0); @@ -601,295 +455,3 @@ function RewardsPage() { ); } - -function GoalsPage() { - const { t } = useTranslation(); - const dispatch = useAppDispatch(); - const [config] = useModule(modules.loyaltyConfig); - const [goals, setGoals] = useModule(modules.loyaltyGoals); - const [filter, setFilter] = useState(''); - const [dialogGoal, setDialogGoal] = useState<{ - open: boolean; - new: boolean; - goal: LoyaltyGoal; - }>({ open: false, new: false, goal: null }); - const filterLC = filter.toLowerCase(); - - const deleteGoal = (id: string): void => { - void dispatch(setGoals(goals?.filter((r) => r.id !== id) ?? [])); - }; - - const toggleGoal = (id: string): void => { - void dispatch( - setGoals( - goals?.map((r) => { - if (r.id === id) { - return { - ...r, - enabled: !r.enabled, - }; - } - return r; - }) ?? [], - ), - ); - }; - - return ( - <> - setDialogGoal({ ...dialogGoal, open: state })} - > - -
{ - e.preventDefault(); - if (!(e.target as HTMLFormElement).checkValidity()) { - return; - } - const { goal } = dialogGoal; - const index = goals?.findIndex((g) => g.id === goal.id); - if (index >= 0) { - const newGoals = goals.slice(0); - newGoals[index] = goal; - void dispatch(setGoals(newGoals)); - } else { - void dispatch(setGoals([...(goals ?? []), goal])); - } - setDialogGoal({ ...dialogGoal, open: false }); - }} - > - - - { - setDialogGoal({ - ...dialogGoal, - goal: { - ...dialogGoal?.goal, - id: - e.target.value - ?.toLowerCase() - .replace(/[^a-z0-9]/gi, '-') ?? '', - }, - }); - if ( - dialogGoal.new && - goals.find((r) => r.id === e.target.value) - ) { - (e.target as HTMLInputElement).setCustomValidity( - t('pages.loyalty-rewards.id-already-in-use'), - ); - } else { - (e.target as HTMLInputElement).setCustomValidity(''); - } - }} - /> - {t('pages.loyalty-rewards.goal-id-hint')} - - - - { - setDialogGoal({ - ...dialogGoal, - goal: { - ...dialogGoal?.goal, - name: e.target.value, - }, - }); - }} - /> - {t('pages.loyalty-rewards.goal-name-hint')} - - - - { - setDialogGoal({ - ...dialogGoal, - goal: { - ...dialogGoal?.goal, - image: e.target.value, - }, - }); - }} - /> - - - - - - - - { - setDialogGoal({ - ...dialogGoal, - goal: { - ...dialogGoal?.goal, - total: parseInt(e.target.value, 10), - }, - }); - }} - /> - - - - - -
-
-
- - - - setFilter(e.target.value)} - /> - - - - {goals && goals.length > 0 ? ( - goals - ?.filter( - (r) => - r.name.toLowerCase().includes(filterLC) || - r.id.toLowerCase().includes(filterLC) || - r.description.toLowerCase().includes(filterLC), - ) - .map((r) => ( - - setDialogGoal({ - open: true, - new: false, - goal: r, - }) - } - onDelete={() => deleteGoal(r.id)} - onToggle={() => toggleGoal(r.id)} - /> - )) - ) : ( - {t('pages.loyalty-rewards.no-goals')} - )} - - - ); -} - -export default function LoyaltyRewardsPage(): React.ReactElement { - const { t } = useTranslation(); - - return ( - - - {t('pages.loyalty-rewards.title')} - {t('pages.loyalty-rewards.subtitle')} - - - - - {t('pages.loyalty-rewards.rewards-tab')} - - - {t('pages.loyalty-rewards.goals-tab')} - - - - - - - - - - - ); -} diff --git a/frontend/src/ui/pages/loyalty/Rewards/theme.tsx b/frontend/src/ui/pages/loyalty/Rewards/theme.tsx new file mode 100644 index 0000000..1a20ff5 --- /dev/null +++ b/frontend/src/ui/pages/loyalty/Rewards/theme.tsx @@ -0,0 +1,71 @@ +import { styled } from '~/ui/theme'; + +export const RewardItemContainer = styled('article', { + backgroundColor: '$gray2', + margin: '0.5rem 0', + padding: '0.5rem', + borderLeft: '5px solid $teal8', + borderRadius: '0.25rem', + borderBottom: '1px solid $gray4', + transition: 'all 50ms', + '&:hover': { + backgroundColor: '$gray3', + }, + variants: { + status: { + enabled: {}, + disabled: { + borderLeftColor: '$red6', + backgroundColor: '$gray3', + color: '$gray10', + }, + }, + }, +}); + +export const RewardHeader = styled('header', { + display: 'flex', + gap: '0.5rem', + alignItems: 'center', + marginBottom: '0.4rem', +}); + +export const RewardName = styled('span', { + color: '$gray12', + flex: 1, + fontWeight: 'bold', + variants: { + status: { + enabled: {}, + disabled: { + color: '$gray9', + }, + }, + }, +}); +export const RewardDescription = styled('span', { + flex: 1, + fontSize: '0.9rem', + color: '$gray11', +}); +export const RewardActions = styled('div', { + display: 'flex', + alignItems: 'center', + gap: '0.25rem', +}); +export const RewardID = styled('code', { + fontFamily: 'Space Mono', + color: '$teal11', +}); +export const RewardCost = styled('div', { + fontSize: '0.9rem', + marginRight: '0.5rem', +}); +export const RewardIcon = styled('div', { + width: '32px', + height: '32px', + backgroundColor: '$gray4', + borderRadius: '0.25rem', + display: 'flex', + alignItems: 'center', +}); diff --git a/frontend/src/ui/pages/Debug.tsx b/frontend/src/ui/pages/system/Debug.tsx similarity index 99% rename from frontend/src/ui/pages/Debug.tsx rename to frontend/src/ui/pages/system/Debug.tsx index 1c00016..9bfaaf3 100644 --- a/frontend/src/ui/pages/Debug.tsx +++ b/frontend/src/ui/pages/system/Debug.tsx @@ -14,7 +14,7 @@ import { PageTitle, styled, Textarea, -} from '../theme'; +} from '../../theme'; const Disclaimer = styled('div', { display: 'flex', diff --git a/frontend/src/ui/pages/Extensions.tsx b/frontend/src/ui/pages/system/Extensions.tsx similarity index 98% rename from frontend/src/ui/pages/Extensions.tsx rename to frontend/src/ui/pages/system/Extensions.tsx index d377005..9d868c4 100644 --- a/frontend/src/ui/pages/Extensions.tsx +++ b/frontend/src/ui/pages/system/Extensions.tsx @@ -28,9 +28,9 @@ import extensionsReducer, { } from '~/store/extensions/reducer'; import { useModule } from '~/lib/react'; import { modules } from '~/store/api/reducer'; -import AlertContent from '../components/AlertContent'; -import DialogContent from '../components/DialogContent'; -import Loading from '../components/Loading'; +import AlertContent from '../../components/AlertContent'; +import DialogContent from '../../components/DialogContent'; +import Loading from '../../components/Loading'; import { Button, ComboBox, @@ -51,8 +51,8 @@ import { TabContainer, TabContent, TabList, -} from '../theme'; -import { Alert, AlertTrigger } from '../theme/alert'; +} from '../../theme'; +import { Alert, AlertTrigger } from '../../theme/alert'; const ExtensionRow = styled('article', { marginBottom: '0.4rem', diff --git a/frontend/src/ui/pages/ServerSettings.tsx b/frontend/src/ui/pages/system/ServerSettings.tsx similarity index 94% rename from frontend/src/ui/pages/ServerSettings.tsx rename to frontend/src/ui/pages/system/ServerSettings.tsx index 31337e6..1431354 100644 --- a/frontend/src/ui/pages/ServerSettings.tsx +++ b/frontend/src/ui/pages/system/ServerSettings.tsx @@ -3,9 +3,9 @@ import { useTranslation } from 'react-i18next'; import { useModule, useStatus } from '~/lib/react'; import { useAppDispatch } from '~/store'; import apiReducer, { modules } from '~/store/api/reducer'; -import AlertContent from '../components/AlertContent'; -import RevealLink from '../components/utils/RevealLink'; -import SaveButton from '../components/forms/SaveButton'; +import AlertContent from '../../components/AlertContent'; +import RevealLink from '../../components/utils/RevealLink'; +import SaveButton from '../../components/forms/SaveButton'; import { Field, FieldNote, @@ -15,8 +15,8 @@ import { PageHeader, PageTitle, PasswordInputBox, -} from '../theme'; -import { Alert } from '../theme/alert'; +} from '../../theme'; +import { Alert } from '../../theme/alert'; export default function ServerSettingsPage(): React.ReactElement { const [serverConfig, setServerConfig, loadStatus] = useModule( diff --git a/frontend/src/ui/pages/Strimertul.tsx b/frontend/src/ui/pages/system/Strimertul.tsx similarity index 95% rename from frontend/src/ui/pages/Strimertul.tsx rename to frontend/src/ui/pages/system/Strimertul.tsx index 3cd915c..5f1f27c 100644 --- a/frontend/src/ui/pages/Strimertul.tsx +++ b/frontend/src/ui/pages/system/Strimertul.tsx @@ -6,9 +6,9 @@ import { useNavigate } from 'react-router-dom'; // @ts-expect-error Asset import import logo from '~/assets/icon-logo.svg'; -import { APPNAME, PageContainer, PageHeader, styled } from '../theme'; -import BrowserLink from '../components/BrowserLink'; -import Channels from '../components/utils/Channels'; +import { APPNAME, PageContainer, PageHeader, styled } from '../../theme'; +import BrowserLink from '../../components/BrowserLink'; +import Channels from '../../components/utils/Channels'; const gradientAnimation = keyframes({ '0%': { diff --git a/frontend/src/ui/pages/UISettingsPage.tsx b/frontend/src/ui/pages/system/UISettingsPage.tsx similarity index 97% rename from frontend/src/ui/pages/UISettingsPage.tsx rename to frontend/src/ui/pages/system/UISettingsPage.tsx index 1656bbd..98df468 100644 --- a/frontend/src/ui/pages/UISettingsPage.tsx +++ b/frontend/src/ui/pages/system/UISettingsPage.tsx @@ -4,7 +4,7 @@ import { useModule } from '~/lib/react'; import { languages } from '~/locale/languages'; import { useAppDispatch } from '~/store'; import { modules } from '~/store/api/reducer'; -import RadioGroup from '../components/forms/RadioGroup'; +import RadioGroup from '../../components/forms/RadioGroup'; import { Button, Field, @@ -14,7 +14,7 @@ import { PageTitle, styled, themes, -} from '../theme'; +} from '../../theme'; const PartialWarning = styled('small', { color: '$yellow11', diff --git a/frontend/src/ui/pages/ChatAlerts.tsx b/frontend/src/ui/pages/twitch/ChatAlerts.tsx similarity index 98% rename from frontend/src/ui/pages/ChatAlerts.tsx rename to frontend/src/ui/pages/twitch/ChatAlerts.tsx index fd9c70f..60e09a3 100644 --- a/frontend/src/ui/pages/ChatAlerts.tsx +++ b/frontend/src/ui/pages/twitch/ChatAlerts.tsx @@ -4,7 +4,7 @@ import { CheckIcon } from '@radix-ui/react-icons'; import { useModule, useStatus } from '~/lib/react'; import apiReducer, { modules } from '~/store/api/reducer'; import { useAppDispatch } from '~/store'; -import MultiInput from '../components/forms/MultiInput'; +import MultiInput from '../../components/forms/MultiInput'; import { Checkbox, CheckboxIndicator, @@ -19,8 +19,8 @@ import { TabContent, TabList, TextBlock, -} from '../theme'; -import SaveButton from '../components/forms/SaveButton'; +} from '../../theme'; +import SaveButton from '../../components/forms/SaveButton'; export default function ChatAlertsPage(): React.ReactElement { const { t } = useTranslation(); diff --git a/frontend/src/ui/pages/ChatCommands.tsx b/frontend/src/ui/pages/twitch/ChatCommands.tsx similarity index 98% rename from frontend/src/ui/pages/ChatCommands.tsx rename to frontend/src/ui/pages/twitch/ChatCommands.tsx index e99a23a..4a8d02b 100644 --- a/frontend/src/ui/pages/ChatCommands.tsx +++ b/frontend/src/ui/pages/twitch/ChatCommands.tsx @@ -11,8 +11,8 @@ import { TwitchChatCustomCommand, } from '~/store/api/types'; import { TestCommandTemplate } from '@wailsapp/go/main/App'; -import AlertContent from '../components/AlertContent'; -import DialogContent from '../components/DialogContent'; +import AlertContent from '../../components/AlertContent'; +import DialogContent from '../../components/DialogContent'; import { Button, ComboBox, @@ -34,8 +34,8 @@ import { styled, Textarea, TextBlock, -} from '../theme'; -import { Alert, AlertTrigger } from '../theme/alert'; +} from '../../theme'; +import { Alert, AlertTrigger } from '../../theme/alert'; const CommandList = styled('div', { marginTop: '1rem' }); const CommandItemContainer = styled('article', { diff --git a/frontend/src/ui/pages/ChatTimers.tsx b/frontend/src/ui/pages/twitch/ChatTimers.tsx similarity index 96% rename from frontend/src/ui/pages/ChatTimers.tsx rename to frontend/src/ui/pages/twitch/ChatTimers.tsx index 0cfb885..50349e4 100644 --- a/frontend/src/ui/pages/ChatTimers.tsx +++ b/frontend/src/ui/pages/twitch/ChatTimers.tsx @@ -6,11 +6,11 @@ import { useModule } from '~/lib/react'; import { useAppDispatch } from '~/store'; import { modules } from '~/store/api/reducer'; import { TwitchChatTimer } from '~/store/api/types'; -import AlertContent from '../components/AlertContent'; -import DialogContent from '../components/DialogContent'; -import Interval from '../components/forms/Interval'; -import { hours, minutes } from '../components/forms/units'; -import MultiInput from '../components/forms/MultiInput'; +import AlertContent from '../../components/AlertContent'; +import DialogContent from '../../components/DialogContent'; +import Interval from '../../components/forms/Interval'; +import { hours, minutes } from '../../components/forms/units'; +import MultiInput from '../../components/forms/MultiInput'; import { Button, Dialog, @@ -27,8 +27,8 @@ import { PageTitle, styled, TextBlock, -} from '../theme'; -import { Alert, AlertTrigger } from '../theme/alert'; +} from '../../theme'; +import { Alert, AlertTrigger } from '../../theme/alert'; const TimerList = styled('div', { marginTop: '1rem' }); const TimerItemContainer = styled('article', { diff --git a/frontend/src/ui/pages/TwitchSettings/Page.tsx b/frontend/src/ui/pages/twitch/TwitchSettings/Page.tsx similarity index 99% rename from frontend/src/ui/pages/TwitchSettings/Page.tsx rename to frontend/src/ui/pages/twitch/TwitchSettings/Page.tsx index cac5e38..e531f94 100644 --- a/frontend/src/ui/pages/TwitchSettings/Page.tsx +++ b/frontend/src/ui/pages/twitch/TwitchSettings/Page.tsx @@ -18,7 +18,7 @@ import { TabContent, TabList, TextBlock, -} from '../../theme'; +} from '../../../theme'; import TwitchAPISettings from './TwitchAPISettings'; import TwitchEventSubSettings from './TwitchEventSubSettings'; import TwitchChatSettings from './TwitchChatSettings'; diff --git a/frontend/src/ui/pages/TwitchSettings/TwitchAPISettings.tsx b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchAPISettings.tsx similarity index 93% rename from frontend/src/ui/pages/TwitchSettings/TwitchAPISettings.tsx rename to frontend/src/ui/pages/twitch/TwitchSettings/TwitchAPISettings.tsx index ce71e58..3a30f8b 100644 --- a/frontend/src/ui/pages/TwitchSettings/TwitchAPISettings.tsx +++ b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchAPISettings.tsx @@ -4,10 +4,10 @@ import { useModule, useStatus } from '~/lib/react'; import { useAppDispatch } from '~/store'; import apiReducer, { modules } from '~/store/api/reducer'; import { checkTwitchKeys } from '~/lib/twitch'; -import BrowserLink from '../../components/BrowserLink'; -import DefinitionTable from '../../components/DefinitionTable'; -import RevealLink from '../../components/utils/RevealLink'; -import SaveButton from '../../components/forms/SaveButton'; +import BrowserLink from '../../../components/BrowserLink'; +import DefinitionTable from '../../../components/DefinitionTable'; +import RevealLink from '../../../components/utils/RevealLink'; +import SaveButton from '../../../components/forms/SaveButton'; import { Button, ButtonGroup, @@ -18,9 +18,9 @@ import { SectionHeader, styled, TextBlock, -} from '../../theme'; -import AlertContent from '../../components/AlertContent'; -import { Alert } from '../../theme/alert'; +} from '../../../theme'; +import AlertContent from '../../../components/AlertContent'; +import { Alert } from '../../../theme/alert'; const StepList = styled('ul', { lineHeight: '1.5', diff --git a/frontend/src/ui/pages/TwitchSettings/TwitchChatSettings.tsx b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchChatSettings.tsx similarity index 97% rename from frontend/src/ui/pages/TwitchSettings/TwitchChatSettings.tsx rename to frontend/src/ui/pages/twitch/TwitchSettings/TwitchChatSettings.tsx index b14b91c..ffb14e5 100644 --- a/frontend/src/ui/pages/TwitchSettings/TwitchChatSettings.tsx +++ b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchChatSettings.tsx @@ -5,7 +5,7 @@ import apiReducer, { modules } from '~/store/api/reducer'; import { startAuthFlow } from '~/lib/twitch'; import TwitchUserBlock from '~/ui/components/TwitchUserBlock'; import { ExternalLinkIcon } from '@radix-ui/react-icons'; -import SaveButton from '../../components/forms/SaveButton'; +import SaveButton from '../../../components/forms/SaveButton'; import { Button, Field, @@ -14,7 +14,7 @@ import { Label, SectionHeader, TextBlock, -} from '../../theme'; +} from '../../../theme'; export default function TwitchChatSettings() { const [chatConfig, setChatConfig, loadStatus] = useModule( diff --git a/frontend/src/ui/pages/TwitchSettings/TwitchEventSubSettings.tsx b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchEventSubSettings.tsx similarity index 95% rename from frontend/src/ui/pages/TwitchSettings/TwitchEventSubSettings.tsx rename to frontend/src/ui/pages/twitch/TwitchSettings/TwitchEventSubSettings.tsx index 8dcb74d..8b680b3 100644 --- a/frontend/src/ui/pages/TwitchSettings/TwitchEventSubSettings.tsx +++ b/frontend/src/ui/pages/twitch/TwitchSettings/TwitchEventSubSettings.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'; import eventsubTests from '~/data/eventsub-tests'; import { useAppSelector } from '~/store'; import { startAuthFlow } from '~/lib/twitch'; -import { Button, ButtonGroup, SectionHeader, TextBlock } from '../../theme'; -import TwitchUserBlock from '../../components/TwitchUserBlock'; +import { Button, ButtonGroup, SectionHeader, TextBlock } from '../../../theme'; +import TwitchUserBlock from '../../../components/TwitchUserBlock'; export default function TwitchEventSubSettings() { const { t } = useTranslation();