Compare commits
17 Commits
de7a4b6d52
...
38a3d4b564
Author | SHA1 | Date | |
---|---|---|---|
|
38a3d4b564 | ||
|
906043d52d | ||
26b0881a46 | |||
931042e716 | |||
5f59bbe18f | |||
4cb539d58a | |||
c852ef14dd | |||
3e8f3943dc | |||
4dadbec6e8 | |||
fbf3c06efd | |||
473a9a1993 | |||
30c711add4 | |||
b7fef53ad7 | |||
3c76c7dc09 | |||
4f02f56f85 | |||
0bc9aad4cf | |||
92fd933c60 |
17
api/authentication.ts
Normal file
17
api/authentication.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export interface AuthenticationInterface {
|
||||
key: string
|
||||
token(): string
|
||||
}
|
||||
|
||||
export class BearerAuthentication implements AuthenticationInterface {
|
||||
key = 'Authorization'
|
||||
_token: string
|
||||
|
||||
constructor(token: string) {
|
||||
this._token = token
|
||||
}
|
||||
|
||||
token(): string {
|
||||
return `Bearer ${this._token}`;
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
import {AuthenticationInterface} from "./authentication";
|
||||
|
||||
export enum Method {
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
@ -9,10 +11,6 @@ export let hasQuery = (method: Method) => {
|
||||
return Method.GET === method
|
||||
}
|
||||
|
||||
export interface ClientOptions {
|
||||
baseUrl: string
|
||||
}
|
||||
|
||||
export interface SchemaInterface {
|
||||
url: string,
|
||||
method: Method
|
||||
@ -25,12 +23,23 @@ export class Context {
|
||||
memoryKey?: string
|
||||
}
|
||||
|
||||
interface SchemaClientOptionsInterface {
|
||||
baseUrl?: string
|
||||
authentication?: AuthenticationInterface
|
||||
}
|
||||
|
||||
export class SchemaClient {
|
||||
baseUrl: string | null = null
|
||||
authentication: AuthenticationInterface | null = null
|
||||
context: Context | null = null
|
||||
|
||||
memory: Record<string, any> = {}
|
||||
|
||||
constructor(options: SchemaClientOptionsInterface) {
|
||||
this.baseUrl = options.baseUrl || null
|
||||
this.authentication = options.authentication || null
|
||||
}
|
||||
|
||||
debouncing(time: number)
|
||||
{
|
||||
let context = this.grabContext()
|
||||
@ -47,6 +56,10 @@ export class SchemaClient {
|
||||
}
|
||||
|
||||
async send(schema: SchemaInterface, data: any) {
|
||||
if (this.baseUrl === null) {
|
||||
throw new Error('Base url not defined!');
|
||||
}
|
||||
|
||||
let context = this.context
|
||||
this.context = null
|
||||
|
||||
@ -64,12 +77,22 @@ export class SchemaClient {
|
||||
preparedUrl += '?' + new URLSearchParams(preparedData)
|
||||
}
|
||||
|
||||
let token = ''
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
let tokenId = localStorage.getItem('token')
|
||||
token = localStorage.getItem(tokenId || '') || ''
|
||||
}
|
||||
|
||||
let headers: Record<string, any> = {
|
||||
...(schema.contentType ? {'Content-Type': schema.contentType} : {}),
|
||||
'X-Plugin-Token': 'passw0rd',
|
||||
}
|
||||
if (this.authentication) {
|
||||
headers[this.authentication.key] = this.authentication.token();
|
||||
}
|
||||
let response = await fetch(preparedUrl, {
|
||||
method: schema.method.toString(),
|
||||
headers: {
|
||||
...(schema.contentType ? {'Content-Type': schema.contentType} : {}),
|
||||
'X-Plugin-Token': 'passw0rd'
|
||||
},
|
||||
headers,
|
||||
body: hasQuery(schema.method) ? null : JSON.stringify(preparedData)
|
||||
});
|
||||
|
||||
|
3
api/sm/requests/approve-process.ts
Normal file
3
api/sm/requests/approve-process.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface ApproveProcessRequest {
|
||||
id: string
|
||||
}
|
3
api/sm/requests/unlock-process.ts
Normal file
3
api/sm/requests/unlock-process.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export interface UnlockProcessRequest {
|
||||
id: string
|
||||
}
|
@ -37,6 +37,8 @@ export interface ProcessInterface {
|
||||
canRepeat: boolean
|
||||
canStop: boolean
|
||||
canKill: boolean
|
||||
canApprove: boolean
|
||||
canUnlock: boolean
|
||||
}
|
||||
|
||||
export enum Status {
|
||||
|
10
api/sm/schemas/approve-process.ts
Normal file
10
api/sm/schemas/approve-process.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import {Method, SchemaInterface} from "../../schema-client";
|
||||
|
||||
class ApproveProcessSchema implements SchemaInterface {
|
||||
method = Method.POST
|
||||
url = '/system-monitoring/processes/{id}/approve'
|
||||
contentType = null
|
||||
}
|
||||
|
||||
export let approveProcessSchema = new ApproveProcessSchema()
|
||||
export default approveProcessSchema
|
10
api/sm/schemas/unlock-process.ts
Normal file
10
api/sm/schemas/unlock-process.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import {Method, SchemaInterface} from "../../schema-client";
|
||||
|
||||
class UnlockProcessSchema implements SchemaInterface {
|
||||
method = Method.POST
|
||||
url = '/system-monitoring/processes/{id}/unlock'
|
||||
contentType = null
|
||||
}
|
||||
|
||||
export let unlockProcessSchema = new UnlockProcessSchema()
|
||||
export default unlockProcessSchema
|
@ -8,7 +8,9 @@ import {CommandInterface} from "./responses/comamnds";
|
||||
import commandsSchema from "./schemas/commands";
|
||||
import runCommandsSchema from "./schemas/run-commands";
|
||||
import {RepeatProcessRequest} from "./requests/repeat-process";
|
||||
import {ApproveProcessRequest} from "./requests/approve-process";
|
||||
import repeatProcessSchema from "./schemas/repeat-process";
|
||||
import approveProcessSchema from "./schemas/approve-process";
|
||||
import processOutputSchema from "./schemas/process-output";
|
||||
import {ProcessOutputRequest} from "./requests/process-ouput";
|
||||
import playProcessSchema from "./schemas/play-process";
|
||||
@ -16,12 +18,10 @@ import pauseProcessSchema from "./schemas/pause-process";
|
||||
import stopProcessSchema from "./schemas/stop-process";
|
||||
import killProcessSchema from "./schemas/kill-process";
|
||||
import commandSchema from "./schemas/command";
|
||||
|
||||
let baseUrl = typeof location !== 'undefined' && location.origin.includes('.wallester.') ? location.origin : 'http://fmw.sipachev.sv'
|
||||
import {UnlockProcessRequest} from "./requests/unlock-process";
|
||||
import unlockProcessSchema from "./schemas/unlock-process";
|
||||
|
||||
export class SMClient extends SchemaClient {
|
||||
baseUrl = baseUrl
|
||||
|
||||
async getCommands(): Promise<ResponseInterface<CommandInterface[]>> {
|
||||
let { responseData, headers } = await this.send(commandsSchema, {})
|
||||
return {
|
||||
@ -56,6 +56,22 @@ export class SMClient extends SchemaClient {
|
||||
}
|
||||
}
|
||||
|
||||
async approveProcess(data: ApproveProcessRequest): Promise<ResponseInterface> {
|
||||
let { responseData, headers } = await this.send(approveProcessSchema, data)
|
||||
return {
|
||||
data: responseData as ProcessInterface[],
|
||||
headers: headers
|
||||
}
|
||||
}
|
||||
|
||||
async unlockProcess(data: UnlockProcessRequest): Promise<ResponseInterface> {
|
||||
let { responseData, headers } = await this.send(unlockProcessSchema, data)
|
||||
return {
|
||||
data: responseData as ProcessInterface[],
|
||||
headers: headers
|
||||
}
|
||||
}
|
||||
|
||||
async repeatProcess(data: RepeatProcessRequest): Promise<ResponseInterface> {
|
||||
let { responseData, headers } = await this.send(repeatProcessSchema, data)
|
||||
return {
|
||||
@ -104,6 +120,3 @@ export class SMClient extends SchemaClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export let smClient = new SMClient
|
||||
export default smClient
|
||||
|
@ -1,14 +1,13 @@
|
||||
import styles from './styles.module.css'
|
||||
import {Table, TableBody, TableCell, TableHead, TableRow, IconButton, Autocomplete, TextField} from "@mui/material";
|
||||
import {IconButton, Autocomplete, TextField} from "@mui/material";
|
||||
import Command from "./elements/command";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import PlayCircleOutline from '@mui/icons-material/PlayCircleOutline';
|
||||
import ConfirmDialog from "../confirm-dialog";
|
||||
import TabContext from "../../../context/tab";
|
||||
import {TabEnum} from "../../../pages";
|
||||
import smClient from "../../../api/sm/sm-client";
|
||||
import Send from "@mui/icons-material/Send"
|
||||
import {CommandInterface} from "../../../api/sm/responses/comamnds";
|
||||
import {useApi} from "../../../hooks/use-api";
|
||||
|
||||
export default function Commands() {
|
||||
const {setTab} = useContext(TabContext)
|
||||
@ -18,9 +17,10 @@ export default function Commands() {
|
||||
const [argumentList, setArgumentList] = useState<Record<string, any>>({});
|
||||
const [value, setValue] = useState<string | null>('');
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
const api = useApi()
|
||||
|
||||
let refreshCommands = async () => {
|
||||
const { data: commands } = await smClient.useMemory().getCommands()
|
||||
const { data: commands } = await api.useMemory().getCommands()
|
||||
setCommands(commands)
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ export default function Commands() {
|
||||
}, [value])
|
||||
|
||||
let variants = commands.map((command: CommandInterface, index: number) => command.name);
|
||||
variants.push('')
|
||||
|
||||
let callback = (name: string, optionList: Record<string, any>, argumentList: Record<string, any>) => {
|
||||
setOptionList(optionList)
|
||||
@ -48,7 +49,7 @@ export default function Commands() {
|
||||
return
|
||||
}
|
||||
lock = true
|
||||
await smClient.runCommand({
|
||||
await api.runCommand({
|
||||
commandName: selectedCommand.name,
|
||||
options: optionList,
|
||||
arguments: argumentList,
|
||||
|
@ -1,5 +1,5 @@
|
||||
import styles from './styles.module.css'
|
||||
import {useEffect, useState} from "react"
|
||||
import {useContext, useEffect, useState} from "react"
|
||||
import DeleteForeverOutlined from "@mui/icons-material/DeleteForeverOutlined"
|
||||
import StopCircleOutlined from "@mui/icons-material/StopCircleOutlined"
|
||||
import PauseCircleOutline from "@mui/icons-material/PauseCircleOutline"
|
||||
@ -10,6 +10,10 @@ import ErrorOutline from "@mui/icons-material/ErrorOutline"
|
||||
import ReplayOutlined from "@mui/icons-material/ReplayOutlined"
|
||||
import RunCircleOutlined from "@mui/icons-material/RunCircleOutlined"
|
||||
import FactCheckOutlined from "@mui/icons-material/FactCheckOutlined"
|
||||
import ThumbUpAltOutlined from '@mui/icons-material/ThumbUpAltOutlined'
|
||||
import LockOutlined from '@mui/icons-material/LockOutlined';
|
||||
import LockOpenOutlined from '@mui/icons-material/LockOpenOutlined';
|
||||
|
||||
import {
|
||||
Box,
|
||||
TextField,
|
||||
@ -25,11 +29,12 @@ import {
|
||||
TablePagination, Autocomplete
|
||||
} from "@mui/material"
|
||||
import ConfirmDialog from "../confirm-dialog";
|
||||
import smClient from "../../../api/sm/sm-client";
|
||||
import {ProcessInterface, Status} from "../../../api/sm/responses/processes";
|
||||
import Command from "../commands/elements/command";
|
||||
import {CommandInterface} from "../../../api/sm/responses/comamnds";
|
||||
import Grid from '@mui/material/Grid';
|
||||
import {useApi} from "../../../hooks/use-api";
|
||||
import Context from "../../../context/token";
|
||||
|
||||
enum Action {
|
||||
Run,
|
||||
@ -38,15 +43,18 @@ enum Action {
|
||||
Kill,
|
||||
Play,
|
||||
Pause,
|
||||
Approve,
|
||||
Unlock,
|
||||
}
|
||||
|
||||
export default function Processes() {
|
||||
const {token} = useContext(Context)
|
||||
const [processes, setProcesses] = useState<ProcessInterface[]>([]);
|
||||
const [commands, setCommands] = useState<CommandInterface[]>([]);
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [count, setCount] = useState<number>(0);
|
||||
const [type, setType] = useState<string | null>('');
|
||||
const [name, setName] = useState<string | null>('');
|
||||
const [type, setType] = useState<string>('');
|
||||
const [name, setName] = useState<string>('');
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [modalLoading, setModalLoading] = useState<boolean>(true);
|
||||
const [action, setAction] = useState<Action | null>(null);
|
||||
@ -54,11 +62,13 @@ export default function Processes() {
|
||||
const [selectedProcess, setSelectedProcess] = useState<ProcessInterface | null>(null);
|
||||
const [optionList, setOptionList] = useState<Record<string, any>>({});
|
||||
const [argumentList, setArgumentList] = useState<Record<string, any>>({});
|
||||
const api = useApi()
|
||||
|
||||
let variants = commands.map((command: CommandInterface, index: number) => command.name);
|
||||
variants.push('')
|
||||
|
||||
let refreshCommands = async () => {
|
||||
const { data: commands } = await smClient.useMemory().getCommands()
|
||||
const { data: commands } = await api.useMemory().getCommands()
|
||||
setCommands(commands)
|
||||
}
|
||||
|
||||
@ -80,7 +90,7 @@ export default function Processes() {
|
||||
if (!!name) {
|
||||
data['name'] = name
|
||||
}
|
||||
const { data: processes, headers } = await smClient.getProcesses({
|
||||
const { data: processes, headers } = await api.getProcesses({
|
||||
...data,
|
||||
page: page + 1,
|
||||
limit: 20,
|
||||
@ -91,7 +101,7 @@ export default function Processes() {
|
||||
refreshLock = false
|
||||
}
|
||||
let output = async (process: ProcessInterface) => {
|
||||
const { data: output } = await smClient.getProcessOutput({
|
||||
const { data: output } = await api.getProcessOutput({
|
||||
id: process.id
|
||||
})
|
||||
|
||||
@ -115,6 +125,12 @@ export default function Processes() {
|
||||
}, [action]);
|
||||
|
||||
let isFinished = (process: ProcessInterface) => process.cancelledAt || process.completedAt
|
||||
let isCancelled = (process: ProcessInterface) => process.cancelledAt
|
||||
let isRunning = (process: ProcessInterface) => process.startedAt && !isFinished(process)
|
||||
|
||||
const processName = (process: ProcessInterface) => {
|
||||
return process.name + ' ' + JSON.stringify(process.options)+ ' ' + JSON.stringify(process.arguments)
|
||||
}
|
||||
|
||||
const handleChangePage = (event: any, page: number) => {
|
||||
setPage(page);
|
||||
@ -136,7 +152,7 @@ export default function Processes() {
|
||||
lock = true
|
||||
|
||||
if (action === Action.Run) {
|
||||
await smClient.runCommand({
|
||||
await api.runCommand({
|
||||
commandName: selectedProcess.name,
|
||||
options: optionList,
|
||||
arguments: argumentList,
|
||||
@ -144,8 +160,20 @@ export default function Processes() {
|
||||
})
|
||||
}
|
||||
|
||||
if (action === Action.Approve) {
|
||||
await api.approveProcess({
|
||||
id: selectedProcess.id,
|
||||
})
|
||||
}
|
||||
|
||||
if (action === Action.Unlock) {
|
||||
await api.unlockProcess({
|
||||
id: selectedProcess.id,
|
||||
})
|
||||
}
|
||||
|
||||
if (action === Action.Repeat) {
|
||||
await smClient.repeatProcess({
|
||||
await api.repeatProcess({
|
||||
id: selectedProcess.id,
|
||||
requestId: dialogId
|
||||
})
|
||||
@ -176,10 +204,11 @@ export default function Processes() {
|
||||
<Autocomplete
|
||||
value={type}
|
||||
onChange={(event: any, newValue: string | null) => {
|
||||
setType(newValue);
|
||||
setType(newValue || '');
|
||||
setLoading(true)
|
||||
}}
|
||||
disablePortal
|
||||
options={['None', "Running", "History"]}
|
||||
options={['None', "Running", "History", "Locked", 'Need_Approve', '']}
|
||||
renderInput={(params) => <TextField {...params} label="Type"/>}
|
||||
/>
|
||||
</Grid>
|
||||
@ -189,6 +218,7 @@ export default function Processes() {
|
||||
value={name}
|
||||
onChange={(event: any, newValue: string | null) => {
|
||||
setName(newValue || '');
|
||||
setLoading(true)
|
||||
}}
|
||||
disablePortal
|
||||
options={variants}
|
||||
@ -210,14 +240,16 @@ export default function Processes() {
|
||||
<TableBody>
|
||||
{processes.map((process: ProcessInterface, index: number) => (
|
||||
<TableRow key={process.id}>
|
||||
<TableCell>{process.name}</TableCell>
|
||||
<TableCell title={processName(process)}>{process.name}</TableCell>
|
||||
<TableCell>
|
||||
{!process.progress && !isFinished(process) && <LinearProgress/>}
|
||||
{!process.progress && isFinished(process) && <LinearProgress variant="determinate" value={100}/>}
|
||||
{!process.progress && !isFinished(process) && isRunning(process) && <LinearProgress/>}
|
||||
{!process.progress && !isFinished(process) && !isRunning(process) && <LinearProgress variant="determinate" value={0}/>}
|
||||
{!process.progress && isFinished(process) && !isCancelled(process) && <LinearProgress variant="determinate" value={100}/>}
|
||||
{!process.progress && isFinished(process) && isCancelled(process) && <LinearProgress variant="determinate" value={0}/>}
|
||||
{process.progress && <LinearProgress variant="determinate" value={process.progress.percent}/>}
|
||||
{process.progress && <span>
|
||||
{`${process.progress.progress}`} / {`${process.progress.total}`} - {process.progress.percent}% [{process.progress.memory}] / {process.progress.remaining}
|
||||
</span>}
|
||||
{`${process.progress.progress}`} / {`${process.progress.total}`} - {process.progress.percent}% [{process.progress.memory}] / {process.progress.remaining}
|
||||
</span>}
|
||||
{process.canPlay && <IconButton onClick={() => openDialog(process, Action.Play)} title={`Play`} aria-label="Play">
|
||||
<PlayCircleOutline/>
|
||||
</IconButton>}
|
||||
@ -244,12 +276,21 @@ export default function Processes() {
|
||||
{Status.Wait === process.status && <IconButton title={`Wait`} aria-label="Wait">
|
||||
<HourglassEmptyOutlined/>
|
||||
</IconButton>}
|
||||
{process.lock && <IconButton title={`Locked`} aria-label="Locked">
|
||||
<LockOutlined/>
|
||||
</IconButton>}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{process.canRepeat && <IconButton onClick={() => openDialog(process, Action.Repeat)} title={`Repeat`} aria-label="Repeat">
|
||||
{process.canUnlock && token && token?.permissions.indexOf('unlock_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Unlock)} title={`Unlock`} aria-label="Unlock">
|
||||
<LockOpenOutlined/>
|
||||
</IconButton>}
|
||||
{process.canApprove && token && token?.permissions.indexOf('approve_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Approve)} title={`Approve`} aria-label="Approve">
|
||||
<ThumbUpAltOutlined/>
|
||||
</IconButton>}
|
||||
{process.canRepeat && token && token?.permissions.indexOf('run_command') > -1 && <IconButton onClick={() => openDialog(process, Action.Repeat)} title={`Repeat`} aria-label="Repeat">
|
||||
<ReplayOutlined/>
|
||||
</IconButton>}
|
||||
{process.canKill && <IconButton onClick={() => openDialog(process, Action.Kill)} title={`Kill`} aria-label="Kill">
|
||||
{process.canKill && token && token?.permissions.indexOf('kill_process') > -1 && <IconButton onClick={() => openDialog(process, Action.Kill)} title={`Kill`} aria-label="Kill">
|
||||
<DeleteForeverOutlined/>
|
||||
</IconButton>}
|
||||
{process?.outputId && <IconButton title={`Output`} onClick={() => output(process)} aria-label="Output">
|
||||
@ -257,7 +298,7 @@ export default function Processes() {
|
||||
</IconButton>}
|
||||
</TableCell>
|
||||
<TableCell title={new Date(process.createdAt).toLocaleString()}>
|
||||
{new Date(process.createdAt).toLocaleDateString()}
|
||||
{new Date(process.createdAt).toLocaleString()}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
@ -268,6 +309,7 @@ export default function Processes() {
|
||||
component="div"
|
||||
count={count}
|
||||
rowsPerPage={20}
|
||||
rowsPerPageOptions={[10, 20, 50, 100]}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
/>
|
||||
@ -278,7 +320,7 @@ export default function Processes() {
|
||||
modifyCallback={async () => {
|
||||
setAction(Action.Run)
|
||||
setModalLoading(true)
|
||||
let {data: command} = await smClient.getCommand(selectedProcess.name)
|
||||
let {data: command} = await api.getCommand(selectedProcess.name)
|
||||
setCommand(command)
|
||||
setModalLoading(false)
|
||||
}}
|
||||
@ -299,14 +341,28 @@ export default function Processes() {
|
||||
argumentsParams={selectedProcess.arguments}
|
||||
callback={callback}/>}
|
||||
</ConfirmDialog>}
|
||||
{selectedProcess && <ConfirmDialog
|
||||
title={selectedProcess.name}
|
||||
open={Action.Approve === action}
|
||||
agreeCallback={agreeCallback}
|
||||
closeCallback={() => {setAction(null)}}>
|
||||
Approve?
|
||||
</ConfirmDialog>}
|
||||
{selectedProcess && <ConfirmDialog
|
||||
title={selectedProcess.name}
|
||||
open={Action.Unlock === action}
|
||||
agreeCallback={agreeCallback}
|
||||
closeCallback={() => {setAction(null)}}>
|
||||
Unlock?
|
||||
</ConfirmDialog>}
|
||||
{selectedProcess && <ConfirmDialog
|
||||
title={`Are you sure?`}
|
||||
open={ !!action && ([Action.Play, Action.Pause, Action.Stop, Action.Kill].indexOf(action) > -1) }
|
||||
agreeCallback={async () => {
|
||||
Action.Play === action && await smClient.playProcess(selectedProcess.id)
|
||||
Action.Pause === action && await smClient.pauseProcess(selectedProcess.id)
|
||||
Action.Stop === action && await smClient.stopProcess(selectedProcess.id)
|
||||
Action.Kill === action && await smClient.killProcess(selectedProcess.id)
|
||||
Action.Play === action && await api.playProcess(selectedProcess.id)
|
||||
Action.Pause === action && await api.pauseProcess(selectedProcess.id)
|
||||
Action.Stop === action && await api.stopProcess(selectedProcess.id)
|
||||
Action.Kill === action && await api.killProcess(selectedProcess.id)
|
||||
setAction(null)
|
||||
}}
|
||||
closeCallback={() => {setAction(null)}}>
|
||||
|
116
components/elements/tokens/index.tsx
Normal file
116
components/elements/tokens/index.tsx
Normal file
@ -0,0 +1,116 @@
|
||||
import styles from './styles.module.css'
|
||||
import {Autocomplete, IconButton, TextareaAutosize, TextField} from "@mui/material";
|
||||
import {useContext, useEffect, useState} from "react";
|
||||
import Context from "../../../context/token";
|
||||
import AddCircleOutlineOutlinedIcon from '@mui/icons-material/AddCircleOutlineOutlined';
|
||||
import DeleteForeverOutlined from '@mui/icons-material/DeleteForeverOutlined';
|
||||
import Modal from '@mui/material/Modal';
|
||||
import Fade from '@mui/material/Fade';
|
||||
import Backdrop from '@mui/material/Backdrop';
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from "@mui/material/Grid";
|
||||
import ConfirmDialog from "../confirm-dialog";
|
||||
|
||||
const style = {
|
||||
position: 'absolute' as 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: '100%',
|
||||
bgcolor: 'background.paper',
|
||||
border: '2px solid #000',
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
};
|
||||
|
||||
export default function Tokens() {
|
||||
const {token, tokens, addToken, deleteToken, switchToken} = useContext(Context)
|
||||
const [showAddForm, setShowAddForm] = useState(false)
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
|
||||
let variants = tokens.map((token, index) => ({
|
||||
...token,
|
||||
label: `${token.host} [${token.id}]`
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
setShowAddForm(!token)
|
||||
}, [token])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid container>
|
||||
<Grid item xs={2}>
|
||||
<Autocomplete
|
||||
freeSolo
|
||||
value={{
|
||||
...token,
|
||||
label: token?.host ? `${token.host} [${token.id}]` : ''
|
||||
}}
|
||||
onChange={(event: any, newValue: any) => {
|
||||
console.log('newValue', newValue);
|
||||
if (newValue?.id) {
|
||||
switchToken(newValue?.id)
|
||||
}
|
||||
}}
|
||||
disablePortal
|
||||
options={variants}
|
||||
renderInput={(params) => <TextField {...params} label="Tokens"/>}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={1}>
|
||||
<IconButton onClick={() => setShowAddForm(true)} title={`Add new token`}
|
||||
aria-label="Add new token">
|
||||
<AddCircleOutlineOutlinedIcon/>
|
||||
</IconButton>
|
||||
<IconButton onClick={() => setShowConfirm(true)} title={`Delete current token`}
|
||||
aria-label="Add new token">
|
||||
<DeleteForeverOutlined/>
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Modal
|
||||
aria-labelledby="transition-modal-title"
|
||||
aria-describedby="transition-modal-description"
|
||||
open={showAddForm}
|
||||
onClose={() => setShowAddForm(false)}
|
||||
closeAfterTransition
|
||||
slots={{backdrop: Backdrop}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
timeout: 500,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Fade in={showAddForm}>
|
||||
<Box sx={style}>
|
||||
<TextareaAutosize
|
||||
placeholder={`Pass JWT token`}
|
||||
minRows={25}
|
||||
style={{width: '100%'}}
|
||||
onInput={(event: any) => {
|
||||
if (addToken(event.target.value)) {
|
||||
setShowAddForm(false)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Fade>
|
||||
</Modal>
|
||||
<ConfirmDialog
|
||||
title={`Delete curent token`}
|
||||
open={showConfirm}
|
||||
agreeCallback={async () => {
|
||||
let tokenId = token?.id || null
|
||||
if (tokenId) {
|
||||
deleteToken(tokenId)
|
||||
}
|
||||
setShowConfirm(false)
|
||||
}}
|
||||
|
||||
closeCallback={() => {setShowConfirm(false)}}>
|
||||
Confirm?
|
||||
</ConfirmDialog>
|
||||
</>
|
||||
)
|
||||
}
|
0
components/elements/tokens/styles.module.css
Normal file
0
components/elements/tokens/styles.module.css
Normal file
6
context/token.ts
Normal file
6
context/token.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
import {UseTokenInterface} from "../hooks/use-token"
|
||||
|
||||
const Context = React.createContext({} as UseTokenInterface)
|
||||
export const Provider = Context.Provider
|
||||
export default Context
|
130
functions/token.ts
Normal file
130
functions/token.ts
Normal file
@ -0,0 +1,130 @@
|
||||
|
||||
|
||||
export interface TokenData<T=any> {
|
||||
payload: T
|
||||
headers: Record<string, any>
|
||||
}
|
||||
|
||||
const isClient = () => {
|
||||
return typeof window !== 'undefined'
|
||||
}
|
||||
|
||||
export const storeToken = (token: string) => {
|
||||
if (!isClient()) {
|
||||
return;
|
||||
}
|
||||
let tokenData = grabTokenData(token)
|
||||
let tokenId = tokenData?.payload?.id;
|
||||
if (!tokenId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let tokens: string | null = window.localStorage.getItem('tokens')
|
||||
let tokensData: string[] = JSON.parse(tokens || '[]');
|
||||
if (!tokensData) {
|
||||
return
|
||||
}
|
||||
window.localStorage.setItem('token', tokenId)
|
||||
window.localStorage.setItem(tokenId, token)
|
||||
tokensData.push(tokenId)
|
||||
window.localStorage.setItem('tokens', JSON.stringify(tokensData))
|
||||
}
|
||||
|
||||
export const cleanToken = (id: string) => {
|
||||
if (!isClient()) {
|
||||
return;
|
||||
}
|
||||
let tokens: string | null = window.localStorage.getItem('tokens')
|
||||
let tokensData: string[] = JSON.parse(tokens || '[]');
|
||||
if (!tokensData) {
|
||||
return
|
||||
}
|
||||
|
||||
tokensData = tokensData.filter((storedTokenId: string) => storedTokenId !== id)
|
||||
window.localStorage.setItem('tokens', JSON.stringify(tokensData))
|
||||
window.localStorage.removeItem(id)
|
||||
|
||||
// Чистим текущий токен
|
||||
let activeTokenId: string | null = window.localStorage.getItem('token')
|
||||
if (id === activeTokenId) {
|
||||
window.localStorage.removeItem('token')
|
||||
}
|
||||
}
|
||||
|
||||
export const selectToken = (id: string) => {
|
||||
if (!isClient()) {
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem('token', id)
|
||||
}
|
||||
|
||||
export const grabTokenData = (token: string): TokenData|null => {
|
||||
if (!isClient()) {
|
||||
return null
|
||||
}
|
||||
|
||||
const parts = token.split('.')
|
||||
if (parts.length !== 3) {
|
||||
return null
|
||||
}
|
||||
|
||||
let payload = JSON.parse(window.atob(parts[1]))
|
||||
if (!payload){
|
||||
return null
|
||||
}
|
||||
|
||||
let headers = JSON.parse(window.atob(parts[0]))
|
||||
if (!headers){
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
payload,
|
||||
headers,
|
||||
};
|
||||
}
|
||||
|
||||
export const grabRawToken = (): string | null => {
|
||||
if (!isClient()) {
|
||||
return null
|
||||
}
|
||||
const tokenId = window.localStorage.getItem('token')
|
||||
if (!tokenId) {
|
||||
return null
|
||||
}
|
||||
return window.localStorage.getItem(tokenId)
|
||||
}
|
||||
|
||||
export const grabToken = (): TokenData | null => {
|
||||
const token = grabRawToken()
|
||||
if (!token) {
|
||||
return null
|
||||
}
|
||||
return grabTokenData(token);
|
||||
}
|
||||
|
||||
export const grabTokens = (): TokenData[] => {
|
||||
if (!isClient()) {
|
||||
return []
|
||||
}
|
||||
const storedTokens = window.localStorage.getItem('tokens')
|
||||
let tokens: string[] = JSON.parse(storedTokens || '[]');
|
||||
if (!tokens) {
|
||||
return []
|
||||
}
|
||||
|
||||
let result: TokenData[] = []
|
||||
|
||||
tokens.forEach((tokenId) => {
|
||||
let token = window.localStorage.getItem(tokenId)
|
||||
if (!token) {
|
||||
return true
|
||||
}
|
||||
let tokenData = grabTokenData(token)
|
||||
if (tokenData) {
|
||||
result.push(tokenData)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
30
hooks/use-api.ts
Normal file
30
hooks/use-api.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {useContext, useEffect, useState} from 'react'
|
||||
import {SMClient} from "../api/sm/sm-client";
|
||||
import Context from "../context/token";
|
||||
import {Token} from "./use-token";
|
||||
import {BearerAuthentication} from "../api/authentication";
|
||||
import {grabRawToken} from "../functions/token";
|
||||
|
||||
const grabClient = (token: Token | null, rawToken: string | null): SMClient => {
|
||||
let authentication = {}
|
||||
if (rawToken) {
|
||||
authentication = {
|
||||
authentication: new BearerAuthentication(rawToken)
|
||||
}
|
||||
}
|
||||
return new SMClient({
|
||||
baseUrl: token?.host || 'localhost',
|
||||
...authentication
|
||||
})
|
||||
}
|
||||
|
||||
export function useApi(): SMClient {
|
||||
let {token} = useContext(Context)
|
||||
let [client, setClient] = useState<SMClient>(grabClient(token, grabRawToken()))
|
||||
|
||||
useEffect(() => {
|
||||
setClient(grabClient(token, grabRawToken()))
|
||||
}, [token])
|
||||
|
||||
return client
|
||||
}
|
57
hooks/use-token.ts
Normal file
57
hooks/use-token.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import {useState, useEffect} from 'react'
|
||||
import {cleanToken, grabToken, grabTokenData, grabTokens, selectToken, storeToken} from "../functions/token";
|
||||
|
||||
export class Token {
|
||||
id!: string
|
||||
host!: string
|
||||
permissions!: string[]
|
||||
}
|
||||
|
||||
export interface UseTokenInterface {
|
||||
token: Token|null
|
||||
tokens: Token[]
|
||||
switchToken(tokenId: string): void
|
||||
deleteToken(tokenId: string): void
|
||||
addToken(tokenId: string): boolean
|
||||
}
|
||||
|
||||
export function useToken(): UseTokenInterface {
|
||||
const [token, setToken] = useState<Token|null>(null);
|
||||
const [tokens, setTokens] = useState<Token[]>([]);
|
||||
useEffect(() => {
|
||||
setToken(grabToken()?.payload)
|
||||
|
||||
let tokens = grabTokens().map((tokenData) => tokenData.payload as Token)
|
||||
setTokens(tokens)
|
||||
|
||||
return () => {
|
||||
|
||||
}
|
||||
}, [])
|
||||
|
||||
const switchToken = (tokenId: string) => {
|
||||
selectToken(tokenId)
|
||||
setToken(grabToken()?.payload)
|
||||
}
|
||||
|
||||
const deleteToken = (tokenId: string) => {
|
||||
cleanToken(tokenId)
|
||||
setToken(grabToken()?.payload)
|
||||
}
|
||||
|
||||
const addToken = (token: string): boolean => {
|
||||
if (!grabTokenData(token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
storeToken(token)
|
||||
setToken(grabToken()?.payload)
|
||||
setTokens([
|
||||
...tokens,
|
||||
grabToken()?.payload
|
||||
])
|
||||
return true
|
||||
}
|
||||
|
||||
return {token, tokens, switchToken, deleteToken, addToken}
|
||||
}
|
@ -1,6 +1,13 @@
|
||||
import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
import {Provider} from "../context/token";
|
||||
import {useToken} from "../hooks/use-token";
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
let {token, tokens, switchToken, deleteToken, addToken} = useToken()
|
||||
return (
|
||||
<Provider value={{token, tokens, switchToken, deleteToken, addToken}}>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
)
|
||||
}
|
||||
|
@ -2,9 +2,12 @@ import Head from 'next/head'
|
||||
import styles from '../styles/Home.module.css'
|
||||
import Processes from "../components/elements/processes";
|
||||
import {Tabs, Tab} from '@mui/material';
|
||||
import {useState} from "react";
|
||||
import {useContext, useState} from "react";
|
||||
import Commands from "../components/elements/commands";
|
||||
import {Provider as TabProvider} from '../context/tab'
|
||||
import Context from "../context/token";
|
||||
import Tokens from "../components/elements/tokens";
|
||||
|
||||
|
||||
export enum TabEnum {
|
||||
Commands,
|
||||
@ -12,8 +15,10 @@ export enum TabEnum {
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
let {token} = useContext(Context)
|
||||
const [tab, setTab] = useState<TabEnum>(TabEnum.Processes);
|
||||
const handleChange = (event: any, tab: number) => setTab(tab)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
@ -23,21 +28,24 @@ export default function Home() {
|
||||
<link rel="icon" href="/favicon.ico"/>
|
||||
</Head>
|
||||
<main className={styles.main}>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={handleChange}
|
||||
variant="scrollable"
|
||||
scrollButtons
|
||||
allowScrollButtonsMobile
|
||||
aria-label="scrollable force tabs example"
|
||||
>
|
||||
<Tab value={TabEnum.Commands} label="Commands" />
|
||||
<Tab value={TabEnum.Processes} label="Processes" />
|
||||
</Tabs>
|
||||
<TabProvider value={{tab, setTab}}>
|
||||
{tab === TabEnum.Commands && <Commands />}
|
||||
{tab === TabEnum.Processes && <Processes />}
|
||||
</TabProvider>
|
||||
<Tokens />
|
||||
{token && <>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={handleChange}
|
||||
variant="scrollable"
|
||||
scrollButtons
|
||||
allowScrollButtonsMobile
|
||||
aria-label="scrollable force tabs example"
|
||||
>
|
||||
<Tab value={TabEnum.Commands} label="Commands" />
|
||||
<Tab value={TabEnum.Processes} label="Processes" />
|
||||
</Tabs>
|
||||
<TabProvider value={{tab, setTab}}>
|
||||
{tab === TabEnum.Commands && <Commands />}
|
||||
{tab === TabEnum.Processes && <Processes />}
|
||||
</TabProvider>
|
||||
</>}
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>404: This page could not be found</title><meta name="next-head-count" content="3"/><link rel="preload" href="/_next/static/css/961153d3a9f88130.css" as="style"/><link rel="stylesheet" href="/_next/static/css/961153d3a9f88130.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js"></script><script src="/_next/static/chunks/webpack-0b5d8249fb15f5f3.js" defer=""></script><script src="/_next/static/chunks/framework-114634acb84f8baa.js" defer=""></script><script src="/_next/static/chunks/main-66ca454f7bdf962f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-891652dd44e1e4e1.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8353112a01355ec2.js" defer=""></script><script src="/_next/static/J4k37v5qNO58v9nWL4ohq/_buildManifest.js" defer=""></script><script src="/_next/static/J4k37v5qNO58v9nWL4ohq/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>
|
||||
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><title>404: This page could not be found</title><meta name="next-head-count" content="3"/><link rel="preload" href="/_next/static/css/961153d3a9f88130.css" as="style"/><link rel="stylesheet" href="/_next/static/css/961153d3a9f88130.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js"></script><script src="/_next/static/chunks/webpack-0b5d8249fb15f5f3.js" defer=""></script><script src="/_next/static/chunks/framework-114634acb84f8baa.js" defer=""></script><script src="/_next/static/chunks/main-66ca454f7bdf962f.js" defer=""></script><script src="/_next/static/chunks/pages/_app-526701f4ebc801df.js" defer=""></script><script src="/_next/static/chunks/pages/_error-8353112a01355ec2.js" defer=""></script><script src="/_next/static/InkGKW3c-wb1BcW3qzzDZ/_buildManifest.js" defer=""></script><script src="/_next/static/InkGKW3c-wb1BcW3qzzDZ/_ssgManifest.js" defer=""></script></head><body><div id="__next"><div style="font-family:-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif;height:100vh;text-align:center;display:flex;flex-direction:column;align-items:center;justify-content:center"><div><style>
|
||||
body { margin: 0; color: #000; background: #fff; }
|
||||
.next-error-h1 {
|
||||
border-right: 1px solid rgba(0, 0, 0, .3);
|
||||
@ -9,4 +9,4 @@
|
||||
.next-error-h1 {
|
||||
border-right: 1px solid rgba(255, 255, 255, .3);
|
||||
}
|
||||
}</style><h1 class="next-error-h1" style="display:inline-block;margin:0;margin-right:20px;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block;text-align:left;line-height:49px;height:49px;vertical-align:middle"><h2 style="font-size:14px;font-weight:normal;line-height:49px;margin:0;padding:0">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"J4k37v5qNO58v9nWL4ohq","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
||||
}</style><h1 class="next-error-h1" style="display:inline-block;margin:0;margin-right:20px;padding:0 23px 0 0;font-size:24px;font-weight:500;vertical-align:top;line-height:49px">404</h1><div style="display:inline-block;text-align:left;line-height:49px;height:49px;vertical-align:middle"><h2 style="font-size:14px;font-weight:normal;line-height:49px;margin:0;padding:0">This page could not be found<!-- -->.</h2></div></div></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":404}},"page":"/_error","query":{},"buildId":"InkGKW3c-wb1BcW3qzzDZ","nextExport":true,"isFallback":false,"gip":true,"scriptLoader":[]}</script></body></html>
|
@ -0,0 +1 @@
|
||||
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/317-7a9f5b1fcf3cfbe5.js","static/css/758d1a1ad3b56ec4.css","static/chunks/pages/index-e05dda18f04fa62a.js"],"/_error":["static/chunks/pages/_error-8353112a01355ec2.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
@ -1 +0,0 @@
|
||||
self.__BUILD_MANIFEST={__rewrites:{beforeFiles:[],afterFiles:[],fallback:[]},"/":["static/chunks/556-56e85e4880bbe04b.js","static/css/758d1a1ad3b56ec4.css","static/chunks/pages/index-84dfafb8f3c7c7ce.js"],"/_error":["static/chunks/pages/_error-8353112a01355ec2.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|
166
plugin/popup/_next/static/chunks/317-7a9f5b1fcf3cfbe5.js
Normal file
166
plugin/popup/_next/static/chunks/317-7a9f5b1fcf3cfbe5.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{6840:function(t,e,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return n(2595)}])},7889:function(t,e,n){"use strict";n.d(e,{z:function(){return l}});var r=n(7294);let o=r.createContext({}),l=o.Provider;e.Z=o},4085:function(t,e,n){"use strict";n.d(e,{DR:function(){return c},F8:function(){return d},Y0:function(){return l},gn:function(){return a},mB:function(){return o},rK:function(){return u},se:function(){return i}});let r=()=>!0,o=t=>{var e;if(!r())return;let n=i(t),o=null==n?void 0:null===(e=n.payload)||void 0===e?void 0:e.id;if(!o)return;let l=JSON.parse(window.localStorage.getItem("tokens")||"[]");l&&(window.localStorage.setItem("token",o),window.localStorage.setItem(o,t),l.push(o),window.localStorage.setItem("tokens",JSON.stringify(l)))},l=t=>{if(!r())return;let e=JSON.parse(window.localStorage.getItem("tokens")||"[]");e&&(e=e.filter(e=>e!==t),window.localStorage.setItem("tokens",JSON.stringify(e)),window.localStorage.removeItem(t),t===window.localStorage.getItem("token")&&window.localStorage.removeItem("token"))},u=t=>{r()&&window.localStorage.setItem("token",t)},i=t=>{if(!r())return null;let e=t.split(".");if(3!==e.length)return null;let n=JSON.parse(window.atob(e[1]));if(!n)return null;let o=JSON.parse(window.atob(e[0]));return o?{payload:n,headers:o}:null},a=()=>{if(!r())return null;let t=window.localStorage.getItem("token");return t?window.localStorage.getItem(t):null},d=()=>{let t=a();return t?i(t):null},c=()=>{if(!r())return[];let t=window.localStorage.getItem("tokens"),e=JSON.parse(t||"[]");if(!e)return[];let n=[];return e.forEach(t=>{let e=window.localStorage.getItem(t);if(!e)return!0;let r=i(e);r&&n.push(r)}),n}},2595:function(t,e,n){"use strict";n.r(e),n.d(e,{default:function(){return i}});var r=n(5893);n(4744);var o=n(7889),l=n(7294),u=n(4085);function i(t){let{Component:e,pageProps:n}=t,{token:i,tokens:a,switchToken:d,deleteToken:c,addToken:f}=function(){let[t,e]=(0,l.useState)(null),[n,r]=(0,l.useState)([]);(0,l.useEffect)(()=>{var t;return e(null===(t=(0,u.F8)())||void 0===t?void 0:t.payload),r((0,u.DR)().map(t=>t.payload)),()=>{}},[]);let o=t=>{var n;(0,u.rK)(t),e(null===(n=(0,u.F8)())||void 0===n?void 0:n.payload)},i=t=>{var n;(0,u.Y0)(t),e(null===(n=(0,u.F8)())||void 0===n?void 0:n.payload)},a=t=>{var o,l;return!!(0,u.se)(t)&&((0,u.mB)(t),e(null===(o=(0,u.F8)())||void 0===o?void 0:o.payload),r([...n,null===(l=(0,u.F8)())||void 0===l?void 0:l.payload]),!0)};return{token:t,tokens:n,switchToken:o,deleteToken:i,addToken:a}}();return(0,r.jsx)(o.z,{value:{token:i,tokens:a,switchToken:d,deleteToken:c,addToken:f},children:(0,r.jsx)(e,{...n})})}},4744:function(){}},function(t){var e=function(e){return t(t.s=e)};t.O(0,[774,179],function(){return e(6840),e(880)}),_N_E=t.O()}]);
|
@ -1 +0,0 @@
|
||||
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{6840:function(n,u,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return t(3847)}])},3847:function(n,u,t){"use strict";t.r(u),t.d(u,{default:function(){return _}});var r=t(5893);function _(n){let{Component:u,pageProps:t}=n;return(0,r.jsx)(u,{...t})}t(4744)},4744:function(){}},function(n){var u=function(u){return n(n.s=u)};n.O(0,[774,179],function(){return u(6840),u(880)}),_N_E=n.O()}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user