Skip to content

Commit dec4e8e

Browse files
Merge pull request #49 from HORNET-Storage/feature/api-migration-and-personal-mode
Feature/api migration and personal mode
2 parents bdc4c08 + 605d358 commit dec4e8e

2 files changed

Lines changed: 185 additions & 33 deletions

File tree

src/api/allowedUsers.api.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ export const updateAllowedUsersSettings = async (settings: AllowedUsersSettings)
106106
}
107107
};
108108

109-
console.log('Sending to backend:', JSON.stringify(nestedSettings, null, 2));
110109

111110
const response = await fetch(`${config.baseURL}/api/settings`, {
112111
method: 'POST',
@@ -118,10 +117,8 @@ export const updateAllowedUsersSettings = async (settings: AllowedUsersSettings)
118117
});
119118

120119
const text = await response.text();
121-
console.log('Backend response:', response.status, text);
122120

123121
if (!response.ok) {
124-
console.error('Backend error:', response.status, text);
125122
throw new Error(`HTTP error! status: ${response.status}, response: ${text}`);
126123
}
127124

@@ -147,9 +144,14 @@ export const getReadNpubs = async (page = 1, pageSize = 20): Promise<AllowedUser
147144
const text = await response.text();
148145
try {
149146
const data = JSON.parse(text);
150-
// Transform backend response to expected format
147+
// Transform backend response to expected format - map tier_name to tier
148+
const transformedNpubs = (data.npubs || []).map((npub: any) => ({
149+
...npub,
150+
tier: npub.tier_name || npub.tier || 'basic'
151+
}));
152+
151153
return {
152-
npubs: data.npubs || [],
154+
npubs: transformedNpubs,
153155
total: data.pagination?.total || 0,
154156
page: data.pagination?.page || page,
155157
pageSize: data.pagination?.pageSize || pageSize
@@ -161,13 +163,16 @@ export const getReadNpubs = async (page = 1, pageSize = 20): Promise<AllowedUser
161163

162164
export const addReadNpub = async (npub: string, tier: string): Promise<{ success: boolean, message: string }> => {
163165
const token = readToken();
166+
const requestBody = { npub, tier };
167+
168+
164169
const response = await fetch(`${config.baseURL}/api/allowed-npubs/read`, {
165170
method: 'POST',
166171
headers: {
167172
'Content-Type': 'application/json',
168173
'Authorization': `Bearer ${token}`,
169174
},
170-
body: JSON.stringify({ npub, tier }),
175+
body: JSON.stringify(requestBody),
171176
});
172177

173178
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
@@ -213,9 +218,14 @@ export const getWriteNpubs = async (page = 1, pageSize = 20): Promise<AllowedUse
213218
const text = await response.text();
214219
try {
215220
const data = JSON.parse(text);
216-
// Transform backend response to expected format
221+
// Transform backend response to expected format - map tier_name to tier
222+
const transformedNpubs = (data.npubs || []).map((npub: any) => ({
223+
...npub,
224+
tier: npub.tier_name || npub.tier || 'basic'
225+
}));
226+
217227
return {
218-
npubs: data.npubs || [],
228+
npubs: transformedNpubs,
219229
total: data.pagination?.total || 0,
220230
page: data.pagination?.page || page,
221231
pageSize: data.pagination?.pageSize || pageSize
@@ -227,13 +237,16 @@ export const getWriteNpubs = async (page = 1, pageSize = 20): Promise<AllowedUse
227237

228238
export const addWriteNpub = async (npub: string, tier: string): Promise<{ success: boolean, message: string }> => {
229239
const token = readToken();
240+
const requestBody = { npub, tier };
241+
242+
230243
const response = await fetch(`${config.baseURL}/api/allowed-npubs/write`, {
231244
method: 'POST',
232245
headers: {
233246
'Content-Type': 'application/json',
234247
'Authorization': `Bearer ${token}`,
235248
},
236-
body: JSON.stringify({ npub, tier }),
249+
body: JSON.stringify(requestBody),
237250
});
238251

239252
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

src/components/allowed-users/components/NPubManagement/NPubManagement.tsx

Lines changed: 163 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect } from 'react';
22
import { 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';
44
import { useAllowedUsersNpubs, useAllowedUsersValidation } from '@app/hooks/useAllowedUsers';
55
import { AllowedUsersSettings, AllowedUsersMode } from '@app/types/allowedUsers.types';
66
import * 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

Comments
 (0)