Browse Source

finish attu-473

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
ryjiang 11 months ago
parent
commit
81a08590d0

+ 17 - 0
client/src/components/icons/Icons.tsx

@@ -862,6 +862,23 @@ const icons: { [x in IconsType]: (props?: any) => React.ReactElement } = {
       ></path>
     </svg>
   ),
+  cross: (props = {}) => (
+    <svg
+      width="15"
+      height="15"
+      viewBox="0 0 15 15"
+      fill="none"
+      xmlns="http://www.w3.org/2000/svg"
+      {...props}
+    >
+      <path
+        d="M12.8536 2.85355C13.0488 2.65829 13.0488 2.34171 12.8536 2.14645C12.6583 1.95118 12.3417 1.95118 12.1464 2.14645L7.5 6.79289L2.85355 2.14645C2.65829 1.95118 2.34171 1.95118 2.14645 2.14645C1.95118 2.34171 1.95118 2.65829 2.14645 2.85355L6.79289 7.5L2.14645 12.1464C1.95118 12.3417 1.95118 12.6583 2.14645 12.8536C2.34171 13.0488 2.65829 13.0488 2.85355 12.8536L7.5 8.20711L12.1464 12.8536C12.3417 13.0488 12.6583 13.0488 12.8536 12.8536C13.0488 12.6583 13.0488 12.3417 12.8536 12.1464L8.20711 7.5L12.8536 2.85355Z"
+        fill="currentColor"
+        fill-rule="evenodd"
+        clip-rule="evenodd"
+      ></path>
+    </svg>
+  ),
 };
 
 export default icons;

+ 2 - 1
client/src/components/icons/Types.ts

@@ -54,4 +54,5 @@ export type IconsType =
   | 'magic'
   | 'code'
   | 'reset'
-  | 'link';
+  | 'link'
+  | 'cross';

+ 8 - 0
client/src/consts/default.ts

@@ -3,3 +3,11 @@ export const DEFAULT_ATTU_VARCHAR_MAX_LENGTH = 32;
 export const DEFAULT_ATTU_ELEMENT_TYPE = 4; // int32
 export const DEFAULT_ATTU_DIM = 128;
 export const MIN_INT64 = `-9223372036854775807`; // safe int64 min value
+export const DEFAULT_CONNECTION = {
+  address: '127.0.0.1:19530',
+  database: 'default',
+  token: '',
+  username: '',
+  password: '',
+  time: -1,
+};

+ 1 - 9
client/src/context/Auth.tsx

@@ -47,14 +47,6 @@ export const AuthProvider = (props: { children: React.ReactNode }) => {
     window.localStorage.getItem(MILVUS_CLIENT_ID) || ''
   );
 
