some ui refactorings,

use_user_db option for storing api user inside db file
This commit is contained in:
euzu
2025-02-26 13:46:23 +01:00
parent 4a5f1b488d
commit cbef4e3d4b
16 changed files with 493 additions and 123 deletions
@@ -1,11 +1,54 @@
@use '../../scss/common';
@import "react-datepicker/dist/react-datepicker.css";
.date-picker-container {
display: flex;
flex-flow: row nowrap;
align-items: center;
gap: 4px;
&__toolbar {
display: flex;
flex-flow: row nowrap;
align-items: center;
gap: 4px;
button {
@include common.iconButton();
}
svg {
fill: var(--icon-button-color);
}
}
}
.react-datepicker-wrapper {
box-sizing: border-box;
overflow: hidden;
}
.react-datepicker__header {
h2 {
display: none;
}
}
.react-datepicker__navigation--years-upcoming {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 12px solid var(--border-color);
transform: translateY(4px);
}
.react-datepicker__navigation--years-previous {
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 12px solid var(--border-color);
transform: translateY(-4px);
}
.react-datepicker__input-container {
display: flex;
flex-flow: row-reverse nowrap;
@@ -15,6 +58,7 @@
border-radius: var(--border-radius);
user-select: none;
cursor: pointer;
box-sizing: border-box;
input {
user-select: none;
@@ -2,6 +2,7 @@ import React, {useCallback, useEffect} from "react";
import "./date-picker.scss";
import ReactDatePicker from "react-datepicker";
import {getIconByName} from "../../icons/icons";
import useTranslator from "../../hook/use-translator";
interface DatePickerProps {
@@ -12,6 +13,7 @@ interface DatePickerProps {
export default function DatePicker(props: DatePickerProps) {
const {name, value, onChange} = props;
const translate= useTranslator();
const [selected, setSelected] = React.useState<any>(value);
const datePickerRef = React.useRef<ReactDatePicker>(null);
@@ -31,19 +33,36 @@ export default function DatePicker(props: DatePickerProps) {
}
}, []);
const handleInfiniteDate = useCallback(() => {
handleDateChange(new Date(0));
}, [handleDateChange]);
const handleNowDate = useCallback(() => {
const date = new Date();
date.setFullYear(date.getFullYear() + 1);
handleDateChange(date);
}, [handleDateChange]);
return (
<div className="date-picker-container" onClick={handleClick}>
<ReactDatePicker
ref={datePickerRef}
showIcon={true}
icon={getIconByName('Calendar')}
dateFormat={'YYYY-MM-dd'}
// readOnly={true}
showMonthDropdown={true}
showYearDropdown={true}
onSelect={handleDateChange}
selected={selected}>
</ReactDatePicker>
<div className="date-picker-container">
<div className="date-picker-container-wrapper" onClick={handleClick}>
<ReactDatePicker
ref={datePickerRef}
showIcon={true}
icon={getIconByName('Calendar')}
dateFormat={'YYYY-MM-dd'}
// readOnly={true}
showMonthDropdown={true}
showYearDropdown={true}
minDate={new Date(0)}
onSelect={handleDateChange}
selected={selected}>
</ReactDatePicker>
</div>
<div className={'date-picker-container__toolbar'}>
<button title={translate('LABEL.INFINITE')} onClick={handleInfiniteDate}>{getIconByName('Unlimited')}</button>
<button title={translate('LABEL.ONE_YEAR')} onClick={handleNowDate}>{getIconByName('Today')}</button>
</div>
</div>
);
}
@@ -1,6 +1,26 @@
@use '../../scss/common';
@use '../../scss/preferences';
.proxy-type {
&-reverse {
}
&-redirect {
color: #fffa00;
}
}
.status {
&-active, .status-trial {
color: var(--ok-color);
}
&-banned {
color: var(--attention-color);
}
&-expired, &-disabled, &-pending {
color: var(--warn-color);
}
}
.tag-select {
min-width: 160px;
}
+58 -14
View File
@@ -12,15 +12,43 @@ import PlaylistFilter from "../playlist-filter/playlist-filter";
import UserEditor from "../user-editor/user-editor";
import TextGenerator from "../../utils/text-generator";
const renderExpDate = (value: any, hidden?: boolean) => {
if (!value) {
return getIconByName('Unlimited');
}
return DateUtils.formatDate(value)
};
const renderMaxCon = (value: any, hidden?: boolean) => {
if (!value) {
return getIconByName('Unlimited');
}
return value;
};
const renderStatus = (value: any, hidden?: boolean) => {
if (value) {
return <span className={'status-' + value.toLowerCase()}>{value}</span>
}
return value;
};
const renderProxyType = (value: any, hidden?: boolean) => {
if (value) {
return <span className={'proxy-type-' + value.toLowerCase()}>{value}</span>
}
return value;
};
const COLUMNS = [
{field: 'username', label: 'LABEL.USERNAME'},
{field: 'password', label: 'LABEL.PASSWORD', hidden: true},
{field: 'token', label: 'LABEL.TOKEN', hidden: true},
{field: 'server', label: 'LABEL.SERVER'},
{field: 'proxy', label: 'LABEL.PROXY'},
{field: 'max_connections', label: 'LABEL.MAX_CON'},
{field: 'status', label: 'LABEL.STATUS'},
{field: 'exp_date', label: 'LABEL.EXP_DATE', render: (value: any, hidden?: boolean) => DateUtils.formatDate(value)},
{field: 'proxy', label: 'LABEL.PROXY', render: renderProxyType},
{field: 'max_connections', label: 'LABEL.MAX_CON', render: renderMaxCon},
{field: 'status', label: 'LABEL.STATUS', render: renderStatus},
{field: 'exp_date', label: 'LABEL.EXP_DATE', render: renderExpDate},
]
COLUMNS.forEach(col => {
@@ -33,7 +61,10 @@ const prepareCredentials = (targetUser: TargetUser[]) => {
targetUser.forEach((user) => {
user.credentials.forEach((credential) => {
if (credential.exp_date) {
credential.exp_date = new Date(credential.exp_date * 1000) as any;
credential.exp_date = DateUtils.unixSecondsToDate(credential.exp_date);
}
if (credential.created_at) {
credential.created_at = DateUtils.unixSecondsToDate(credential.created_at);
}
})
});
@@ -41,13 +72,12 @@ const prepareCredentials = (targetUser: TargetUser[]) => {
const prepareTargetUserForSave = (targetUser: TargetUser[]): TargetUser[] => {
return targetUser.map((user) => {
let newUser = {...user, credentials: user.credentials.map(c => ({...c}))};
newUser.credentials.forEach((credential) => {
if (credential.exp_date) {
credential.exp_date = Math.floor((credential.exp_date as any).getTime() / 1000);
}
let storeUser = {...user, credentials: user.credentials.map(c => ({...c}))};
storeUser.credentials.forEach((credential) => {
credential.exp_date = DateUtils.toUnixSeconds(credential.exp_date);
credential.created_at = DateUtils.toUnixSeconds(credential.created_at);
});
return newUser;
return storeUser;
});
}
@@ -93,15 +123,17 @@ const createNewUser = (targets: TargetUser[]): Credentials => {
break;
}
}
const created_at = Math.floor(Date.now() / 1000);
const created_at = Date.now();
const exp_date = new Date();
exp_date.setFullYear(exp_date.getFullYear()+1);
return {
username,
password: TextGenerator.generatePassword(),
token: TextGenerator.generatePassword(),
proxy: 'reverse',
created_at,
exp_date: undefined,
max_connections: undefined,
exp_date: exp_date.getTime(),
max_connections: 1,
status: "Active",
// @ts-ignore
_ref: undefined, // an indicator for new user
@@ -203,6 +235,7 @@ export default function UserView(props: UserViewProps) {
const handleSave = useCallback(() => {
const usernames: any = {};
const tokens: any = {};
for (const target of targets) {
for (const user of target.credentials) {
const err = checkuser(user);
@@ -210,7 +243,18 @@ export default function UserView(props: UserViewProps) {
enqueueSnackbar(translate(err), {variant: 'error'});
return;
}
if (usernames[user.username]) {
enqueueSnackbar(translate("MESSAGES.USER.DUPLICATE_USERNAME") + user.username, {variant: 'error'});
return;
}
usernames[user.username] = true;
if (user.token) {
if (tokens[user.token]) {
enqueueSnackbar(translate("MESSAGES.USER.DUPLICATE_TOKEN") + user.token, {variant: 'error'});
return;
}
tokens[user.token] = true;
}
}
}
const targetUser = targets.map(t => {