11import React , { useState , useEffect } from 'react' ;
22import { Button , Input , Table , Space , Modal , Form , Select , message , Popconfirm } from 'antd' ;
3- import { PlusOutlined , UploadOutlined , DeleteOutlined , DownloadOutlined } from '@ant-design/icons' ;
3+ import { PlusOutlined , UploadOutlined , DeleteOutlined , DownloadOutlined , EditOutlined } from '@ant-design/icons' ;
44import { useAllowedUsersNpubs , useAllowedUsersValidation } from '@app/hooks/useAllowedUsers' ;
55import { AllowedUsersSettings , AllowedUsersMode } from '@app/types/allowedUsers.types' ;
66import * as S from './NPubManagement.styles' ;
@@ -30,10 +30,13 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
3030 mode
3131} ) => {
3232 const [ isAddModalVisible , setIsAddModalVisible ] = useState ( false ) ;
33+ const [ isEditModalVisible , setIsEditModalVisible ] = useState ( false ) ;
3334 const [ isBulkModalVisible , setIsBulkModalVisible ] = useState ( false ) ;
3435 const [ bulkText , setBulkText ] = useState ( '' ) ;
3536 const [ unifiedUsers , setUnifiedUsers ] = useState < UnifiedUser [ ] > ( [ ] ) ;
37+ const [ editingUser , setEditingUser ] = useState < UnifiedUser | null > ( null ) ;
3638 const [ addForm ] = Form . useForm < AddNpubFormData > ( ) ;
39+ const [ editForm ] = Form . useForm < AddNpubFormData > ( ) ;
3740
3841 const readNpubs = useAllowedUsersNpubs ( 'read' ) ;
3942 const writeNpubs = useAllowedUsersNpubs ( 'write' ) ;
@@ -47,7 +50,7 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
4750 readNpubs . npubs . forEach ( npub => {
4851 allNpubs . set ( npub . npub , {
4952 npub : npub . npub ,
50- tier : npub . tier ,
53+ tier : npub . tier || settings . tiers [ 0 ] ?. name || 'basic' ,
5154 readAccess : true ,
5255 writeAccess : false ,
5356 added_at : npub . added_at
@@ -59,10 +62,12 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
5962 const existing = allNpubs . get ( npub . npub ) ;
6063 if ( existing ) {
6164 existing . writeAccess = true ;
65+ // Preserve the tier from read access, or use write tier, or fallback
66+ existing . tier = existing . tier || npub . tier || settings . tiers [ 0 ] ?. name || 'basic' ;
6267 } else {
6368 allNpubs . set ( npub . npub , {
6469 npub : npub . npub ,
65- tier : npub . tier ,
70+ tier : npub . tier || settings . tiers [ 0 ] ?. name || 'basic' ,
6671 readAccess : false ,
6772 writeAccess : true ,
6873 added_at : npub . added_at
@@ -71,7 +76,7 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
7176 } ) ;
7277
7378 setUnifiedUsers ( Array . from ( allNpubs . values ( ) ) ) ;
74- } , [ readNpubs . npubs , writeNpubs . npubs ] ) ;
79+ } , [ readNpubs . npubs , writeNpubs . npubs , settings . tiers ] ) ;
7580 const tierOptions = settings . tiers . map ( tier => {
7681 const displayFormat = tier . unlimited
7782 ? 'unlimited'
@@ -108,16 +113,19 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
108113 const user = unifiedUsers . find ( u => u . npub === npub ) ;
109114 if ( ! user ) return ;
110115
116+ // Ensure we have a valid tier - fallback to first available tier if undefined
117+ const tierToUse = user . tier || settings . tiers [ 0 ] ?. name || 'basic' ;
118+
111119 try {
112120 if ( type === 'read' ) {
113121 if ( enabled ) {
114- await readNpubs . addNpub ( npub , user . tier ) ;
122+ await readNpubs . addNpub ( npub , tierToUse ) ;
115123 } else {
116124 await readNpubs . removeNpub ( npub ) ;
117125 }
118126 } else {
119127 if ( enabled ) {
120- await writeNpubs . addNpub ( npub , user . tier ) ;
128+ await writeNpubs . addNpub ( npub , tierToUse ) ;
121129 } else {
122130 await writeNpubs . removeNpub ( npub ) ;
123131 }
@@ -127,6 +135,67 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
127135 }
128136 } ;
129137
138+ const handleEditUser = ( user : UnifiedUser ) => {
139+ setEditingUser ( user ) ;
140+ setIsEditModalVisible ( true ) ;
141+ // Set form values after modal is visible
142+ setTimeout ( ( ) => {
143+ editForm . setFieldsValue ( {
144+ npub : user . npub ,
145+ tier : user . tier ,
146+ readAccess : user . readAccess ,
147+ writeAccess : user . writeAccess
148+ } ) ;
149+ } , 0 ) ;
150+ } ;
151+
152+ const handleSaveEdit = async ( ) => {
153+ try {
154+ const values = await editForm . validateFields ( ) ;
155+ const originalUser = editingUser ! ;
156+
157+ // Check what actually changed
158+ const readChanged = originalUser . readAccess !== values . readAccess ;
159+ const writeChanged = originalUser . writeAccess !== values . writeAccess ;
160+ const tierChanged = originalUser . tier !== values . tier ;
161+
162+ // Handle read access changes
163+ if ( readChanged || ( tierChanged && values . readAccess ) ) {
164+ if ( values . readAccess ) {
165+ // Remove old entry if exists and re-add with new tier
166+ if ( originalUser . readAccess ) {
167+ await readNpubs . removeNpub ( values . npub ) ;
168+ }
169+ await readNpubs . addNpub ( values . npub , values . tier ) ;
170+ } else {
171+ // Remove read access
172+ await readNpubs . removeNpub ( values . npub ) ;
173+ }
174+ }
175+
176+ // Handle write access changes
177+ if ( writeChanged || ( tierChanged && values . writeAccess ) ) {
178+ if ( values . writeAccess ) {
179+ // Remove old entry if exists and re-add with new tier
180+ if ( originalUser . writeAccess ) {
181+ await writeNpubs . removeNpub ( values . npub ) ;
182+ }
183+ await writeNpubs . addNpub ( values . npub , values . tier ) ;
184+ } else {
185+ // Remove write access
186+ await writeNpubs . removeNpub ( values . npub ) ;
187+ }
188+ }
189+
190+ setIsEditModalVisible ( false ) ;
191+ setEditingUser ( null ) ;
192+ editForm . resetFields ( ) ;
193+ } catch ( error ) {
194+ console . error ( 'Edit user error:' , error ) ;
195+ message . error ( 'Failed to update user' ) ;
196+ }
197+ } ;
198+
130199 const handleRemoveUser = async ( npub : string ) => {
131200 try {
132201 // Remove from both lists
@@ -257,17 +326,27 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
257326 title : 'Actions' ,
258327 key : 'actions' ,
259328 render : ( _ : any , record : UnifiedUser ) => (
260- < Popconfirm
261- title = "Are you sure you want to remove this user completely?"
262- onConfirm = { ( ) => handleRemoveUser ( record . npub ) }
263- >
329+ < Space size = "small" >
264330 < Button
265331 type = "text"
266- danger
267- icon = { < DeleteOutlined /> }
332+ icon = { < EditOutlined /> }
268333 size = "small"
334+ onClick = { ( ) => handleEditUser ( record ) }
335+ title = "Edit user"
269336 />
270- </ Popconfirm >
337+ < Popconfirm
338+ title = "Are you sure you want to remove this user completely?"
339+ onConfirm = { ( ) => handleRemoveUser ( record . npub ) }
340+ >
341+ < Button
342+ type = "text"
343+ danger
344+ icon = { < DeleteOutlined /> }
345+ size = "small"
346+ title = "Remove user"
347+ />
348+ </ Popconfirm >
349+ </ Space >
271350 )
272351 }
273352 ] ;
@@ -346,21 +425,81 @@ export const NPubManagement: React.FC<NPubManagementProps> = ({
346425 </ Form . Item >
347426
348427 < Form . Item
349- name = "readAccess"
350428 label = "Permissions"
351429 style = { { marginBottom : 0 } }
352430 >
353431 < Space direction = "vertical" >
354- < Form . Item name = "readAccess" valuePropName = "checked" style = { { marginBottom : 8 } } >
355- < S . PermissionLabel >
356- < S . StyledSwitch size = "small" /> Read Access
357- </ S . PermissionLabel >
358- </ Form . Item >
359- < Form . Item name = "writeAccess" valuePropName = "checked" style = { { marginBottom : 0 } } >
360- < S . PermissionLabel >
361- < S . StyledSwitch size = "small" /> Write Access
362- </ S . PermissionLabel >
363- </ Form . Item >
432+ < div style = { { display : 'flex' , alignItems : 'center' , gap : '8px' , color : 'var(--text-main-color)' } } >
433+ < Form . Item name = "readAccess" valuePropName = "checked" style = { { marginBottom : 0 } } >
434+ < S . StyledSwitch size = "small" />
435+ </ Form . Item >
436+ < span > Read Access</ span >
437+ </ div >
438+ < div style = { { display : 'flex' , alignItems : 'center' , gap : '8px' , color : 'var(--text-main-color)' } } >
439+ < Form . Item name = "writeAccess" valuePropName = "checked" style = { { marginBottom : 0 } } >
440+ < S . StyledSwitch size = "small" />
441+ </ Form . Item >
442+ < span > Write Access</ span >
443+ </ div >
444+ </ Space >
445+ </ Form . Item >
446+ </ Form >
447+ </ Modal >
448+
449+ { /* Edit User Modal */ }
450+ < Modal
451+ title = "Edit User"
452+ open = { isEditModalVisible }
453+ onOk = { handleSaveEdit }
454+ onCancel = { ( ) => {
455+ setIsEditModalVisible ( false ) ;
456+ setEditingUser ( null ) ;
457+ editForm . resetFields ( ) ;
458+ } }
459+ destroyOnClose
460+ >
461+ < Form
462+ form = { editForm }
463+ layout = "vertical"
464+ initialValues = { {
465+ npub : editingUser ?. npub || '' ,
466+ tier : editingUser ?. tier || '' ,
467+ readAccess : editingUser ?. readAccess || false ,
468+ writeAccess : editingUser ?. writeAccess || false
469+ } }
470+ >
471+ < Form . Item
472+ name = "npub"
473+ label = "NPUB"
474+ >
475+ < Input disabled />
476+ </ Form . Item >
477+
478+ < Form . Item
479+ name = "tier"
480+ label = "Tier"
481+ rules = { [ { required : true , message : 'Please select a tier' } ] }
482+ >
483+ < Select placeholder = "Select tier" options = { tierOptions } />
484+ </ Form . Item >
485+
486+ < Form . Item
487+ label = "Permissions"
488+ style = { { marginBottom : 0 } }
489+ >
490+ < Space direction = "vertical" >
491+ < div style = { { display : 'flex' , alignItems : 'center' , gap : '8px' , color : 'var(--text-main-color)' } } >
492+ < Form . Item name = "readAccess" valuePropName = "checked" style = { { marginBottom : 0 } } >
493+ < S . StyledSwitch size = "small" />
494+ </ Form . Item >
495+ < span > Read Access</ span >
496+ </ div >
497+ < div style = { { display : 'flex' , alignItems : 'center' , gap : '8px' , color : 'var(--text-main-color)' } } >
498+ < Form . Item name = "writeAccess" valuePropName = "checked" style = { { marginBottom : 0 } } >
499+ < S . StyledSwitch size = "small" />
500+ </ Form . Item >
501+ < span > Write Access</ span >
502+ </ div >
364503 </ Space >
365504 </ Form . Item >
366505 </ Form >
0 commit comments