mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-20 02:00:49 +00:00
More missing translation, invalid styling and some progress on rewards
This commit is contained in:
parent
2358c1cf25
commit
4d7744b4db
7 changed files with 237 additions and 26 deletions
|
@ -115,8 +115,6 @@
|
||||||
"command-response-placeholder": "Hello {0}!",
|
"command-response-placeholder": "Hello {0}!",
|
||||||
"command-acl": "Access level",
|
"command-acl": "Access level",
|
||||||
"command-acl-help": "This specifies the minimum level, eg. if you choose VIPs, moderators and streamer can still use the command",
|
"command-acl-help": "This specifies the minimum level, eg. if you choose VIPs, moderators and streamer can still use the command",
|
||||||
"command-action-new": "Create",
|
|
||||||
"command-action-edit": "Edit",
|
|
||||||
"acl": {
|
"acl": {
|
||||||
"everyone": "Everyone",
|
"everyone": "Everyone",
|
||||||
"subscribers": "Subscribers",
|
"subscribers": "Subscribers",
|
||||||
|
@ -135,8 +133,6 @@
|
||||||
"timer-header-edit": "Edit timer",
|
"timer-header-edit": "Edit timer",
|
||||||
"timer-name": "Timer name",
|
"timer-name": "Timer name",
|
||||||
"timer-name-placeholder": "my-timer",
|
"timer-name-placeholder": "my-timer",
|
||||||
"timer-action-new": "Create",
|
|
||||||
"timer-action-edit": "Edit",
|
|
||||||
"remove-timer-title": "Remove timer {{name}}?",
|
"remove-timer-title": "Remove timer {{name}}?",
|
||||||
"timer-parameters": "every {{time}}, ≥ {{messages}} messages in the last {{interval}}",
|
"timer-parameters": "every {{time}}, ≥ {{messages}} messages in the last {{interval}}",
|
||||||
"timer-interval": "Minimul interval",
|
"timer-interval": "Minimul interval",
|
||||||
|
@ -160,7 +156,14 @@
|
||||||
"subscription-enable": "Enable subscription message",
|
"subscription-enable": "Enable subscription message",
|
||||||
"gift_sub-enable": "Enable gifted subscription message",
|
"gift_sub-enable": "Enable gifted subscription message",
|
||||||
"raid-enable": "Enable raid message",
|
"raid-enable": "Enable raid message",
|
||||||
"cheer-enable": "Enable cheering message"
|
"cheer-enable": "Enable cheering message",
|
||||||
|
"events": {
|
||||||
|
"follow": "New follow",
|
||||||
|
"subscription": "Subscription",
|
||||||
|
"gift-sub": "Gift sub",
|
||||||
|
"raid": "Raid",
|
||||||
|
"cheer": "Cheer"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"loyalty-settings": {
|
"loyalty-settings": {
|
||||||
"title": "Loyalty system configuration",
|
"title": "Loyalty system configuration",
|
||||||
|
@ -189,7 +192,9 @@
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"request": "Request",
|
"request": "Request",
|
||||||
"no-redeems": "No pending redeems",
|
"no-redeems": "No pending redeems",
|
||||||
"no-users": "No viewers found"
|
"no-users": "No viewers found",
|
||||||
|
"refund": "Refund",
|
||||||
|
"accept": "Accept"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"dismiss-warning": "I am not afraid! ...well ok maybe a little",
|
"dismiss-warning": "I am not afraid! ...well ok maybe a little",
|
||||||
|
@ -197,7 +202,15 @@
|
||||||
"disclaimer-header": "Big scary disclaimer"
|
"disclaimer-header": "Big scary disclaimer"
|
||||||
},
|
},
|
||||||
"loyalty-rewards": {
|
"loyalty-rewards": {
|
||||||
"title": "Rewards and goals"
|
"title": "Rewards and goals",
|
||||||
|
"rewards-tab": "Rewards",
|
||||||
|
"goals-tab": "Goals",
|
||||||
|
"subtitle": "Set up rewards and community goals for your viewers to play with",
|
||||||
|
"reward-filter": "Search by reward name",
|
||||||
|
"create-reward": "Create reward",
|
||||||
|
"reward-id": "Reward ID",
|
||||||
|
"id-already-in-use": "ID already in use by another reward",
|
||||||
|
"reward-name": "Reward name"
|
||||||
},
|
},
|
||||||
"strimertul": {
|
"strimertul": {
|
||||||
"need-help": "Need help?",
|
"need-help": "Need help?",
|
||||||
|
@ -218,7 +231,8 @@
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"warning-delete": "This cannot be undone"
|
"warning-delete": "This cannot be undone",
|
||||||
|
"create": "Create"
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"dev-build": "Development build"
|
"dev-build": "Development build"
|
||||||
|
|
|
@ -263,7 +263,7 @@ function CommandDialog({
|
||||||
</Field>
|
</Field>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
{t(`pages.botcommands.command-action-${kind}`)}
|
{kind === 'new' ? t('form-actions.create') : t('form-actions.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button type="button">{t('form-actions.cancel')}</Button>
|
<Button type="button">{t('form-actions.cancel')}</Button>
|
||||||
|
|
|
@ -277,7 +277,7 @@ function TimerDialog({
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variation="primary">
|
<Button variation="primary">
|
||||||
{t(`pages.bottimers.timer-action-${kind}`)}
|
{kind === 'new' ? t('form-actions.create') : t('form-actions.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
<DialogClose asChild>
|
<DialogClose asChild>
|
||||||
<Button type="button">{t('form-actions.cancel')}</Button>
|
<Button type="button">{t('form-actions.cancel')}</Button>
|
||||||
|
|
|
@ -42,11 +42,19 @@ export default function ChatAlertsPage(): React.ReactElement {
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<TabContainer defaultValue="follow">
|
<TabContainer defaultValue="follow">
|
||||||
<TabList>
|
<TabList>
|
||||||
<TabButton value="follow">New follow</TabButton>
|
<TabButton value="follow">
|
||||||
<TabButton value="sub">Subscription</TabButton>
|
{t('pages.alerts.events.follow')}
|
||||||
<TabButton value="gift">Gift sub</TabButton>
|
</TabButton>
|
||||||
<TabButton value="raid">Raid</TabButton>
|
<TabButton value="sub">
|
||||||
<TabButton value="cheer">Cheer</TabButton>
|
{t('pages.alerts.events.subscription')}
|
||||||
|
</TabButton>
|
||||||
|
<TabButton value="gift">
|
||||||
|
{t('pages.alerts.events.gift-sub')}
|
||||||
|
</TabButton>
|
||||||
|
<TabButton value="raid">{t('pages.alerts.events.raid')}</TabButton>
|
||||||
|
<TabButton value="cheer">
|
||||||
|
{t('pages.alerts.events.cheer')}
|
||||||
|
</TabButton>
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabContent value="follow">
|
<TabContent value="follow">
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth">
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { useDispatch } from 'react-redux';
|
||||||
import { useModule, useUserPoints } from '../../lib/react-utils';
|
import { useModule, useUserPoints } from '../../lib/react-utils';
|
||||||
import { SortFunction } from '../../lib/type-utils';
|
import { SortFunction } from '../../lib/type-utils';
|
||||||
import { modules, removeRedeem, setUserPoints } from '../../store/api/reducer';
|
import { modules, removeRedeem, setUserPoints } from '../../store/api/reducer';
|
||||||
import { LoyaltyRedeem } from '../../store/api/types';
|
|
||||||
import { DataTable } from '../components/DataTable';
|
import { DataTable } from '../components/DataTable';
|
||||||
import DialogContent from '../components/DialogContent';
|
import DialogContent from '../components/DialogContent';
|
||||||
import {
|
import {
|
||||||
|
@ -112,7 +111,7 @@ function RewardQueue() {
|
||||||
dispatch(removeRedeem(entry));
|
dispatch(removeRedeem(entry));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Accept
|
{t('pages.loyalty-queue.accept')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -129,7 +128,7 @@ function RewardQueue() {
|
||||||
dispatch(removeRedeem(entry));
|
dispatch(removeRedeem(entry));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Refund
|
{t('pages.loyalty-queue.refund')}
|
||||||
</Button>
|
</Button>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
@ -196,7 +195,7 @@ function UserList() {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth" spacing="narrow">
|
||||||
<Label htmlFor="d-username">
|
<Label htmlFor="d-username">
|
||||||
{t('pages.loyalty-queue.username')}
|
{t('pages.loyalty-queue.username')}
|
||||||
</Label>
|
</Label>
|
||||||
|
@ -212,7 +211,7 @@ function UserList() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth" spacing="narrow">
|
||||||
<Label htmlFor="d-points" css={{ textTransform: 'capitalize' }}>
|
<Label htmlFor="d-points" css={{ textTransform: 'capitalize' }}>
|
||||||
{config?.currency || t('pages.loyalty-queue.points')}
|
{config?.currency || t('pages.loyalty-queue.points')}
|
||||||
</Label>
|
</Label>
|
||||||
|
@ -259,7 +258,7 @@ function UserList() {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth" spacing="narrow">
|
||||||
<Label htmlFor="d-username">
|
<Label htmlFor="d-username">
|
||||||
{t('pages.loyalty-queue.username')}
|
{t('pages.loyalty-queue.username')}
|
||||||
</Label>
|
</Label>
|
||||||
|
@ -269,7 +268,7 @@ function UserList() {
|
||||||
value={currentEntry?.username ?? ''}
|
value={currentEntry?.username ?? ''}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field size="fullWidth">
|
<Field size="fullWidth" spacing="narrow">
|
||||||
<Label htmlFor="d-points" css={{ textTransform: 'capitalize' }}>
|
<Label htmlFor="d-points" css={{ textTransform: 'capitalize' }}>
|
||||||
{config?.currency || t('pages.loyalty-queue.points')}
|
{config?.currency || t('pages.loyalty-queue.points')}
|
||||||
</Label>
|
</Label>
|
||||||
|
@ -303,7 +302,7 @@ function UserList() {
|
||||||
setGivePointDialog({ open: true, user: '', points: 0 })
|
setGivePointDialog({ open: true, user: '', points: 0 })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Give points
|
{t('pages.loyalty-queue.give-points-dialog')}
|
||||||
</Button>
|
</Button>
|
||||||
<InputBox
|
<InputBox
|
||||||
css={{ flex: 1 }}
|
css={{ flex: 1 }}
|
||||||
|
@ -352,7 +351,7 @@ function UserList() {
|
||||||
<TableCell>{entry.points}</TableCell>
|
<TableCell>{entry.points}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Button onClick={() => setCurrentEntry(entry)} size="small">
|
<Button onClick={() => setCurrentEntry(entry)} size="small">
|
||||||
Edit
|
{t('form-actions.edit')}
|
||||||
</Button>
|
</Button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|
|
@ -1,6 +1,173 @@
|
||||||
import React from 'react';
|
import { PlusIcon } from '@radix-ui/react-icons';
|
||||||
|
import React, { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { PageContainer, PageHeader, PageTitle } from '../theme';
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { useModule } from '../../lib/react-utils';
|
||||||
|
import { modules } from '../../store/api/reducer';
|
||||||
|
import { LoyaltyReward } from '../../store/api/types';
|
||||||
|
import DialogContent from '../components/DialogContent';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
Field,
|
||||||
|
FlexRow,
|
||||||
|
InputBox,
|
||||||
|
Label,
|
||||||
|
PageContainer,
|
||||||
|
PageHeader,
|
||||||
|
PageTitle,
|
||||||
|
TabButton,
|
||||||
|
TabContainer,
|
||||||
|
TabContent,
|
||||||
|
TabList,
|
||||||
|
TextBlock,
|
||||||
|
} from '../theme';
|
||||||
|
|
||||||
|
function RewardsPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [rewards, setRewards] = useModule(modules.loyaltyRewards);
|
||||||
|
const [filter, setFilter] = useState('');
|
||||||
|
const [dialogReward, setDialogReward] = useState<{
|
||||||
|
open: boolean;
|
||||||
|
new: boolean;
|
||||||
|
reward: LoyaltyReward;
|
||||||
|
}>({ open: false, new: false, reward: null });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog
|
||||||
|
open={dialogReward.open}
|
||||||
|
onOpenChange={(state) =>
|
||||||
|
setDialogReward({ ...dialogReward, open: state })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DialogContent title={t('pages.loyalty-rewards.create-reward')}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!(e.target as HTMLFormElement).checkValidity()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reward = dialogReward.reward;
|
||||||
|
const index = rewards.findIndex((t) => t.id == reward.id);
|
||||||
|
if (index >= 0) {
|
||||||
|
const newRewards = rewards.slice(0);
|
||||||
|
newRewards[index] = reward;
|
||||||
|
dispatch(setRewards(newRewards));
|
||||||
|
} else {
|
||||||
|
dispatch(setRewards([...rewards, reward]));
|
||||||
|
}
|
||||||
|
setDialogReward({ ...dialogReward, open: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Field size="fullWidth" spacing="narrow">
|
||||||
|
<Label htmlFor="reward-id">
|
||||||
|
{t('pages.loyalty-rewards.reward-id')}
|
||||||
|
</Label>
|
||||||
|
<InputBox
|
||||||
|
id="reward-id"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
disabled={!dialogReward.new}
|
||||||
|
value={dialogReward?.reward?.id}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDialogReward({
|
||||||
|
...dialogReward,
|
||||||
|
reward: {
|
||||||
|
...dialogReward?.reward,
|
||||||
|
id: e.target.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
dialogReward.new &&
|
||||||
|
rewards.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('');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<Field size="fullWidth" spacing="narrow">
|
||||||
|
<Label htmlFor="reward-name">
|
||||||
|
{t('pages.loyalty-rewards.reward-name')}
|
||||||
|
</Label>
|
||||||
|
<InputBox
|
||||||
|
id="reward-name"
|
||||||
|
type="text"
|
||||||
|
required
|
||||||
|
value={dialogReward?.reward?.name}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDialogReward({
|
||||||
|
...dialogReward,
|
||||||
|
reward: {
|
||||||
|
...dialogReward?.reward,
|
||||||
|
name: e.target.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
<DialogActions>
|
||||||
|
<Button variation="primary" type="submit">
|
||||||
|
{t('form-actions.create')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() =>
|
||||||
|
setDialogReward({ ...dialogReward, open: false })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('form-actions.cancel')}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<Field size="fullWidth" spacing="none">
|
||||||
|
<FlexRow css={{ flex: 1, alignItems: 'stretch' }} spacing="1">
|
||||||
|
<Button
|
||||||
|
variation="primary"
|
||||||
|
onClick={() =>
|
||||||
|
setDialogReward({
|
||||||
|
open: true,
|
||||||
|
new: true,
|
||||||
|
reward: {
|
||||||
|
id: '',
|
||||||
|
enabled: true,
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
image: '',
|
||||||
|
price: 0,
|
||||||
|
cooldown: 0,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PlusIcon /> {t('pages.loyalty-rewards.create-reward')}
|
||||||
|
</Button>
|
||||||
|
<InputBox
|
||||||
|
css={{ flex: 1 }}
|
||||||
|
placeholder={t('pages.loyalty-rewards.reward-filter')}
|
||||||
|
value={filter}
|
||||||
|
onChange={(e) => setFilter(e.target.value)}
|
||||||
|
/>
|
||||||
|
</FlexRow>
|
||||||
|
</Field>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GoalsPage() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
export default function LoyaltyRewardsPage(): React.ReactElement {
|
export default function LoyaltyRewardsPage(): React.ReactElement {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -9,7 +176,24 @@ export default function LoyaltyRewardsPage(): React.ReactElement {
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<PageTitle>{t('pages.loyalty-rewards.title')}</PageTitle>
|
<PageTitle>{t('pages.loyalty-rewards.title')}</PageTitle>
|
||||||
|
<TextBlock>{t('pages.loyalty-rewards.subtitle')}</TextBlock>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
<TabContainer defaultValue="rewards">
|
||||||
|
<TabList>
|
||||||
|
<TabButton value="rewards">
|
||||||
|
{t('pages.loyalty-rewards.rewards-tab')}
|
||||||
|
</TabButton>
|
||||||
|
<TabButton value="goals">
|
||||||
|
{t('pages.loyalty-rewards.goals-tab')}
|
||||||
|
</TabButton>
|
||||||
|
</TabList>
|
||||||
|
<TabContent value="rewards">
|
||||||
|
<RewardsPage />
|
||||||
|
</TabContent>
|
||||||
|
<TabContent value="goals">
|
||||||
|
<GoalsPage />
|
||||||
|
</TabContent>
|
||||||
|
</TabContainer>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,9 @@ export const InputBox = styled('input', {
|
||||||
borderColor: '$gray5',
|
borderColor: '$gray5',
|
||||||
color: '$gray8',
|
color: '$gray8',
|
||||||
},
|
},
|
||||||
|
'&:invalid': {
|
||||||
|
borderColor: '$red5',
|
||||||
|
},
|
||||||
variants: {
|
variants: {
|
||||||
border: {
|
border: {
|
||||||
none: {
|
none: {
|
||||||
|
@ -91,6 +94,9 @@ export const Textarea = styled('textarea', {
|
||||||
borderColor: '$gray5',
|
borderColor: '$gray5',
|
||||||
color: '$gray8',
|
color: '$gray8',
|
||||||
},
|
},
|
||||||
|
'&:invalid': {
|
||||||
|
borderColor: '$red5',
|
||||||
|
},
|
||||||
variants: {
|
variants: {
|
||||||
border: {
|
border: {
|
||||||
none: {
|
none: {
|
||||||
|
|
Loading…
Reference in a new issue