Enabled React server page in Frontend BETA

Various fixes to React frontend

- Implemented Extension DataTables table support
- Fixed colors in night mode
- Fixed DataTables night mode
- Fixed chart opacity in night mode (HighCharts doesn't like hsl values)
- Fixed color selector buttons in night mode
- Translated Login button
- Added license to package.json
- Changed Extension endpoint table representation to objects to allow value formatting
This commit is contained in:
Aurora Lahtela 2022-08-27 22:26:30 +03:00
parent c824994cb9
commit 4c53a9a406
25 changed files with 251 additions and 78 deletions

View File

@ -0,0 +1,71 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.delivery.domain.datatransfer.extension;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* @author AuroraLS3
*/
public class TableCellDto {
private final String value;
@Nullable
private final Object valueUnformatted;
public TableCellDto(String value) {
this.value = value;
this.valueUnformatted = null;
}
public TableCellDto(String value, @Nullable Object valueUnformatted) {
this.value = value;
this.valueUnformatted = valueUnformatted;
}
public String getValue() {
return value;
}
@Nullable
public Object getValueUnformatted() {
return valueUnformatted;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TableCellDto that = (TableCellDto) o;
return Objects.equals(getValue(), that.getValue()) && Objects.equals(getValueUnformatted(), that.getValueUnformatted());
}
@Override
public int hashCode() {
return Objects.hash(getValue(), getValueUnformatted());
}
@Override
public String toString() {
return "TableCellDto{" +
"value='" + value + '\'' +
", valueUnformatted=" + valueUnformatted +
'}';
}
}

View File

@ -30,7 +30,7 @@ public class TableDto {
private final List<String> columns;
private final List<IconDto> icons;
private final List<List<Object>> rows;
private final List<List<TableCellDto>> rows;
public TableDto(Table table) {
columns = Arrays.stream(table.getColumns())
@ -46,17 +46,17 @@ public class TableDto {
.collect(Collectors.toList());
}
private List<Object> constructRow(List<String> columns, Object[] row) {
List<Object> constructedRow = new ArrayList<>();
private List<TableCellDto> constructRow(List<String> columns, TableCellDto[] row) {
List<TableCellDto> constructedRow = new ArrayList<>();
int headerLength = row.length - 1;
int columnCount = columns.size();
for (int i = 0; i < columnCount; i++) {
if (i > headerLength) {
constructedRow.add("-");
constructedRow.add(new TableCellDto("-"));
} else {
Object value = row[i];
constructedRow.add(value != null ? value : '-');
TableCellDto cell = row[i];
constructedRow.add(cell != null ? cell : new TableCellDto("-"));
}
}
return constructedRow;
@ -70,7 +70,7 @@ public class TableDto {
return icons;
}
public List<List<Object>> getRows() {
public List<List<TableCellDto>> getRows() {
return rows;
}

View File

@ -23,6 +23,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
@Deprecated
public class DynamicHtmlTable implements HtmlTable {
private final Header[] headers;
private final List<Object[]> rows;

View File

@ -16,6 +16,7 @@
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.domain.datatransfer.extension.TableCellDto;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.html.Html;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
@ -25,8 +26,10 @@ import com.djrapitops.plan.extension.table.TableColumnFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Deprecated
public interface HtmlTable {
static HtmlTable fromExtensionTable(Table table, com.djrapitops.plan.extension.icon.Color tableColor) {
@ -57,25 +60,25 @@ public interface HtmlTable {
return headers.toArray(new Header[0]);
}
static List<Object[]> mapToRows(List<Object[]> rows, TableColumnFormat[] tableColumnFormats) {
static List<TableCellDto[]> mapToRows(List<Object[]> rows, TableColumnFormat[] tableColumnFormats) {
return rows.stream()
.map(row -> {
List<Object> mapped = new ArrayList<>(row.length);
List<TableCellDto> mapped = new ArrayList<>(row.length);
for (int i = 0; i < row.length; i++) {
Object value = row[i];
if (value == null) {
mapped.add(null);
} else {
TableColumnFormat format = tableColumnFormats[i];
mapped.add(applyFormat(format, value));
mapped.add(new TableCellDto(applyFormat(format, value), value));
}
}
return mapped.toArray();
return mapped.toArray(new TableCellDto[0]);
})
.collect(Collectors.toList());
}
static Object applyFormat(TableColumnFormat format, Object value) {
static String applyFormat(TableColumnFormat format, Object value) {
try {
switch (format) {
case TIME_MILLISECONDS:
@ -90,7 +93,7 @@ public interface HtmlTable {
return Html.swapColorCodesToSpan(value.toString());
}
} catch (Exception e) {
return value;
return Objects.toString(value);
}
}

View File

@ -16,17 +16,19 @@
*/
package com.djrapitops.plan.delivery.rendering.html.structure;
import com.djrapitops.plan.delivery.domain.datatransfer.extension.TableCellDto;
import com.djrapitops.plan.delivery.rendering.html.icon.Color;
import com.djrapitops.plan.extension.table.Table;
import java.util.List;
@Deprecated
public class HtmlTableWithColoredHeader implements HtmlTable {
private final Header[] headers;
private final Color headerColor;
private final List<Object[]> rows;
private final List<TableCellDto[]> rows;
public HtmlTableWithColoredHeader(Header[] headers, Color headerColor, List<Object[]> rows) {
public HtmlTableWithColoredHeader(Header[] headers, Color headerColor, List<TableCellDto[]> rows) {
this.headers = headers;
this.headerColor = headerColor;
this.rows = rows;
@ -63,15 +65,15 @@ public class HtmlTableWithColoredHeader implements HtmlTable {
StringBuilder builtBody = new StringBuilder();
builtBody.append("<tbody>");
if (rows.isEmpty()) {
appendRow(builtBody, "No Data");
appendRow(builtBody, new TableCellDto("No Data"));
}
for (Object[] row : rows) {
for (TableCellDto[] row : rows) {
appendRow(builtBody, row);
}
return builtBody.append("</tbody>").toString();
}
private void appendRow(StringBuilder builtBody, Object... row) {
private void appendRow(StringBuilder builtBody, TableCellDto... row) {
int headerLength = row.length - 1;
builtBody.append("<tr>");
for (int i = 0; i < headers.length; i++) {
@ -80,8 +82,8 @@ public class HtmlTableWithColoredHeader implements HtmlTable {
builtBody.append("<td>-");
} else {
builtBody.append("<td>");
Object value = row[i];
builtBody.append(value != null ? value : '-');
TableCellDto cell = row[i];
builtBody.append(cell != null ? cell.getValue() : '-');
}
builtBody.append("</td>");
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {

View File

@ -107,6 +107,12 @@ public class PageFactory {
public Page serverPage(ServerUUID serverUUID) throws IOException {
Server server = dbSystem.get().getDatabase().query(ServerQueries.fetchServerMatchingIdentifier(serverUUID))
.orElseThrow(() -> new NotFoundException("Server not found in the database"));
if (config.get().isTrue(PluginSettings.FRONTEND_BETA)) {
String reactHtml = getResource("index.html");
return () -> reactHtml;
}
return new ServerPage(
getResource("server.html"),
server,

View File

@ -1,4 +1,5 @@
{
"license": "LGPL-3.0-or-later",
"name": "dashboard",
"version": "0.1.0",
"private": true,

View File

@ -9,6 +9,7 @@ import KillsTable from "../table/KillsTable";
import Accordion from "./Accordion";
import {useTranslation} from "react-i18next";
import {baseAddress} from "../../service/backendConfiguration";
import {ChartLoader} from "../navigation/Loader";
const SessionHeader = ({session}) => {
return (
@ -88,7 +89,7 @@ const SessionAccordion = (
) => {
const {t} = useTranslation();
if (!sessions) return <></>
if (!sessions) return <ChartLoader/>
const firstColumn = isPlayer ? (<><Fa icon={faUser}/> {t('html.label.player')}</>)
: (<><Fa icon={faServer}/> {t('html.label.server')}</>)

View File

@ -3,8 +3,7 @@ import {Card, Col} from "react-bootstrap-v5";
import ExtensionIcon from "./ExtensionIcon";
import Datapoint from "../Datapoint";
import Masonry from 'masonry-layout'
import {useTheme} from "../../hooks/themeHook";
import {useTranslation} from "react-i18next";
import ExtensionTable from "./ExtensionTable";
export const ExtensionCardWrapper = ({extension, children}) => {
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
@ -65,32 +64,6 @@ const ExtensionValues = ({tab}) => {
)
}
const ExtensionTable = ({table}) => {
const {nightModeEnabled} = useTheme();
const {t} = useTranslation();
const columns = table.table.rows.length ? table.table.rows.map((row, i) => <tr key={i}>{row.map((value, j) => <td
key={i + '' + j}>{value}</td>)}</tr>) :
<tr>{table.table.columns.map((column, i) =>
<td key={i}>{i === 0 ? t('generic.noData') : '-'}</td>)}
</tr>
return (
<table className={"table table-striped" + (nightModeEnabled ? " table-dark" : '')}>
<thead className={table.tableColorClass}>
<tr>
{table.table.columns.map((column, i) => <th key={i}><ExtensionIcon
icon={table.table.icons[i]}/> {column}
</th>)}
</tr>
</thead>
<tbody>
{columns}
</tbody>
</table>
)
}
const ExtensionTables = ({tab}) => {
return (<>
{tab.tableData.map((table, i) => (

View File

@ -11,4 +11,8 @@ const ExtensionIcon = ({icon}) => (
/>
)
export const toExtensionIconHtmlString = ({icon}) => {
return icon ? `<i class="${iconTypeToFontAwesomeClass(icon.family)} ${icon.iconName} ${icon.colorClass}"></i>` : '';
}
export default ExtensionIcon;

View File

@ -0,0 +1,77 @@
import React, {useState} from 'react';
import {useTheme} from "../../hooks/themeHook";
import {useTranslation} from "react-i18next";
import ExtensionIcon, {toExtensionIconHtmlString} from "./ExtensionIcon";
import DataTablesTable from "../table/DataTablesTable";
const ExtensionDataTable = ({table}) => {
const [id] = useState("extension-table-" + new Date().getTime() + "-" + (Math.floor(Math.random() * 100000)));
const data = {
columns: table.table.columns.map((column, i) => {
return {
title: toExtensionIconHtmlString(table.table.icons[i]) + ' ' + column,
data: {
"_": `col${i}.v`,
display: `col${i}.d`
},
};
}),
data: table.table.rows.map((row) => {
const dataRow = {};
row.forEach((cell, j) => dataRow[`col${j}`] = {
v: cell['valueUnformatted'] || cell.value || cell,
d: cell.value || cell
});
return dataRow;
})
};
const options = {
responsive: true,
deferRender: true,
columns: data.columns,
data: data.data,
order: [[1, "desc"]]
}
return (
<DataTablesTable id={id} options={options}/>
)
}
const ExtensionColoredTable = ({table}) => {
const {nightModeEnabled} = useTheme();
const {t} = useTranslation();
const rows = table.table.rows.length ? table.table.rows.map((row, i) => <tr key={i}>{row.map((value, j) => <td
key={i + '' + j}>{value.value || String(value)}</td>)}</tr>) :
<tr>{table.table.columns.map((column, i) =>
<td key={i}>{i === 0 ? t('generic.noData') : '-'}</td>)}
</tr>
return (
<table className={"table table-striped" + (nightModeEnabled ? " table-dark" : '')}>
<thead className={table.tableColorClass}>
<tr>
{table.table.columns.map((column, i) => <th key={i}><ExtensionIcon
icon={table.table.icons[i]}/> {column}
</th>)}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
)
}
const ExtensionTable = ({table}) => {
const tableLength = table.table.rows.length;
if (tableLength > 25) {
return <ExtensionDataTable table={table}/>
} else {
return <ExtensionColoredTable table={table}/>
}
}
export default ExtensionTable

View File

@ -8,7 +8,7 @@ import {useTranslation} from "react-i18next";
const LineGraph = ({id, series}) => {
const {t} = useTranslation()
const {graphTheming} = useTheme();
const {graphTheming, nightModeEnabled} = useTheme();
useEffect(() => {
NoDataDisplay(Highcharts);
@ -27,12 +27,12 @@ const LineGraph = ({id, series}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
series: series
})
}, [series, graphTheming, id, t])
}, [series, graphTheming, id, t, nightModeEnabled])
return (
<div className="chart-area" id={id}>

View File

@ -2,10 +2,13 @@ import React, {useEffect, useState} from "react";
import {useTranslation} from "react-i18next";
import {tooltip} from "../../util/graphs";
import LineGraph from "./LineGraph";
import {useTheme} from "../../hooks/themeHook";
import {withReducedSaturation} from "../../util/colors";
const PlayerPingGraph = ({data}) => {
const {t} = useTranslation();
const [series, setSeries] = useState([]);
const {nightModeEnabled} = useTheme();
useEffect(() => {
const avgPingSeries = {
@ -13,23 +16,23 @@ const PlayerPingGraph = ({data}) => {
type: 'spline',
tooltip: tooltip.twoDecimals,
data: data.avg_ping_series,
color: data.colors.avg
color: nightModeEnabled ? withReducedSaturation(data.colors.avg) : data.colors.avg
}
const maxPingSeries = {
name: t('html.label.worstPing'),
type: 'spline',
tooltip: tooltip.twoDecimals,
data: data.max_ping_series,
color: data.colors.max
color: nightModeEnabled ? withReducedSaturation(data.colors.max) : data.colors.max
}
const minPingSeries = {
name: t('html.label.bestPing'),
type: 'spline',
tooltip: tooltip.twoDecimals,
data: data.min_ping_series,
color: data.colors.min
color: nightModeEnabled ? withReducedSaturation(data.colors.min) : data.colors.min
}
setSeries([avgPingSeries, maxPingSeries, minPingSeries]);
setSeries([avgPingSeries, maxPingSeries, minPingSeries, nightModeEnabled]);
}, [data, t])
return (

View File

@ -3,14 +3,15 @@ import Highcharts from 'highcharts';
import {useTheme} from "../../hooks/themeHook";
import {useTranslation} from "react-i18next";
import Accessibility from "highcharts/modules/accessibility";
import {withReducedSaturation} from "../../util/colors";
const PunchCard = ({series}) => {
const {t} = useTranslation();
const {graphTheming} = useTheme();
const {graphTheming, nightModeEnabled} = useTheme();
useEffect(() => {
const punchCard = {
name: t('html.label.relativeJoinActivity'),
color: '#222',
color: nightModeEnabled ? withReducedSaturation('#222') : '#222',
data: series
};
Accessibility(Highcharts);
@ -46,7 +47,7 @@ const PunchCard = ({series}) => {
},
series: [punchCard]
}), 25)
}, [series, graphTheming, t])
}, [series, graphTheming, t, nightModeEnabled])
return (
<div className="chart-area" id="punchcard">

View File

@ -2,10 +2,13 @@ import {useTranslation} from "react-i18next";
import React, {useEffect, useState} from "react";
import {tooltip} from "../../util/graphs";
import LineGraph from "./LineGraph";
import {useTheme} from "../../hooks/themeHook";
import {withReducedSaturation} from "../../util/colors";
const TimeByTimeGraph = ({data}) => {
const {t} = useTranslation();
const [series, setSeries] = useState([]);
const {nightModeEnabled} = useTheme();
useEffect(() => {
const uniquePlayers = {
@ -13,16 +16,16 @@ const TimeByTimeGraph = ({data}) => {
type: 'spline',
tooltip: tooltip.zeroDecimals,
data: data.uniquePlayers,
color: data.colors.playersOnline
color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline
};
const newPlayers = {
name: t('html.label.newPlayers'),
type: 'spline',
tooltip: tooltip.zeroDecimals,
data: data.newPlayers,
color: data.colors.newPlayers
color: nightModeEnabled ? withReducedSaturation(data.colors.newPlayers) : data.colors.newPlayers
};
setSeries([uniquePlayers, newPlayers]);
setSeries([uniquePlayers, newPlayers, nightModeEnabled]);
}, [data, t])
return (

View File

@ -110,7 +110,7 @@ const AllPerformanceGraph = ({id, data, dataSeries}) => {
type: 'areaspline',
tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline,
color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline,
color: data.colors.playersOnline,
yAxis: 0
}, tps: {
name: t('html.label.tps'),
@ -164,7 +164,7 @@ const AllPerformanceGraph = ({id, data, dataSeries}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
legend: {

View File

@ -21,7 +21,7 @@ const CpuRamPerformanceGraph = ({id, data, dataSeries}) => {
type: 'areaspline',
tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline,
color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline,
color: data.colors.playersOnline,
yAxis: 0
}, cpu: {
name: t('html.label.cpu'),
@ -76,7 +76,7 @@ const CpuRamPerformanceGraph = ({id, data, dataSeries}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
legend: {

View File

@ -16,13 +16,13 @@ const DiskPerformanceGraph = ({id, data, dataSeries}) => {
const zones = {
disk: [{
value: data.zones.diskThresholdMed,
color: nightModeEnabled ? withReducedSaturation(data.colors.low) : data.colors.low
color: data.colors.low
}, {
value: data.zones.diskThresholdHigh,
color: nightModeEnabled ? withReducedSaturation(data.colors.med) : data.colors.med
color: data.colors.med
}, {
value: Number.MAX_VALUE,
color: nightModeEnabled ? withReducedSaturation(data.colors.high) : data.colors.high
color: data.colors.high
}]
};
@ -57,7 +57,7 @@ const DiskPerformanceGraph = ({id, data, dataSeries}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
legend: {

View File

@ -33,7 +33,7 @@ const TpsPerformanceGraph = ({id, data, dataSeries}) => {
type: 'areaspline',
tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline,
color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline,
color: data.colors.playersOnline,
yAxis: 0
}, tps: {
name: t('html.label.tps'),
@ -73,7 +73,7 @@ const TpsPerformanceGraph = ({id, data, dataSeries}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
legend: {

View File

@ -21,7 +21,7 @@ const WorldPerformanceGraph = ({id, data, dataSeries}) => {
type: 'areaspline',
tooltip: tooltip.zeroDecimals,
data: dataSeries.playersOnline,
color: nightModeEnabled ? withReducedSaturation(data.colors.playersOnline) : data.colors.playersOnline,
color: data.colors.playersOnline,
yAxis: 0
}, entities: {
name: t('html.label.loadedEntities'),
@ -75,7 +75,7 @@ const WorldPerformanceGraph = ({id, data, dataSeries}) => {
title: {text: ''},
plotOptions: {
areaspline: {
fillOpacity: 0.4
fillOpacity: nightModeEnabled ? 0.2 : 0.4
}
},
legend: {

View File

@ -7,7 +7,7 @@ import {Modal} from "react-bootstrap-v5";
import {useTranslation} from "react-i18next";
const ColorSelectorButton = ({color, setColor, disabled}) =>
<button className={"btn color-chooser " + colorEnumToBgClass(color)}
<button className={"btn color-chooser " + colorEnumToBgClass(color) + (disabled ? " disabled" : '')}
id={"choose-" + color}
disabled={disabled}
onClick={() => setColor(color)}

View File

@ -4,9 +4,11 @@ import 'datatables.net-bs5'
import 'datatables.net-responsive-bs5'
import 'datatables.net-bs5/css/dataTables.bootstrap5.min.css';
import 'datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css';
import {useTheme} from "../../hooks/themeHook";
const DataTablesTable = ({id, options}) => {
const dataTableRef = useRef(null);
const {nightModeEnabled} = useTheme();
useEffect(() => {
const idSelector = `#${id}`;
@ -24,7 +26,8 @@ const DataTablesTable = ({id, options}) => {
}, [id, options, dataTableRef]);
return (
<table id={id} className="table table-bordered table-striped" style={{width: "100%"}}/>
<table id={id} className={"table table-bordered table-striped" + (nightModeEnabled ? " table-dark" : '')}
style={{width: "100%"}}/>
)
};

View File

@ -181,6 +181,7 @@ div.scrollbar {
.color-chooser {
margin-right: 0.15rem;
margin-bottom: 0.2rem;
border-color: transparent !important;
}
/* Navbar ====================================== */
@ -767,111 +768,133 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
.bg-red, body.theme-red .fc-toolbar-chunk .btn.btn-primary {
background-color: #F44336;
--bs-btn-disabled-bg: #F44336;
color: #fff;
}
.bg-pink, body.theme-pink .fc-toolbar-chunk .btn.btn-primary {
background-color: #E91E63;
--bs-btn-disabled-bg: #E91E63;
color: #fff;
}
.bg-purple, body.theme-purple .fc-toolbar-chunk .btn.btn-primary {
background-color: #9C27B0;
--bs-btn-disabled-bg: #9C27B0;
color: #fff;
}
.bg-deep-purple, body.theme-deep-purple .fc-toolbar-chunk .btn.btn-primary {
background-color: #673AB7;
--bs-btn-disabled-bg: #673AB7;
color: #fff;
}
.bg-indigo, body.theme-indigo .fc-toolbar-chunk .btn.btn-primary {
background-color: #3F51B5;
--bs-btn-disabled-bg: #3F51B5;
color: #fff;
}
.bg-blue, body.theme-blue .fc-toolbar-chunk .btn.btn-primary {
background-color: #2196F3;
--bs-btn-disabled-bg: #2196F3;
color: #fff;
}
.bg-light-blue, body.theme-light-blue .fc-toolbar-chunk .btn.btn-primary {
background-color: #03A9F4;
--bs-btn-disabled-bg: #03A9F4;
color: #fff;
}
.bg-cyan, body.theme-cyan .fc-toolbar-chunk .btn.btn-primary {
background-color: #00BCD4;
--bs-btn-disabled-bg: #00BCD4;
color: #fff;
}
.bg-teal, body.theme-teal .fc-toolbar-chunk .btn.btn-primary {
background-color: #009688;
--bs-btn-disabled-bg: #009688;
color: #fff;
}
.bg-green, body.theme-green .fc-toolbar-chunk .btn.btn-primary {
background-color: #4CAF50;
--bs-btn-disabled-bg: #4CAF50;
color: #fff;
}
.bg-light-green, body.theme-light-green .fc-toolbar-chunk .btn.btn-primary {
background-color: #8BC34A;
--bs-btn-disabled-bg: #8BC34A;
color: #fff;
}
.bg-lime, body.theme-lime .fc-toolbar-chunk .btn.btn-primary {
background-color: #CDDC39;
--bs-btn-disabled-bg: #CDDC39;
color: #fff;
}
.bg-yellow, body.theme-yellow .fc-toolbar-chunk .btn.btn-primary {
background-color: #ffe821;
--bs-btn-disabled-bg: #ffe821;
color: #fff;
}
.bg-amber, body.theme-amber .fc-toolbar-chunk .btn.btn-primary {
background-color: #FFC107;
--bs-btn-disabled-bg: #FFC107;
color: #fff;
}
.bg-orange, body.theme-orange .fc-toolbar-chunk .btn.btn-primary {
background-color: #FF9800;
--bs-btn-disabled-bg: #FF9800;
color: #fff;
}
.bg-deep-orange, body.theme-deep-orange .fc-toolbar-chunk .btn.btn-primary {
background-color: #FF5722;
--bs-btn-disabled-bg: #FF5722;
color: #fff;
}
.bg-brown, body.theme-brown .fc-toolbar-chunk .btn.btn-primary {
background-color: #795548;
--bs-btn-disabled-bg: #795548;
color: #fff;
}
.bg-grey, body.theme-grey .fc-toolbar-chunk .btn.btn-primary {
background-color: #9E9E9E;
--bs-btn-disabled-bg: #9E9E9E;
color: #fff;
}
.bg-blue-grey, body.theme-blue-grey .fc-toolbar-chunk .btn.btn-primary {
background-color: #607D8B;
--bs-btn-disabled-bg: #607D8B;
color: #fff;
}
.bg-black, body.theme-black .fc-toolbar-chunk .btn.btn-primary {
background-color: #555555;
--bs-btn-disabled-bg: #555555;
color: #fff;
}
.bg-white, body.theme-white .fc-toolbar-chunk .btn.btn-primary {
background-color: #ffffff;
--bs-btn-disabled-bg: #ffffff;
color: #333;
}
.bg-plan, body.theme-plan .fc-toolbar-chunk .btn.btn-primary {
background-color: #368F17;
--bs-btn-disabled-bg: #368F17;
color: #fff;
}
@ -902,6 +925,7 @@ div#navSrvContainer::-webkit-scrollbar-thumb {
.bg-night, body.theme-night .fc-toolbar-chunk .btn.btn-primary {
background-color: #44475a;
--bs-btn-disabled-bg: #44475a;
color: #eee8d5;
}

View File

@ -181,7 +181,7 @@ const createNightModeColorCss = () => {
return `.bg-${color.name}{background-color: ${desaturatedColor} !important;color: ${nightColors.yellow};}` +
`.bg-${color.name}-outline{outline-color: ${desaturatedColor};border-color: ${desaturatedColor};}` +
`.col-${color.name}{color: ${desaturatedColor} !important;}`
}).join();
}).join('');
}
export const createNightModeCss = () => {

View File

@ -66,7 +66,7 @@ const LoginForm = ({login}) => {
value={password} onChange={event => setPassword(event.target.value)}/>
</div>
<button className="btn bg-plan btn-user w-100" id="login-button" onClick={onLogin}>
Login
{t('html.login.login')}
</button>
</form>
);