1
0
Fork 0
mirror of https://git.sr.ht/~ashkeel/strimertul synced 2024-09-20 02:00:49 +00:00

Fix minor UI issues after update

This commit is contained in:
Ash Keel 2021-05-17 09:32:57 +02:00
parent ec7f882beb
commit 5a455951b7
No known key found for this signature in database
GPG key ID: CF2CC050478BD7E5
4 changed files with 66 additions and 19 deletions

View file

@ -1,9 +1,16 @@
import { ActionCreatorWithOptionalPayload, AsyncThunk } from '@reduxjs/toolkit'; import { ActionCreatorWithOptionalPayload, AsyncThunk } from '@reduxjs/toolkit';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { KilovoltMessage } from '@strimertul/kilovolt-client'; import {
KilovoltMessage,
SubscriptionHandler,
} from '@strimertul/kilovolt-client';
import { RootState } from '../store'; import { RootState } from '../store';
import { APIState } from '../store/api/reducer'; import apiReducer, {
APIState,
getUserPoints,
LoyaltyStorage,
} from '../store/api/reducer';
export function useModule<T>({ export function useModule<T>({
key, key,
@ -37,6 +44,27 @@ export function useModule<T>({
return [data, setter]; return [data, setter];
} }
export function useUserPoints(): LoyaltyStorage {
const prefix = 'loyalty/points/';
const client = useSelector((state: RootState) => state.api.client);
const data = useSelector((state: RootState) => state.api.loyalty.users);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getUserPoints());
const subscriber: SubscriptionHandler = (newValue, key) => {
const user = key.substring(prefix.length);
const entry = JSON.parse(newValue);
dispatch(apiReducer.actions.loyaltyUserPointsChanged({ user, entry }));
};
client.subscribePrefix(prefix, subscriber);
return () => {
client.subscribePrefix(prefix, subscriber);
};
}, []);
return data;
}
export default { export default {
useModule, useModule,
useUserPoints,
}; };

View file

@ -208,7 +208,6 @@ export const getUserPoints = createAsyncThunk(
async (_: void, { getState }) => { async (_: void, { getState }) => {
const { api } = getState() as { api: APIState }; const { api } = getState() as { api: APIState };
const keys = await api.client.getKeysByPrefix(loyaltyPointsPrefix); const keys = await api.client.getKeysByPrefix(loyaltyPointsPrefix);
console.log(keys);
const userpoints: LoyaltyStorage = {}; const userpoints: LoyaltyStorage = {};
Object.entries(keys).forEach(([k, v]) => { Object.entries(keys).forEach(([k, v]) => {
userpoints[k.substr(loyaltyPointsPrefix.length)] = JSON.parse(v); userpoints[k.substr(loyaltyPointsPrefix.length)] = JSON.parse(v);
@ -217,6 +216,25 @@ export const getUserPoints = createAsyncThunk(
}, },
); );
export const setUserPoints = createAsyncThunk(
'api/setUserPoints',
async (
{
user,
points,
relative,
}: { user: string; points: number; relative: boolean },
{ getState },
) => {
const { api } = getState() as { api: APIState };
const entry: LoyaltyPointsEntry = { points };
if (relative) {
entry.points += api.loyalty.users[user].points ?? 0;
}
return api.client.putJSON(loyaltyPointsPrefix + user, entry);
},
);
export const modules = { export const modules = {
moduleConfig: makeModule<ModuleConfig>( moduleConfig: makeModule<ModuleConfig>(
moduleConfigKey, moduleConfigKey,
@ -333,6 +351,14 @@ const apiReducer = createSlice({
loyaltyGoalsChanged(state, { payload }: PayloadAction<LoyaltyGoal[]>) { loyaltyGoalsChanged(state, { payload }: PayloadAction<LoyaltyGoal[]>) {
state.loyalty.goals = payload; state.loyalty.goals = payload;
}, },
loyaltyUserPointsChanged(
state,
{
payload: { user, entry },
}: PayloadAction<{ user: string; entry: LoyaltyPointsEntry }>,
) {
state.loyalty.users[user] = entry;
},
}, },
extraReducers: (builder) => { extraReducers: (builder) => {
builder.addCase(createWSClient.fulfilled, (state, { payload }) => { builder.addCase(createWSClient.fulfilled, (state, { payload }) => {

View file

@ -141,7 +141,7 @@ function GoalModal({
setID(newID.toLowerCase().replace(/[^a-zA-Z0-9]/gi, '-')); setID(newID.toLowerCase().replace(/[^a-zA-Z0-9]/gi, '-'));
const slug = id || name?.toLowerCase().replace(/[^a-zA-Z0-9]/gi, '-') || ''; const slug = id || name?.toLowerCase().replace(/[^a-zA-Z0-9]/gi, '-') || '';
const idExists = goals?.some((reward) => reward.id === slug) ?? false; const idExists = goals?.some((goal) => goal.id === slug) ?? false;
const idInvalid = slug !== initialData?.id && idExists; const idInvalid = slug !== initialData?.id && idExists;
const validForm = idInvalid === false && name !== '' && total >= 0; const validForm = idInvalid === false && name !== '' && total >= 0;
@ -175,7 +175,7 @@ function GoalModal({
> >
<div className="field is-horizontal"> <div className="field is-horizontal">
<div className="field-label is-normal"> <div className="field-label is-normal">
<label className="label">Reward ID</label> <label className="label">Goal ID</label>
</div> </div>
<div className="field-body"> <div className="field-body">
<div className="field"> <div className="field">
@ -183,20 +183,20 @@ function GoalModal({
<input <input
className={idInvalid ? 'input is-danger' : 'input'} className={idInvalid ? 'input is-danger' : 'input'}
type="text" type="text"
placeholder="reward_id_here" placeholder="goal_id_here"
value={slug} value={slug}
onChange={(ev) => setIDex(ev.target.value)} onChange={(ev) => setIDex(ev.target.value)}
/> />
</p> </p>
{idInvalid ? ( {idInvalid ? (
<p className="help is-danger"> <p className="help is-danger">
There is already a reward with this ID! Please choose a There is already a goal with this ID! Please choose a different
different one. one.
</p> </p>
) : ( ) : (
<p className="help"> <p className="help">
Choose a simple name that can be referenced by other software. Choose a simple name that can be referenced by other software.
It will be auto-generated from the reward name if you leave it It will be auto-generated from the goal name if you leave it
blank. blank.
</p> </p>
)} )}

View file

@ -1,9 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RouteComponentProps } from '@reach/router'; import { RouteComponentProps } from '@reach/router';
import PageList from '../../components/PageList'; import PageList from '../../components/PageList';
import { RootState } from '../../../store'; import { useUserPoints } from '../../../lib/react-utils';
import { getUserPoints } from '../../../store/api/reducer';
interface SortingOrder { interface SortingOrder {
key: 'user' | 'points'; key: 'user' | 'points';
@ -13,17 +11,12 @@ export default function LoyaltyUserListPage(
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
props: RouteComponentProps<unknown>, props: RouteComponentProps<unknown>,
): React.ReactElement { ): React.ReactElement {
const users = useSelector((state: RootState) => state.api.loyalty.users); const users = useUserPoints();
const dispatch = useDispatch();
const [sorting, setSorting] = useState<SortingOrder>({ const [sorting, setSorting] = useState<SortingOrder>({
key: 'points', key: 'points',
order: 'desc', order: 'desc',
}); });
useEffect(() => {
dispatch(getUserPoints());
}, []);
const [entriesPerPage, setEntriesPerPage] = useState(15); const [entriesPerPage, setEntriesPerPage] = useState(15);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const [usernameFilter, setUsernameFilter] = useState(''); const [usernameFilter, setUsernameFilter] = useState('');