Fixes for bots running (#22)

* Fixes for bots running

* Up botmanager

* Add cooldown

* Refact can open position

* Add cooldown Period and MaxLossStreak

* Add agentName

* Add env variable for botManager

* Always enable Botmanager

* Fix bot handle

* Fix get positions

* Add Ticker url

* Dont start stopped bot

* fix
This commit is contained in:
Oda
2025-05-09 17:40:31 +02:00
committed by GitHub
parent a8eb0aaf02
commit 7c38c27b4a
54 changed files with 5164 additions and 641 deletions

View File

@@ -7,13 +7,15 @@ import Toast from '../Toast/Toast'
interface TradesModalProps {
showModal: boolean
botName: string | null
agentName: string | null
strategyName: string | null
onClose: () => void
}
const TradesModal: React.FC<TradesModalProps> = ({
showModal,
botName,
strategyName,
agentName,
onClose,
}) => {
const { apiUrl } = useApiUrlStore()
@@ -22,19 +24,19 @@ const TradesModal: React.FC<TradesModalProps> = ({
const [closingPosition, setClosingPosition] = useState<string | null>(null)
useEffect(() => {
if (showModal && botName) {
if (showModal && strategyName && agentName) {
fetchStrategyData()
}
}, [showModal, botName])
}, [showModal, strategyName, agentName])
const fetchStrategyData = async () => {
if (!botName) return
if (!strategyName || !agentName) return
setLoading(true)
const client = new DataClient({}, apiUrl)
try {
const data = await client.data_GetUserStrategy("Oda", botName)
const data = await client.data_GetUserStrategy(agentName, strategyName)
setStrategyData(data)
} catch (error) {
console.error('Error fetching strategy data:', error)
@@ -46,7 +48,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
}
const closePosition = async (position: Position) => {
if (!botName) return
if (!agentName) return
try {
setClosingPosition(position.identifier)
@@ -56,7 +58,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
// Use BotClient instead of fetch
const botClient = new BotClient({}, apiUrl)
const request: ClosePositionRequest = {
botName: botName,
identifier: agentName,
positionId: position.identifier
}
@@ -78,7 +80,7 @@ const TradesModal: React.FC<TradesModalProps> = ({
<Modal
showModal={showModal}
onClose={onClose}
titleHeader={`Trades for ${botName}`}
titleHeader={`Trades for ${strategyName}`}
>
<div className="mt-4">
{loading ? (

View File

@@ -42,7 +42,9 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
const { register, handleSubmit, setValue } = useForm<IBacktestsFormInput>({
defaultValues: {
startDate: defaultStartDateString,
endDate: defaultEndDateString
endDate: defaultEndDateString,
cooldownPeriod: 1, // Default cooldown period of 1 minute
maxLossStreak: 0 // Default max loss streak of 0 (no limit)
}
});
const [selectedAccount, setSelectedAccount] = useState<string>('')
@@ -110,6 +112,8 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
new Date(form.startDate), // startDate
new Date(form.endDate), // endDate
form.save,
form.cooldownPeriod, // Use the cooldown period from the form
form.maxLossStreak, // Add the max loss streak parameter
customMoneyManagement
)
.then((backtest: Backtest) => {
@@ -362,6 +366,28 @@ const BacktestModal: React.FC<BacktestModalProps> = ({
/>
</FormInput>
<FormInput label="Cooldown Period (candles)" htmlFor="cooldownPeriod">
<input
type="number"
className="input input-bordered w-full"
min="1"
step="1"
{...register('cooldownPeriod', { valueAsNumber: true })}
/>
</FormInput>
</div>
<div className="grid grid-cols-2 gap-4">
<FormInput label="Max Loss Streak" htmlFor="maxLossStreak">
<input
type="number"
className="input input-bordered w-full"
min="0"
step="1"
{...register('maxLossStreak', { valueAsNumber: true })}
/>
</FormInput>
<FormInput label="Save" htmlFor="save">
<input
type="checkbox"

View File

@@ -337,7 +337,7 @@ export class BacktestClient extends AuthorizedApiBase {
return Promise.resolve<Backtest>(null as any);
}
backtest_Run(accountName: string | null | undefined, botType: BotType | undefined, ticker: Ticker | undefined, scenarioName: string | null | undefined, timeframe: Timeframe | undefined, watchOnly: boolean | undefined, balance: number | undefined, moneyManagementName: string | null | undefined, startDate: Date | undefined, endDate: Date | undefined, save: boolean | undefined, moneyManagement: MoneyManagement | undefined): Promise<Backtest> {
backtest_Run(accountName: string | null | undefined, botType: BotType | undefined, ticker: Ticker | undefined, scenarioName: string | null | undefined, timeframe: Timeframe | undefined, watchOnly: boolean | undefined, balance: number | undefined, moneyManagementName: string | null | undefined, startDate: Date | undefined, endDate: Date | undefined, save: boolean | undefined, cooldownPeriod: number | undefined, maxLossStreak: number | undefined, moneyManagement: MoneyManagement | undefined): Promise<Backtest> {
let url_ = this.baseUrl + "/Backtest/Run?";
if (accountName !== undefined && accountName !== null)
url_ += "accountName=" + encodeURIComponent("" + accountName) + "&";
@@ -377,6 +377,14 @@ export class BacktestClient extends AuthorizedApiBase {
throw new Error("The parameter 'save' cannot be null.");
else if (save !== undefined)
url_ += "save=" + encodeURIComponent("" + save) + "&";
if (cooldownPeriod === null)
throw new Error("The parameter 'cooldownPeriod' cannot be null.");
else if (cooldownPeriod !== undefined)
url_ += "cooldownPeriod=" + encodeURIComponent("" + cooldownPeriod) + "&";
if (maxLossStreak === null)
throw new Error("The parameter 'maxLossStreak' cannot be null.");
else if (maxLossStreak !== undefined)
url_ += "maxLossStreak=" + encodeURIComponent("" + maxLossStreak) + "&";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(moneyManagement);
@@ -465,14 +473,14 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve<string>(null as any);
}
bot_Stop(botType: BotType | undefined, botName: string | null | undefined): Promise<string> {
bot_Stop(botType: BotType | undefined, identifier: string | null | undefined): Promise<string> {
let url_ = this.baseUrl + "/Bot/Stop?";
if (botType === null)
throw new Error("The parameter 'botType' cannot be null.");
else if (botType !== undefined)
url_ += "botType=" + encodeURIComponent("" + botType) + "&";
if (botName !== undefined && botName !== null)
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
if (identifier !== undefined && identifier !== null)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
@@ -506,10 +514,10 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve<string>(null as any);
}
bot_Delete(botName: string | null | undefined): Promise<boolean> {
bot_Delete(identifier: string | null | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/Bot/Delete?";
if (botName !== undefined && botName !== null)
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
if (identifier !== undefined && identifier !== null)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
@@ -578,14 +586,14 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve<string>(null as any);
}
bot_Restart(botType: BotType | undefined, botName: string | null | undefined): Promise<string> {
bot_Restart(botType: BotType | undefined, identifier: string | null | undefined): Promise<string> {
let url_ = this.baseUrl + "/Bot/Restart?";
if (botType === null)
throw new Error("The parameter 'botType' cannot be null.");
else if (botType !== undefined)
url_ += "botType=" + encodeURIComponent("" + botType) + "&";
if (botName !== undefined && botName !== null)
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
if (identifier !== undefined && identifier !== null)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
@@ -654,10 +662,10 @@ export class BotClient extends AuthorizedApiBase {
return Promise.resolve<string>(null as any);
}
bot_ToggleIsForWatching(botName: string | null | undefined): Promise<string> {
bot_ToggleIsForWatching(identifier: string | null | undefined): Promise<string> {
let url_ = this.baseUrl + "/Bot/ToggleIsForWatching?";
if (botName !== undefined && botName !== null)
url_ += "botName=" + encodeURIComponent("" + botName) + "&";
if (identifier !== undefined && identifier !== null)
url_ += "identifier=" + encodeURIComponent("" + identifier) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
@@ -816,7 +824,7 @@ export class DataClient extends AuthorizedApiBase {
this.baseUrl = baseUrl ?? "http://localhost:5000";
}
data_GetTickers(timeframe: Timeframe | undefined): Promise<Ticker[]> {
data_GetTickers(timeframe: Timeframe | undefined): Promise<TickerInfos[]> {
let url_ = this.baseUrl + "/Data/GetTickers?";
if (timeframe === null)
throw new Error("The parameter 'timeframe' cannot be null.");
@@ -838,13 +846,13 @@ export class DataClient extends AuthorizedApiBase {
});
}
protected processData_GetTickers(response: Response): Promise<Ticker[]> {
protected processData_GetTickers(response: Response): Promise<TickerInfos[]> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as Ticker[];
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TickerInfos[];
return result200;
});
} else if (status !== 200 && status !== 204) {
@@ -852,7 +860,7 @@ export class DataClient extends AuthorizedApiBase {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<Ticker[]>(null as any);
return Promise.resolve<TickerInfos[]>(null as any);
}
data_GetSpotlight(): Promise<SpotlightOverview> {
@@ -2202,6 +2210,80 @@ export class UserClient extends AuthorizedApiBase {
}
return Promise.resolve<string>(null as any);
}
user_GetCurrentUser(): Promise<User> {
let url_ = this.baseUrl + "/User";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processUser_GetCurrentUser(_response);
});
}
protected processUser_GetCurrentUser(response: Response): Promise<User> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as User;
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<User>(null as any);
}
user_UpdateAgentName(agentName: string): Promise<User> {
let url_ = this.baseUrl + "/User/agent-name";
url_ = url_.replace(/[?&]$/, "");
const content_ = JSON.stringify(agentName);
let options_: RequestInit = {
body: content_,
method: "PUT",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processUser_UpdateAgentName(_response);
});
}
protected processUser_UpdateAgentName(response: Response): Promise<User> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as User;
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<User>(null as any);
}
}
export class WorkflowClient extends AuthorizedApiBase {
@@ -2397,6 +2479,7 @@ export enum AccountType {
export interface User {
name?: string | null;
accounts?: Account[] | null;
agentName?: string | null;
}
export interface Balance {
@@ -2839,15 +2922,15 @@ export interface SuperTrendResult extends ResultBase {
}
export interface StartBotRequest {
botType: BotType;
botName: string;
ticker: Ticker;
timeframe: Timeframe;
isForWatchOnly: boolean;
scenario: string;
accountName: string;
moneyManagementName: string;
initialTradingBalance: number;
botType?: BotType;
identifier?: string | null;
ticker?: Ticker;
scenario?: string | null;
timeframe?: Timeframe;
accountName?: string | null;
moneyManagementName?: string | null;
isForWatchOnly?: boolean;
initialTradingBalance?: number;
}
export interface TradingBot {
@@ -2865,18 +2948,25 @@ export interface TradingBot {
botType: BotType;
accountName: string;
moneyManagement: MoneyManagement;
identifier: string;
agentName: string;
}
export interface OpenPositionManuallyRequest {
botName?: string | null;
identifier?: string | null;
direction?: TradeDirection;
}
export interface ClosePositionRequest {
botName?: string | null;
identifier?: string | null;
positionId?: string | null;
}
export interface TickerInfos {
ticker?: Ticker;
imageUrl?: string | null;
}
export interface SpotlightOverview {
spotlights: Spotlight[];
dateTime: Date;
@@ -2962,7 +3052,7 @@ export interface PlatformSummaryViewModel {
}
export interface AgentSummaryViewModel {
username?: string | null;
agentName?: string | null;
totalPnL?: number;
pnLLast24h?: number;
totalROI?: number;

View File

@@ -111,6 +111,8 @@ export type IBacktestsFormInput = {
loop: number
startDate: string
endDate: string
cooldownPeriod: number
maxLossStreak: number
}
export type IBacktestCards = {

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react'
import React, {useState} from 'react'
import 'react-toastify/dist/ReactToastify.css'
import { Tabs } from '../../components/mollecules'
import type { TabsType } from '../../global/type'
import {Tabs} from '../../components/mollecules'
import type {TabsType} from '../../global/type'
import BacktestLoop from './backtestLoop'
import BacktestPlayground from './backtestPlayground'
@@ -12,14 +12,14 @@ import BacktestUpload from './backtestUpload'
// Tabs Array
const tabs: TabsType = [
{
Component: BacktestScanner,
Component: BacktestPlayground,
index: 1,
label: 'Scanner',
label: 'Playground',
},
{
Component: BacktestPlayground,
Component: BacktestScanner,
index: 2,
label: 'Playground',
label: 'Scanner',
},
{
Component: BacktestLoop,

View File

@@ -37,13 +37,13 @@ const BotList: React.FC<IBotList> = ({ list }) => {
const [showManualPositionModal, setShowManualPositionModal] = useState(false)
const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState<string | null>(null)
const [showTradesModal, setShowTradesModal] = useState(false)
const [selectedBotForTrades, setSelectedBotForTrades] = useState<string | null>(null)
const [selectedBotForTrades, setSelectedBotForTrades] = useState<{ identifier: string; agentName: string } | null>(null)
function getIsForWatchingBadge(isForWatchingOnly: boolean, name: string) {
function getIsForWatchingBadge(isForWatchingOnly: boolean, identifier: string) {
const classes =
baseBadgeClass() + (isForWatchingOnly ? ' bg-accent' : ' bg-primary')
return (
<button className={classes} onClick={() => toggleIsForWatchingOnly(name)}>
<button className={classes} onClick={() => toggleIsForWatchingOnly(identifier)}>
{isForWatchingOnly ? (
<p className="text-accent-content flex">
<EyeIcon width={12}></EyeIcon>
@@ -59,16 +59,16 @@ const BotList: React.FC<IBotList> = ({ list }) => {
)
}
function toggleIsForWatchingOnly(name: string) {
function toggleIsForWatchingOnly(identifier: string) {
const t = new Toast('Switch watch only')
client.bot_ToggleIsForWatching(name).then(() => {
client.bot_ToggleIsForWatching(identifier).then(() => {
t.update('success', 'Bot updated')
})
}
function getDeleteBadge(name: string) {
function getDeleteBadge(identifier: string) {
const classes = baseBadgeClass() + 'bg-error'
return (
<button className={classes} onClick={() => deleteBot(name)}>
<button className={classes} onClick={() => deleteBot(identifier)}>
<p className="text-primary-content flex">
<TrashIcon width={15}></TrashIcon>
</p>
@@ -77,7 +77,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
}
function getToggleBotStatusBadge(
status: string,
name: string,
identifier: string,
botType: BotType
) {
const classes =
@@ -85,7 +85,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
return (
<button
className={classes}
onClick={() => toggleBotStatus(status, name, botType)}
onClick={() => toggleBotStatus(status, identifier, botType)}
>
{status == 'Up' ? (
<p className="text-accent-content flex">
@@ -118,10 +118,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
)
}
function getManualPositionBadge(botName: string) {
function getManualPositionBadge(botIdentifier: string) {
const classes = baseBadgeClass() + ' bg-info'
return (
<button className={classes} onClick={() => openManualPositionModal(botName)}>
<button className={classes} onClick={() => openManualPositionModal(botIdentifier)}>
<p className="text-primary-content flex">
<PlusCircleIcon width={15}></PlusCircleIcon>
</p>
@@ -129,10 +129,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
)
}
function getTradesBadge(botName: string) {
function getTradesBadge(botIdentifier: string, agentName: string) {
const classes = baseBadgeClass() + ' bg-secondary'
return (
<button className={classes} onClick={() => openTradesModal(botName)}>
<button className={classes} onClick={() => openTradesModal(botIdentifier, agentName)}>
<p className="text-primary-content flex">
<ChartBarIcon width={15}></ChartBarIcon>
</p>
@@ -140,23 +140,23 @@ const BotList: React.FC<IBotList> = ({ list }) => {
)
}
function openManualPositionModal(botName: string) {
setSelectedBotForManualPosition(botName)
function openManualPositionModal(botIdentifier: string) {
setSelectedBotForManualPosition(botIdentifier)
setShowManualPositionModal(true)
}
function openTradesModal(botName: string) {
setSelectedBotForTrades(botName)
function openTradesModal(botIdentifier: string, agentName: string) {
setSelectedBotForTrades({ identifier: botIdentifier, agentName })
setShowTradesModal(true)
}
function toggleBotStatus(status: string, name: string, botType: BotType) {
function toggleBotStatus(status: string, identifier: string, botType: BotType) {
const isUp = status == 'Up'
const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot')
if (status == 'Up') {
client
.bot_Stop(botType, name)
.bot_Stop(botType, identifier)
.then(() => {
t.update('success', 'Bot stopped')
})
@@ -165,7 +165,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
})
} else if (status == 'Down') {
client
.bot_Restart(botType, name)
.bot_Restart(botType, identifier)
.then(() => {
t.update('success', 'Bot up')
})
@@ -175,11 +175,11 @@ const BotList: React.FC<IBotList> = ({ list }) => {
}
}
function deleteBot(name: string) {
function deleteBot(identifier: string) {
const t = new Toast('Deleting bot')
client
.bot_Delete(name)
.bot_Delete(identifier)
.then(() => {
t.update('success', 'Bot deleted')
})
@@ -211,10 +211,10 @@ const BotList: React.FC<IBotList> = ({ list }) => {
<h2 className="card-title text-sm">
{bot.ticker}
{getMoneyManagementBadge(bot.moneyManagement)}
{getIsForWatchingBadge(bot.isForWatchingOnly, bot.name)}
{getToggleBotStatusBadge(bot.status, bot.name, bot.botType)}
{getManualPositionBadge(bot.name)}
{getDeleteBadge(bot.name)}
{getIsForWatchingBadge(bot.isForWatchingOnly, bot.identifier)}
{getToggleBotStatusBadge(bot.status, bot.identifier, bot.botType)}
{getManualPositionBadge(bot.identifier)}
{getDeleteBadge(bot.identifier)}
</h2>
@@ -256,7 +256,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
<div className={baseBadgeClass(true)}>
PNL {bot.profitAndLoss.toFixed(2).toString()} $
</div>
{getTradesBadge(bot.name)}
{getTradesBadge(bot.identifier, bot.agentName)}
</div>
</div>
</div>
@@ -280,7 +280,8 @@ const BotList: React.FC<IBotList> = ({ list }) => {
/>
<TradesModal
showModal={showTradesModal}
botName={selectedBotForTrades}
strategyName={selectedBotForTrades?.identifier ?? null}
agentName={selectedBotForTrades?.agentName ?? null}
onClose={() => {
setShowTradesModal(false)
setSelectedBotForTrades(null)

View File

@@ -0,0 +1,101 @@
import {useState} from 'react'
import {useForm} from 'react-hook-form'
import {useQuery, useQueryClient} from '@tanstack/react-query'
import {UserClient} from '../../generated/ManagingApi'
import Modal from '../../components/mollecules/Modal/Modal'
import useApiUrlStore from '../../app/store/apiStore'
import {Toast} from '../../components/mollecules'
type UpdateAgentNameForm = {
agentName: string
}
function UserInfoSettings() {
const [showUpdateModal, setShowUpdateModal] = useState(false)
const queryClient = useQueryClient()
const { apiUrl } = useApiUrlStore()
const api = new UserClient({}, apiUrl)
const { data: user } = useQuery({
queryKey: ['user'],
queryFn: () => api.user_GetCurrentUser(),
})
const {
register,
handleSubmit,
formState: { errors },
} = useForm<UpdateAgentNameForm>()
const onSubmit = async (data: UpdateAgentNameForm) => {
const toast = new Toast('Updating agent name')
try {
await api.user_UpdateAgentName(data.agentName)
queryClient.invalidateQueries({ queryKey: ['user'] })
setShowUpdateModal(false)
toast.update('success', 'Agent name updated successfully')
} catch (error) {
console.error('Error updating agent name:', error)
toast.update('error', 'Failed to update agent name')
}
}
return (
<div className="container mx-auto p-4">
<div className="bg-base-200 rounded-lg p-6 shadow-lg">
<h2 className="text-2xl font-bold mb-4">User Information</h2>
<div className="space-y-4">
<div>
<label className="font-semibold">Name:</label>
<p>{user?.name}</p>
</div>
<div>
<label className="font-semibold">Agent Name:</label>
<p>{user?.agentName || 'Not set'}</p>
<button
className="btn btn-primary mt-2"
onClick={() => setShowUpdateModal(true)}
>
Update Agent Name
</button>
</div>
</div>
</div>
<Modal
showModal={showUpdateModal}
onClose={() => setShowUpdateModal(false)}
onSubmit={handleSubmit(onSubmit)}
titleHeader="Update Agent Name"
>
<div className="form-control w-full">
<label className="label">
<span className="label-text">Agent Name</span>
</label>
<input
type="text"
className="input input-bordered w-full"
{...register('agentName', { required: 'Agent name is required' })}
defaultValue={user?.agentName || ''}
/>
{errors.agentName && (
<label className="label">
<span className="label-text-alt text-error">
{errors.agentName.message}
</span>
</label>
)}
</div>
<div className="modal-action">
<button type="submit" className="btn">
Update
</button>
</div>
</Modal>
</div>
)
}
export default UserInfoSettings

View File

@@ -1,12 +1,13 @@
import { useState } from 'react'
import {useState} from 'react'
import { Tabs } from '../../components/mollecules'
import {Tabs} from '../../components/mollecules'
import AccountSettings from './account/accountSettings'
import HealthChecks from './healthchecks/healthChecks'
import MoneyManagementSettings from './moneymanagement/moneyManagement'
import Theme from './theme'
import DefaultConfig from './defaultConfig/defaultConfig'
import UserInfoSettings from './UserInfoSettings'
type TabsType = {
label: string
@@ -17,28 +18,33 @@ type TabsType = {
// Tabs Array
const tabs: TabsType = [
{
Component: MoneyManagementSettings,
Component: UserInfoSettings,
index: 1,
label: 'User Info',
},
{
Component: MoneyManagementSettings,
index: 2,
label: 'Money Management',
},
{
Component: AccountSettings,
index: 2,
index: 3,
label: 'Account Settings',
},
{
Component: Theme,
index: 3,
index: 4,
label: 'Theme',
},
{
Component: DefaultConfig,
index: 4,
index: 5,
label: 'Quick Start Config',
},
{
Component: HealthChecks,
index: 5,
index: 6,
label: 'Health Checks',
},
]