From 2012e5ccf2a530df3f0cd9661581d3d560ac2e61 Mon Sep 17 00:00:00 2001 From: Victor Date: Tue, 27 Feb 2024 21:59:24 -0800 Subject: [PATCH] revised moderation job control page/TempSelect --- .../public/locales/en/EntityGeneral.json | 24 ++ .../components/common/Checkbox/Checkbox.scss | 26 +- .../components/common/Checkbox/Checkbox.tsx | 19 +- .../common/NumberSlider/NumberSlider.tsx | 26 +- .../common/TempSelect/TempSelect.scss | 15 ++ .../common/TempSelect/TempSelect.tsx | 31 ++- .../entities/EntityInput/EntityInput.tsx | 7 +- .../src/components/entities/EntityTypes.tsx | 9 + .../job_control/ModerationJobControlPage.tsx | 226 ++++++------------ 9 files changed, 195 insertions(+), 188 deletions(-) create mode 100644 packages/fakeyou.com/public/locales/en/EntityGeneral.json diff --git a/packages/fakeyou.com/public/locales/en/EntityGeneral.json b/packages/fakeyou.com/public/locales/en/EntityGeneral.json new file mode 100644 index 0000000000..1800d1ac4b --- /dev/null +++ b/packages/fakeyou.com/public/locales/en/EntityGeneral.json @@ -0,0 +1,24 @@ +{ + "WeightTypes": { + "all": "All weights", + "hifigan_tt2": "HiFi-GAN TT2", + "rvc_v2": "RVCv2", + "sd_1": "SD 1.0", + "sd_1.5": "SD 1.5", + "sdxl": "SDXL", + "so_vits_svc": "so_vits_svc", + "tt2": "TT2", + "lora": "loRA", + "vall_e": "vall_e" + }, + "MediaTypes": { + "all": "All media", + "audio": "Audio", + "image": "Image", + "video": "Video", + "bvh": "BVH", + "glb": "GLB", + "gltf": "gltf", + "fbx": "FBX" + }, +} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.scss b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.scss index 79a06040d1..a2980e3fbb 100644 --- a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.scss +++ b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.scss @@ -3,16 +3,38 @@ .fy-checkbox-frame { margin-bottom: .5rem; white-space: nowrap; + cursor: pointer; label { margin-left: .5rem; + cursor: pointer; + } +} + +.fy-checkbox-primary-unchecked, .fy-checkbox-primary-checked { + .fy-checkbox { + background-color: $primary; + } + &:hover { + .fy-checkbox { + background-color: lighten($primary, 5%); + } + } +} + +.fy-checkbox-secondary-unchecked, .fy-checkbox-secondary-checked { + .fy-checkbox { + background-color: $secondary; + } + &:hover { + .fy-checkbox { + background-color: $secondary-light; + } } } .fy-checkbox { - background-color: $primary; border-radius: .5rem; - cursor: pointer; display: inline-block; vertical-align: middle; height: 2rem; diff --git a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx index 489c2b8ea8..7611848b7e 100644 --- a/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx +++ b/packages/fakeyou.com/src/components/common/Checkbox/Checkbox.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { a, useSpring } from "@react-spring/web"; import { Check } from "components/svg"; -import { useHover, useId } from "hooks"; +import { useId } from "hooks"; import makeClass from "resources/makeClass"; import "./Checkbox.scss"; @@ -11,20 +10,18 @@ interface Props { label?: string; name?: string; onChange?: any; + variant?: "primary" | "secondary"; } -export default function Checkbox({ checked, className = "", label = "", name = "", onChange }: Props) { - const [hover, hoverEvents = {}] = useHover({}); - const style = useSpring({ - config: { tension: 120, friction: 14 }, - backgroundColor: checked ? hover ? "#bb5a5d" : "#e66462" : hover ? "#bb5a5d" : "#39394c" - }); +export default function Checkbox({ checked, className = "", label = "", name = "", onChange, variant = "secondary" }: Props) { const onClick = ({ target }: any) => onChange({ target: { checked: !checked, name, type: 'checkbox' } }); const id = "checkbox-" + useId(); - return
- + return
+ - + { label && }
}; \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/NumberSlider/NumberSlider.tsx b/packages/fakeyou.com/src/components/common/NumberSlider/NumberSlider.tsx index bb3b425fd7..a54eaa02b2 100644 --- a/packages/fakeyou.com/src/components/common/NumberSlider/NumberSlider.tsx +++ b/packages/fakeyou.com/src/components/common/NumberSlider/NumberSlider.tsx @@ -11,10 +11,6 @@ interface Props { required?: boolean; step?: number; thumbTip?: string; - // name, value and setter for onChange has to be in this particular format - // name = state variable's name as a stringh - // value = the state variable's reference - // onChange = the statevariable's setter function's reference name?: string; value?: number; onChange?: any; @@ -29,20 +25,14 @@ const renderTrack = ({ props: { style, ...props }, children }: any) => ( const thumb = (thumbTip = "") => ({ props: { style, ...props } }: any) => { - return ( - -
-
- ); + return thumbTip ? +
+
:
; }; export default function NumberSlider({ diff --git a/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.scss b/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.scss index 18b7cee633..fcdd3a4c5d 100644 --- a/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.scss +++ b/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.scss @@ -8,6 +8,21 @@ margin-bottom: 0; } + .select-container { + overflow: hidden; + } + + .select-option { + background: none; + } + + .select-option.select__option--is-selected { + background-color: $secondary-lighter; + &.select__option--is-focused { + background-color: $secondary-very-light; + } + } + .select__value-container--is-multi { height: min-content; // background: red; diff --git a/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.tsx b/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.tsx index a24f7f4868..e5c9e98a3e 100644 --- a/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.tsx +++ b/packages/fakeyou.com/src/components/common/TempSelect/TempSelect.tsx @@ -6,11 +6,15 @@ import "./TempSelect.scss"; type OptionValue = string | number | boolean; -export interface Option { - value: OptionValue; +export interface BaseOption { + value?: OptionValue; label: string; } +export interface Option extends BaseOption { + options?: BaseOption[]; +} + export interface SelectProps extends ReactSelectProps { icon?: IconDefinition; label?: string; @@ -34,8 +38,21 @@ export default function Select({ ...rest }: SelectProps) { const isMulti = Array.isArray(value); - const valueLabel = - options.find((option: any) => option.value === value)?.label || ""; + const findVal = (opts: Option[], nest = 0): Option | undefined => { + let val: Option | undefined; + opts.forEach((option) => { + if (!val) { + if (option.options) { + val = findVal(option.options,++nest); + } else if (option.value === value) { + val = option + } + } + }); + return val; + }; + + const valueLabel = findVal(options)?.label || ""; const onChange = (option: any, x: any) => { if (Array.isArray(option)) { inChange({ target: {value: option.map(({ value = "" }) => value), name, type: "select" } }); @@ -80,9 +97,9 @@ export default function Select({ name, onChange, options, - ...isMulti ? { value: Array.isArray(value) ? - value.map((val: any) => ({ label: val, value: val })) : [] } : - { value: { label: valueLabel, value } }, + ...value && (isMulti ? { + value: Array.isArray(value) ? value.map((val: any) => ({ label: val, value: val })) : [] + } : { value: { label: valueLabel, value } }), ...rest, }} /> diff --git a/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx index ed8150199e..176965a1ea 100644 --- a/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx +++ b/packages/fakeyou.com/src/components/entities/EntityInput/EntityInput.tsx @@ -132,4 +132,9 @@ export default function EntityInput({ accept: inAccept, aspectRatio = "square", } ; -}; \ No newline at end of file +}; + + // const handleTargetChange = (ev: React.FormEvent) => { + // const value = (ev.target as HTMLSelectElement).value; + // setTarget(value); + // }; diff --git a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx index 06bba0d119..184109ac5c 100644 --- a/packages/fakeyou.com/src/components/entities/EntityTypes.tsx +++ b/packages/fakeyou.com/src/components/entities/EntityTypes.tsx @@ -29,10 +29,19 @@ export enum WeightsFilters { vall_e } +export enum WeightsCategories { + all, + faceAnimation, + tts, + voiceConversion +} + export type EntityModeProp = keyof typeof EntityInputMode; export type MediaFilterProp = keyof typeof MediaFilters; export type WeightFilterProp = keyof typeof WeightsFilters; +export type WeightCategoriesProp = keyof typeof WeightsCategories; export type AcceptTypes = MediaFilterProp | WeightFilterProp; +export type JobSelection = WeightCategoriesProp | WeightFilterProp; export enum EntityType { unknown, diff --git a/packages/fakeyou.com/src/v2/view/pages/moderation/job_control/ModerationJobControlPage.tsx b/packages/fakeyou.com/src/v2/view/pages/moderation/job_control/ModerationJobControlPage.tsx index 131781d77f..8ef0687e77 100644 --- a/packages/fakeyou.com/src/v2/view/pages/moderation/job_control/ModerationJobControlPage.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/moderation/job_control/ModerationJobControlPage.tsx @@ -1,23 +1,35 @@ import React, { useState } from "react"; import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import PageHeader from 'components/layout/PageHeader'; +import { Checkbox, Container, NumberSlider, Panel, TempSelect as Select } from "components/common"; import { WebUrl } from "../../../../../common/WebUrl"; -import { BackLink } from "../../../_common/BackLink"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faBomb } from "@fortawesome/free-solid-svg-icons"; import { KillJobs, } from "@storyteller/components/src/api/moderation/queues/KillJobs"; +import { WeightsCategories, WeightsFilters } from "components/entities/EntityTypes"; interface Props { sessionWrapper: SessionWrapper; } function ModerationJobControlPage(props: Props) { + const [filter, filterSet] = useState(); + const objNumKeys = (obj = {}) => Object.keys(obj) + .filter((val: any) => isNaN(Number(val))) + .filter((val: any) => val !== "all"); + const weightCategories = objNumKeys(WeightsCategories); + const weightTypes = objNumKeys(WeightsFilters); + const makeOptions = (filters: string[]) => filters.map((filter,i) => ({ label: filter, value: filter })); + const categoryOpts = makeOptions(weightCategories); + const typeOpts = makeOptions(weightTypes); + const groupedOpts = [{ label: "ALL JOBS", value: "all_jobs" },{ label: "Categories", options: categoryOpts },{ label: "Types", options: typeOpts }]; + const isCategory = weightCategories.indexOf(filter || "") >= 0; const [killPendingJobs, setKillPendingJobs] = useState(true); const [killFailedJobs, setKillFailedJobs] = useState(true); const [killStartedJobs, setKillStartedJobs] = useState(true); const [priorityLevel, setPriorityLevel] = useState(0); - const [target, setTarget] = useState('all_jobs'); const killJobs= async () => { let jobStatuses = []; @@ -26,30 +38,15 @@ function ModerationJobControlPage(props: Props) { if (killFailedJobs) { jobStatuses.push('failed') } if (killStartedJobs) { jobStatuses.push('started') } - // NB: The backend uses parameterized enums, so the request shape here is unusual - let requestTarget: any = 'all_jobs'; - - switch (target) { - case 'all_jobs': - break; - case 'lipsync_animation': - case 'text_to_speech': - case 'voice_conversion': - requestTarget = { 'category': target }; - break; - case 'rvc_v2': - case 'so_vits_svc': - case 'tacotron2': - requestTarget = { 'model_type': target }; - break; - } - const request = { job_statuses: jobStatuses, - target: requestTarget, + target: filter === "all_jobs" ? filter : + { [ isCategory ? "category" : "model_type" ]: filter }, // * maybe_priority_or_lower: priorityLevel, }; + // * NB: The backend uses parameterized enums, so the request shape here is unusual + await KillJobs(request); }; @@ -59,146 +56,77 @@ function ModerationJobControlPage(props: Props) { return (ev.target as HTMLInputElement).checked; } - const handlePriorityChange = (ev: React.FormEvent) => { - const level = (ev.target as HTMLInputElement).value; - let numericLevel = parseInt(level.trim()); - if (isNaN(numericLevel)) { - numericLevel = 0; - } else if (numericLevel < 0) { - numericLevel = 0; - } - setPriorityLevel(numericLevel); - }; - - const handleTargetChange = (ev: React.FormEvent) => { - const value = (ev.target as HTMLSelectElement).value; - setTarget(value); - }; - if (!props.sessionWrapper.canBanUsers()) { return

Unauthorized

; } + const priorityGroups = ["logged out users","free users","loyalty users","premium users"]; + let groupList = [...priorityGroups]; + + groupList.length = priorityLevel + 1; + return ( -
-
-

Job Stats

-
- + + + + - - - - - - - - -
-
-
- -
Job Statuses
+ { + setPriorityLevel(target.value); + }, + value: priorityLevel, + min: 0, + max: 3 + }}/> +

+ This will kill jobs for { groupList.join(", ") } +

+
- -
-
- -
- -
- -
- -
-
Job Priorities
- -
- -
- -
-
-
eg. setting this to "3" will kill all priorities 3 and below (3, 2, 1, and 0).
-
-
    -
  • Level 0 = logged out
  • -
  • Level 1 = free account
  • -
  • Level 2+ = premium or loyalty user
  • -
-
-
Try not to kill premium user workloads unless things get bad.
-
- -
-
- - -
+
-
- -
- -
- + + ); }