wip: bucket list
This commit is contained in:
parent
3c8c559ac7
commit
9079a82434
26
frontend/package-lock.json
generated
26
frontend/package-lock.json
generated
@ -11,12 +11,14 @@
|
||||
"@fluentui/react-components": "^9.54.16",
|
||||
"@fluentui/react-icons": "^2.0.258",
|
||||
"jotai": "^2.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"zustand": "^5.0.0-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^2.0.1",
|
||||
@ -2372,6 +2374,13 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.13",
|
||||
"resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz",
|
||||
@ -3060,6 +3069,12 @@
|
||||
"integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
@ -5245,6 +5260,12 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.13",
|
||||
"resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.13.tgz",
|
||||
@ -5629,6 +5650,11 @@
|
||||
"resolved": "https://registry.npmmirror.com/keyborg/-/keyborg-2.6.0.tgz",
|
||||
"integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA=="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
|
@ -12,12 +12,14 @@
|
||||
"@fluentui/react-components": "^9.54.16",
|
||||
"@fluentui/react-icons": "^2.0.258",
|
||||
"jotai": "^2.10.0",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.26.2",
|
||||
"zustand": "^5.0.0-rc.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.10",
|
||||
"@types/react": "^18.0.17",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@vitejs/plugin-react": "^2.0.1",
|
||||
|
@ -1 +1 @@
|
||||
f23304e575da740e9b738508a43df31e
|
||||
b20ef5a27687e07e09878451f9a2e1aa
|
@ -25,11 +25,15 @@ const useActionStyle = makeStyles({
|
||||
test: {}
|
||||
});
|
||||
|
||||
export function ConnectionCreate(){
|
||||
export interface ConnectionCreateProps {
|
||||
openFn: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export function ConnectionCreate(props: ConnectionCreateProps) {
|
||||
const actionStyle = useActionStyle();
|
||||
const {dispatchMessage} = useToast();
|
||||
const [testLoading, setTestLoading] = useState<"initial" | "loading" | "success" | "error">("initial");
|
||||
const {conn_list, get_conn} = useStoreConnection();
|
||||
const {conn_get} = useStoreConnection();
|
||||
const buttonIcon =
|
||||
testLoading === "loading" ? (
|
||||
<Spinner size="tiny"/>
|
||||
@ -54,12 +58,19 @@ export function ConnectionCreate(){
|
||||
}
|
||||
|
||||
async function create() {
|
||||
// self
|
||||
// qUvfW8xpOTc23O96
|
||||
// eTcuc8BebHPVpZZwIaNmzfwxRxPYGfTj
|
||||
|
||||
// 48-dev
|
||||
// OSIsqPrl0TkAUj3R
|
||||
// FYF4BBzL2j2ObbVYH0FrvOZqJf1EACRy
|
||||
let res = await Dial("/api/connection/create", value)
|
||||
dispatchMessage(res.msg, res.status === 200 ? "success" : "error");
|
||||
if (res.status === 200) {
|
||||
get_conn()
|
||||
dispatchMessage("新建连接成功", "success");
|
||||
conn_get()
|
||||
props.openFn(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import {useToast} from "../../message";
|
||||
import {Connection} from "../../interfaces/connection";
|
||||
import {ConnectionCreate} from "../connection/new";
|
||||
import {useStoreConnection} from "../../store/connection";
|
||||
import {useStoreBucket} from "../../store/bucket";
|
||||
|
||||
const useMenuListContainerStyles = makeStyles({
|
||||
container: {
|
||||
@ -50,50 +51,36 @@ const useMenuListContainerStyles = makeStyles({
|
||||
});
|
||||
|
||||
|
||||
function Home() {
|
||||
export function Home() {
|
||||
const styles = useMenuListContainerStyles();
|
||||
const {dispatchMessage} = useToast();
|
||||
const [openCreate, setOpenCreate] = useState(false);
|
||||
const [connectionFilterKeywords, setConnectionFilterKeywords] = useState<string>('');
|
||||
const [connections, setConnections] = useState<Connection[]>([]);
|
||||
const {conn_list, get_conn} = useStoreConnection();
|
||||
const [conn_filter, set_conn_filter] = useState<string>('');
|
||||
const {conn_list, conn_get, conn_update} = useStoreConnection();
|
||||
const {bucket_list, bucket_get} = useStoreBucket()
|
||||
|
||||
useEffect(() => {
|
||||
get_conn()
|
||||
conn_get()
|
||||
}, []);
|
||||
|
||||
async function handleConnect(item: Connection) {
|
||||
console.log('[DEBUG] db click item =', item)
|
||||
for (const c of connections) {
|
||||
if (item.id === c.id && c.active) {
|
||||
console.log('[DEBUG] conn is already connected:', c)
|
||||
let res = await Dial('/api/connection/connect', {id: item.id});
|
||||
if (res.status !== 200) {
|
||||
dispatchMessage(res.msg, "error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let res = await Dial("/api/connection/connect", {id: item.id})
|
||||
if (res.status === 200) {
|
||||
dispatchMessage("连接成功", "success")
|
||||
setConnections(connections.map(one => {
|
||||
if (one.id === item.id) {
|
||||
one.active = true
|
||||
}
|
||||
|
||||
return one
|
||||
}))
|
||||
}
|
||||
conn_update({...item, active: true})
|
||||
bucket_get(item, true)
|
||||
}
|
||||
|
||||
async function handleDisconnect(item: Connection) {
|
||||
let res = await Dial('/api/connection/disconnect', {id: item.id})
|
||||
if (res.status === 200) {
|
||||
setConnections(connections.map(c => {
|
||||
if (item.id === c.id) {
|
||||
c.active = false
|
||||
}
|
||||
return c
|
||||
}))
|
||||
if (res.status !== 200) {
|
||||
dispatchMessage(res.msg, "error")
|
||||
return
|
||||
}
|
||||
conn_update({...item, active: false})
|
||||
}
|
||||
|
||||
return (
|
||||
@ -107,17 +94,17 @@ function Home() {
|
||||
新建连接
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConnectionCreate />
|
||||
<ConnectionCreate openFn={setOpenCreate}/>
|
||||
</Dialog>
|
||||
</div>
|
||||
<div className="body">
|
||||
<div className="body-connections">
|
||||
<div className="body-connections-search">
|
||||
<input className="body-connections-search-input" type={"text"} placeholder="搜索连接"
|
||||
value={connectionFilterKeywords}
|
||||
onChange={(e) => setConnectionFilterKeywords(e.target.value)}/>
|
||||
value={conn_filter}
|
||||
onChange={(e) => set_conn_filter(e.target.value)}/>
|
||||
<div className="body-connections-search-dismiss" onClick={() => {
|
||||
setConnectionFilterKeywords('')
|
||||
set_conn_filter('')
|
||||
}}>
|
||||
<DismissRegular/>
|
||||
</div>
|
||||
@ -125,7 +112,7 @@ function Home() {
|
||||
<div className="body-connections-list">
|
||||
<div className={styles.container}>
|
||||
<MenuList>
|
||||
{conn_list.map(item => {
|
||||
{conn_list.filter(item => item.name.includes(conn_filter)).map(item => {
|
||||
return <MenuItem
|
||||
onDoubleClick={async () => {
|
||||
await handleConnect(item)
|
||||
@ -134,7 +121,9 @@ function Home() {
|
||||
key={item.id}>
|
||||
{item.name}
|
||||
<Tooltip content="断开连接" relationship="label">
|
||||
<Button onClick={async () => {await handleDisconnect(item)}} size="small" className={styles.item_icon} icon={<DismissRegular />} />
|
||||
<Button onClick={async () => {
|
||||
await handleDisconnect(item)
|
||||
}} size="small" className={styles.item_icon} icon={<DismissRegular/>}/>
|
||||
</Tooltip>
|
||||
</MenuItem>
|
||||
})}
|
||||
@ -142,11 +131,18 @@ function Home() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body-content"></div>
|
||||
<div className="body-content" style={{width:'100%'}}>
|
||||
<div></div>
|
||||
<div style={{width:'100%'}}>
|
||||
<MenuList style={{width:'100%'}}>
|
||||
{bucket_list.map(((item, idx) => {
|
||||
return <MenuItem style={{width:'100%'}} key={idx}>{item.name}</MenuItem>
|
||||
}))}
|
||||
</MenuList>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="footer"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
||||
|
@ -7,3 +7,8 @@ export interface Connection {
|
||||
endpoint: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface Bucket {
|
||||
name: string;
|
||||
created_at: number;
|
||||
}
|
@ -3,7 +3,7 @@ import {createRoot} from 'react-dom/client'
|
||||
import './style.css'
|
||||
import {FluentProvider, webLightTheme} from '@fluentui/react-components';
|
||||
import {createBrowserRouter, RouterProvider} from "react-router-dom";
|
||||
import Home from "./component/home/home";
|
||||
import {Home} from "./component/home/home";
|
||||
import {ToastProvider} from "./message";
|
||||
|
||||
const container = document.getElementById('root')
|
||||
|
32
frontend/src/store/bucket.tsx
Normal file
32
frontend/src/store/bucket.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import {create} from 'zustand'
|
||||
import {Bucket, Connection} from "../interfaces/connection";
|
||||
import {Dial, Resp} from "../api";
|
||||
|
||||
interface StoreBucket {
|
||||
bucket_list: Bucket[];
|
||||
bucket_get: (conn: Connection, refresh: boolean) => void;
|
||||
}
|
||||
|
||||
let bucket_map: { [id: number]: Bucket[] };
|
||||
|
||||
export const useStoreBucket = create<StoreBucket>()((set) => ({
|
||||
bucket_list: [],
|
||||
bucket_get: async (conn: Connection, refresh: boolean) => {
|
||||
let res: Resp<{ list: Bucket[]; }>;
|
||||
if (refresh) {
|
||||
res = await Dial<{list: Bucket[]}>('/api/connection/buckets', {id: conn.id});
|
||||
if (res.status !== 200) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
if (refresh) {
|
||||
bucket_map = {...bucket_map, [conn.id]: res.data.list}
|
||||
return {bucket_list: res.data.list};
|
||||
}
|
||||
|
||||
return {bucket_list: bucket_map[conn.id]};
|
||||
})
|
||||
}
|
||||
}))
|
@ -2,15 +2,37 @@ import {create} from 'zustand'
|
||||
import {Connection} from "../interfaces/connection";
|
||||
import {Dial} from "../api";
|
||||
|
||||
type StoreConnection = {
|
||||
conn_list: Connection[]
|
||||
get_conn: () => void
|
||||
interface StoreConnection {
|
||||
conn_active: Connection | null;
|
||||
conn_list: Connection[];
|
||||
conn_get: () => void;
|
||||
conn_update: (connection: Connection) => void;
|
||||
}
|
||||
|
||||
export const useStoreConnection = create<StoreConnection>()((set) => ({
|
||||
conn_active: null,
|
||||
conn_list: [],
|
||||
get_conn: async () => {
|
||||
conn_get: async () => {
|
||||
const res = await Dial<{ list: Connection[] }>('/api/connection/list');
|
||||
if (res.status !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
set({conn_list: res.data.list})
|
||||
},
|
||||
|
||||
conn_update: async (connection: Connection) => {
|
||||
set((state) => {
|
||||
return {
|
||||
conn_active: connection.active? connection: null,
|
||||
conn_list: state.conn_list.map(item => {
|
||||
if (item.id === connection.id) {
|
||||
return connection
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
type ListBucketRes struct {
|
||||
CreatedAt int64
|
||||
Name string
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func (c *Client) ListBucket(ctx context.Context) ([]*ListBucketRes, error) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user