-  // update title when address changes
-  useEffect(() => {
-    document.title = authReq.address ? `${authReq.address} - Attu` : 'Attu';
-    return () => {
-      document.title = 'Attu';
-    };
-  }, [authReq.address]);
-
   // update local storage when authReq changes
   useEffect(() => {
     // store auth request in local storage
@@ -69,7 +61,7 @@ export const AuthProvider = (props: { children: React.ReactNode }) => {
     // connect to Milvus
     const res = await MilvusService.connect(params);
     // update auth request
-    setAuthReq({ ...params, database: res.database, password: '', token: '' });
+    setAuthReq({ ...params, database: res.database });
     setClientId(res.clientId);
 
     return res;

+ 69 - 17
client/src/pages/connect/AuthForm.tsx

@@ -7,7 +7,12 @@ import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
 import { useNavigate } from 'react-router-dom';
 import { rootContext, authContext, dataContext } from '@/context';
-import { MILVUS_CLIENT_ID, ATTU_AUTH_HISTORY, MILVUS_DATABASE } from '@/consts';
+import {
+  MILVUS_CLIENT_ID,
+  ATTU_AUTH_HISTORY,
+  MILVUS_DATABASE,
+  DEFAULT_CONNECTION,
+} from '@/consts';
 import { CustomRadio } from '@/components/customRadio/CustomRadio';
 import Icons from '@/components/icons/Icons';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
@@ -16,7 +21,7 @@ import { useStyles } from './style';
 import { AuthReq } from '@server/types';
 
 type Connection = AuthReq & {
-  time: string;
+  time: number;
 };
 
 export const AuthForm = () => {
@@ -123,6 +128,9 @@ export const AuthForm = () => {
         JSON.stringify(newHistory)
       );
 
+      // set title
+      document.title = authReq.address ? `${authReq.address} - Attu` : 'Attu';
+
       // redirect to homepage
       navigate('/');
     } catch (error: any) {
@@ -136,25 +144,52 @@ export const AuthForm = () => {
   };
 
   // connect history clicked
-  const onConnectHistoryClicked = (connection: any) => {
-    console.log('connection', connection);
+  const handleClickOnHisotry = (connection: Connection) => {
     // set auth request
     setAuthReq(connection);
     // close menu
     handleMenuClose();
   };
 
+  const handleDeleteConnection = (connection: Connection) => {
+    const history = JSON.parse(
+      window.localStorage.getItem(ATTU_AUTH_HISTORY) || '[]'
+    ) as Connection[];
+
+    const newHistory = history.filter(
+      item =>
+        item.address !== connection.address ||
+        item.database !== connection.database
+    );
+
+    if (newHistory.length === 0) {
+      newHistory.push(DEFAULT_CONNECTION);
+    }
+
+    // save to local storage
+    window.localStorage.setItem(ATTU_AUTH_HISTORY, JSON.stringify(newHistory));
+
+    // sort by time, put '--' to the end
+    newHistory.sort((a, b) => {
+      return new Date(b.time).getTime() - new Date(a.time).getTime();
+    });
+    setConnections(newHistory);
+  };
+
   // is button should be disabled
   const btnDisabled = authReq.address.trim().length === 0 || isConnecting;
 
   // load connection from local storage
   useEffect(() => {
     const connections: Connection[] = JSON.parse(
-      window.localStorage.getItem(ATTU_AUTH_HISTORY) ||
-        '[{"address":"http://127.0.0.1:19530","database":"default","username":"","time":"--"}]'
+      window.localStorage.getItem(ATTU_AUTH_HISTORY) || '[]'
     );
 
-    // sort by time
+    if (connections.length === 0) {
+      connections.push(DEFAULT_CONNECTION);
+    }
+
+    // sort by time, put '--' to the end
     connections.sort((a, b) => {
       return new Date(b.time).getTime() - new Date(a.time).getTime();
     });
@@ -166,15 +201,17 @@ export const AuthForm = () => {
   useEffect(() => {
     // if address contains zilliz, or username or password is not empty
     //  set withpass to true
-    if (
+    const withPass =
       (authReq.address.length > 0 && authReq.address.includes('zilliz')) ||
       authReq.username.length > 0 ||
-      authReq.password.length > 0
-    ) {
-      setWithPass(true);
-    }
+      authReq.password.length > 0;
+
+    // set with pass
+    setWithPass(withPass);
     // reset form
     resetValidation(formatForm(authReq));
+    // update title
+    document.title = 'Attu';
   }, [authReq.address, authReq.username, authReq.password]);
 
   return (
@@ -195,7 +232,8 @@ export const AuthForm = () => {
           textConfig={{
             label: attuTrans.address,
             key: 'address',
-            onChange: (val: string) => handleInputChange('address', String(val)),
+            onChange: (val: string) =>
+              handleInputChange('address', String(val)),
             variant: 'filled',
             className: classes.input,
             placeholder: attuTrans.address,
@@ -271,7 +309,6 @@ export const AuthForm = () => {
               validInfo={validation}
               key={attuTrans.token}
             />
-
             {/* user  */}
             <CustomInput
               type="text"
@@ -290,7 +327,6 @@ export const AuthForm = () => {
               validInfo={validation}
               key={attuTrans.username}
             />
-
             {/* pass  */}
             <CustomInput
               type="text"
@@ -339,7 +375,7 @@ export const AuthForm = () => {
             key={index}
             className={classes.connection}
             onClick={() => {
-              onConnectHistoryClicked(connection);
+              handleClickOnHisotry(connection);
             }}
           >
             <div className="address">
@@ -349,7 +385,23 @@ export const AuthForm = () => {
               </div>
             </div>
             <div className="time">
-              {new Date(connection.time).toLocaleString()}
+              {connection.time !== -1
+                ? new Date(connection.time).toLocaleString()
+                : '--'}
+            </div>
+
+            <div>
+              {connection.time !== -1 && (
+                <CustomIconButton
+                  className="deleteIconBtn"
+                  onClick={e => {
+                    e.stopPropagation();
+                    handleDeleteConnection(connection);
+                  }}
+                >
+                  <Icons.cross></Icons.cross>
+                </CustomIconButton>
+              )}
             </div>
           </li>
         ))}

+ 24 - 6
client/src/pages/connect/style.ts

@@ -50,10 +50,15 @@ export const useStyles = makeStyles((theme: Theme) => ({
   },
   menuBtn: {
     display: 'flex',
-    width: 36,
+
     paddingLeft: 8,
     paddingRight: 8,
+
     fontSize: 14,
+    '& button': {
+      width: 36,
+      height: 36,
+    },
   },
   menu: {
     '& ul': {
@@ -70,8 +75,8 @@ export const useStyles = makeStyles((theme: Theme) => ({
     display: 'flex',
     justifyContent: 'space-between',
     fontSize: '14px',
-    width: 360,
-    padding: `0 16px`,
+    width: 380,
+    padding: `0 8px`,
     '&:hover': {
       backgroundColor: theme.palette.action.hover,
     },
@@ -79,7 +84,7 @@ export const useStyles = makeStyles((theme: Theme) => ({
 
     '& .address': {
       display: 'grid',
-      gridTemplateColumns: '20px 1fr',
+      gridTemplateColumns: '24px 1fr',
       gap: 4,
       color: theme.palette.text.primary,
       fontSize: '14px',
@@ -87,7 +92,7 @@ export const useStyles = makeStyles((theme: Theme) => ({
       '& .text': {
         overflow: 'hidden',
         textOverflow: 'ellipsis',
-        maxWidth: 200,
+        width: 200,
         wordWrap: 'break-word',
       },
     },
@@ -100,8 +105,21 @@ export const useStyles = makeStyles((theme: Theme) => ({
 
     '& .time': {
       color: theme.palette.text.secondary,
-      fontSize: '12px',
+      fontSize: 11,
+      lineHeight: 1.5,
       padding: '12px 0',
+      width: 130,
+      fontStyle: 'italic',
+    },
+
+    '& .deleteIconBtn': {
+      padding: '8px 0',
+      '& svg': {
+        fontSize: '14px',
+      },
+      height: 16,
+      lineHeight: '16px',
+      margin: 0,
     },
   },
 }));