@@ -2,10 +2,10 @@ import { getApiKeys } from "redux/selectors/usersSelectors";
22import Card from "antd/es/card" ;
33import Flex from "antd/es/flex" ;
44import Title from "antd/es/typography/Title" ;
5- import Table from "antd/es/table" ;
5+ import Table , { ColumnsType } from "antd/es/table" ;
66import Tooltip from "antd/es/tooltip" ;
77import { useDispatch , useSelector } from "react-redux" ;
8- import { useState } from "react" ;
8+ import { useState , useCallback , useMemo } from "react" ;
99import { styled } from "styled-components" ;
1010import { AddIcon , CustomModal , EditPopover , TacoButton , messageInstance } from "lowcoder-design" ;
1111import { trans } from "i18n" ;
@@ -21,7 +21,7 @@ const TableStyled = styled(Table)`
2121 .ant-table-tbody > tr > td {
2222 padding: 11px 12px;
2323 }
24- ` ;
24+ ` as typeof Table ;
2525
2626const OperationWrapper = styled . div `
2727 display: flex;
@@ -42,6 +42,8 @@ const CreateButton = styled(TacoButton)`
4242export type ApiKeyType = {
4343 id : string ;
4444 token : string ;
45+ name : string ;
46+ description ?: string ;
4547}
4648
4749export default function UserApiKeysCard ( ) {
@@ -50,116 +52,140 @@ export default function UserApiKeysCard() {
5052 const [ modalVisible , setModalVisible ] = useState ( false ) ;
5153 const [ newApiKey , setNewApiKey ] = useState < ApiKeyType > ( ) ;
5254
53- const handleCopy = ( value : string ) => {
54- navigator . clipboard . writeText ( value ) . then ( ( ) => {
55- messageInstance . success ( 'Copied to clipboard!' ) ;
56- } ) . catch ( err => {
57- messageInstance . error ( 'Failed to copy!' ) ;
55+ const handleCopy = useCallback ( ( value : string ) => {
56+ navigator . clipboard . writeText ( value )
57+ . then ( ( ) => messageInstance . success ( 'Copied to clipboard!' ) )
58+ . catch ( ( ) => messageInstance . error ( 'Failed to copy!' ) ) ;
59+ } , [ ] ) ;
60+
61+ const handleDeleteApiKey = useCallback ( ( apiKeyId : string ) => {
62+ CustomModal . confirm ( {
63+ title : trans ( "profile.deleteApiKey" ) ,
64+ content : trans ( "profile.deleteApiKeyContent" ) ,
65+ onConfirm : ( ) => {
66+ UserApi . deleteApiKey ( apiKeyId )
67+ . then ( resp => {
68+ if ( validateResponse ( resp ) ) {
69+ dispatch ( fetchApiKeysAction ( ) ) ;
70+ }
71+ } )
72+ . catch ( ( ) => {
73+ messageInstance . error ( trans ( "profile.deleteApiKeyError" ) ) ;
74+ } ) ;
75+ } ,
76+ confirmBtnType : "delete" ,
77+ okText : trans ( "delete" ) ,
5878 } ) ;
59- } ;
79+ } , [ dispatch ] ) ;
80+
81+ const handleModalClose = useCallback ( ( ) => {
82+ setModalVisible ( false ) ;
83+ } , [ ] ) ;
84+
85+ const handleConfigCreate = useCallback ( ( apiKey ?: ApiKeyType ) => {
86+ setModalVisible ( false ) ;
87+ setNewApiKey ( apiKey ) ;
88+ dispatch ( fetchApiKeysAction ( ) ) ;
89+ } , [ dispatch ] ) ;
90+
91+ const columns : ColumnsType < ApiKeyType > = useMemo ( ( ) => [
92+ {
93+ title : trans ( "profile.apiKeyName" ) ,
94+ dataIndex : "name" ,
95+ ellipsis : true ,
96+ } ,
97+ {
98+ title : trans ( "profile.apiKeyDescription" ) ,
99+ dataIndex : "description" ,
100+ width : 400 ,
101+ render : ( value : string ) => value || '-' ,
102+ } ,
103+ {
104+ title : trans ( "profile.apiKey" ) ,
105+ dataIndex : "token" ,
106+ width : 500 ,
107+ render : ( value : string , record : ApiKeyType ) => {
108+ if ( newApiKey ?. id === record . id ) {
109+ return (
110+ < Tooltip placement = "topLeft" title = { trans ( "profile.apiKeyCopy" ) } >
111+ < div
112+ onClick = { ( ) => handleCopy ( newApiKey . token ) }
113+ style = { { cursor : 'pointer' , width : '500px' } }
114+ >
115+ { newApiKey . token }
116+
117+ < CopyOutlined />
118+ </ div >
119+ </ Tooltip >
120+ ) ;
121+ }
122+ return < div > { value } </ div > ;
123+ }
124+ } ,
125+ {
126+ title : " " ,
127+ dataIndex : "operation" ,
128+ width : "208px" ,
129+ render : ( _ : unknown , record : ApiKeyType ) => (
130+ < OperationWrapper >
131+ < EditPopover
132+ del = { ( ) => handleDeleteApiKey ( record . id ) }
133+ >
134+ < PopoverIcon tabIndex = { - 1 } />
135+ </ EditPopover >
136+ </ OperationWrapper >
137+ ) ,
138+ } ,
139+ ] , [ newApiKey , handleCopy , handleDeleteApiKey ] ) ;
140+
141+ const dataSource = useMemo ( ( ) =>
142+ apiKeys . map ( ( apiKey , i ) => ( {
143+ ...apiKey ,
144+ key : i ,
145+ } ) )
146+ , [ apiKeys ] ) ;
60147
61148 return (
62149 < >
63150 < Card style = { { marginBottom : "20px" } } >
64151 < Flex justify = "space-between" align = "center" style = { { marginBottom : '8px' } } >
65152 < Title level = { 4 } > { trans ( "profile.apiKeys" ) } </ Title >
66- < h4 > < a href = { trans ( "docUrls.apiDocHome" ) } target = "_blank" > { trans ( "home.howToUseAPI" ) } </ a > </ h4 >
153+ < h4 >
154+ < a href = { trans ( "docUrls.apiDocHome" ) } target = "_blank" rel = "noopener noreferrer" >
155+ { trans ( "home.howToUseAPI" ) }
156+ </ a >
157+ </ h4 >
67158 < CreateButton
68- buttonType = { "primary" }
159+ buttonType = "primary"
69160 icon = { < AddIcon /> }
70- onClick = { ( ) =>
71- setModalVisible ( true )
72- }
161+ onClick = { ( ) => setModalVisible ( true ) }
73162 >
74163 { trans ( "profile.createApiKey" ) }
75164 </ CreateButton >
76165 </ Flex >
77- { Boolean ( newApiKey ) && < Alert message = { trans ( "profile.apiKeyInfo" ) } type = "info" style = { { marginBottom : '16px' } } /> }
166+
167+ { Boolean ( newApiKey ) && (
168+ < Alert
169+ message = { trans ( "profile.apiKeyInfo" ) }
170+ type = "info"
171+ style = { { marginBottom : '16px' } }
172+ />
173+ ) }
174+
78175 < TableStyled
79- tableLayout = { "auto" }
176+ tableLayout = "auto"
80177 scroll = { { x : "100%" } }
81178 pagination = { false }
82- columns = { [
83- {
84- title : trans ( "profile.apiKeyName" ) ,
85- dataIndex : "name" ,
86- ellipsis : true ,
87- } ,
88- {
89- title : trans ( "profile.apiKeyDescription" ) ,
90- dataIndex : "description" ,
91- ellipsis : true ,
92- render : ( value : string ) => {
93- return (
94- < >
95- { value || '-' }
96- </ >
97- )
98- }
99- } ,
100- {
101- title : trans ( "profile.apiKey" ) ,
102- dataIndex : "token" ,
103- ellipsis : true ,
104- render : ( value : string , record : any ) => {
105- if ( newApiKey ?. id === record . id ) {
106- return (
107- < Tooltip placement = "topLeft" title = { trans ( "profile.apiKeyCopy" ) } >
108- < div onClick = { ( ) => handleCopy ( newApiKey ?. token ! ) } style = { { cursor : 'pointer' } } >
109- { value }
110-
111- < CopyOutlined />
112- </ div >
113- </ Tooltip >
114- )
115- }
116- return < div > { value } </ div >
117- }
118- } ,
119- { title : " " , dataIndex : "operation" , width : "208px" } ,
120- ] }
121- dataSource = { apiKeys . map ( ( apiKey , i ) => ( {
122- ...apiKey ,
123- key : i ,
124- operation : (
125- < OperationWrapper >
126- < EditPopover
127- del = { ( ) => {
128- CustomModal . confirm ( {
129- title : trans ( "profile.deleteApiKey" ) ,
130- content : trans ( "profile.deleteApiKeyContent" ) ,
131- onConfirm : ( ) => {
132- UserApi . deleteApiKey ( apiKey . id ) . then ( resp => {
133- if ( validateResponse ( resp ) ) {
134- dispatch ( fetchApiKeysAction ( ) ) ;
135- }
136- } )
137- . catch ( ( e ) => {
138- messageInstance . error ( trans ( "profile.deleteApiKeyError" ) ) ;
139- } )
140- } ,
141- confirmBtnType : "delete" ,
142- okText : trans ( "delete" ) ,
143- } )
144- } }
145- >
146- < PopoverIcon tabIndex = { - 1 } />
147- </ EditPopover >
148- </ OperationWrapper >
149- ) ,
150- } ) ) }
179+ columns = { columns }
180+ dataSource = { dataSource }
151181 />
152182 </ Card >
153183
154184 < CreateApiKeyModal
155185 modalVisible = { modalVisible }
156- closeModal = { ( ) => setModalVisible ( false ) }
157- onConfigCreate = { ( apiKey ?: ApiKeyType ) => {
158- setModalVisible ( false ) ;
159- setNewApiKey ( apiKey ) ;
160- dispatch ( fetchApiKeysAction ( ) ) ;
161- } }
186+ closeModal = { handleModalClose }
187+ onConfigCreate = { handleConfigCreate }
162188 />
163189 </ >
164- )
190+ ) ;
165191}
0 commit comments