Skip to content

Commit b5e4e55

Browse files
committed
Add input fields to optionally specify landscape token and secret
1 parent 955234a commit b5e4e55

3 files changed

Lines changed: 119 additions & 25 deletions

File tree

‎src/components/token-creation-modal.tsx‎

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,61 @@
1-
import React, { useState } from 'react';
2-
import { Modal, Button, Form } from 'react-bootstrap';
1+
import React, { useEffect, useState } from 'react';
2+
import { Button, Form, Modal } from 'react-bootstrap';
3+
4+
export type LandscapeTokenCreateParams = {
5+
alias: string;
6+
value?: string;
7+
secret?: string;
8+
};
39

410
interface TokenCreationModalProps {
511
show: boolean;
612
handleClose: () => void;
7-
createToken: (tokenAlias: string) => Promise<void>;
13+
createToken: (params: LandscapeTokenCreateParams) => Promise<void>;
814
}
915

16+
const emptyFormState = (): LandscapeTokenCreateParams => ({
17+
alias: '',
18+
value: '',
19+
secret: '',
20+
});
21+
1022
export default function TokenCreationModal({
1123
show,
1224
handleClose,
1325
createToken,
1426
}: TokenCreationModalProps) {
15-
const [tokenAlias, setTokenAlias] = useState('');
27+
const [formState, setFormState] =
28+
useState<LandscapeTokenCreateParams>(emptyFormState);
29+
const [isSubmitting, setIsSubmitting] = useState(false);
1630

17-
const handleSubmit = (e: React.FormEvent) => {
31+
useEffect(() => {
32+
if (!show) {
33+
setFormState(emptyFormState());
34+
setIsSubmitting(false);
35+
}
36+
}, [show]);
37+
38+
const handleSubmit = async (e: React.FormEvent) => {
1839
e.preventDefault();
19-
createToken(tokenAlias);
20-
handleClose();
40+
setIsSubmitting(true);
41+
42+
const params: LandscapeTokenCreateParams = {
43+
alias: formState.alias.trim(),
44+
};
45+
const value = formState.value?.trim();
46+
const secret = formState.secret?.trim();
47+
if (value) {
48+
params.value = value;
49+
}
50+
if (secret) {
51+
params.secret = secret;
52+
}
53+
54+
try {
55+
await createToken(params);
56+
} finally {
57+
setIsSubmitting(false);
58+
}
2159
};
2260

2361
return (
@@ -27,20 +65,54 @@ export default function TokenCreationModal({
2765
</Modal.Header>
2866
<Modal.Body>
2967
<Form onSubmit={handleSubmit}>
30-
<Form.Group controlId="tokenAlias">
31-
<Form.Label>Alias (may be left empty)</Form.Label>
68+
<Form.Group controlId="tokenAlias" className="mb-3">
69+
<Form.Label>Alias (optional)</Form.Label>
3270
<Form.Control
3371
type="text"
3472
autoFocus
35-
value={tokenAlias}
36-
onChange={(e) => setTokenAlias(e.target.value)}
73+
value={formState.alias}
74+
onChange={(e) =>
75+
setFormState((prev) => ({ ...prev, alias: e.target.value }))
76+
}
77+
/>
78+
</Form.Group>
79+
<Form.Group controlId="tokenValue" className="mb-3">
80+
<Form.Label>Landscape Token (optional)</Form.Label>
81+
<Form.Control
82+
type="text"
83+
placeholder="Leave empty to generate automatically"
84+
value={formState.value ?? ''}
85+
onChange={(e) =>
86+
setFormState((prev) => ({ ...prev, value: e.target.value }))
87+
}
88+
/>
89+
<Form.Text className="text-muted">
90+
Specify a custom token value or leave empty to generate one.
91+
</Form.Text>
92+
</Form.Group>
93+
<Form.Group controlId="tokenSecret" className="mb-3">
94+
<Form.Label>Token Secret (optional)</Form.Label>
95+
<Form.Control
96+
type="text"
97+
placeholder="Leave empty to generate automatically"
98+
value={formState.secret ?? ''}
99+
onChange={(e) =>
100+
setFormState((prev) => ({ ...prev, secret: e.target.value }))
101+
}
37102
/>
103+
<Form.Text className="text-muted">
104+
Specify a custom token secret or leave empty to generate one.
105+
</Form.Text>
38106
</Form.Group>
39107
<Modal.Footer>
40108
<Button variant="outline-secondary" onClick={handleClose}>
41109
Cancel
42110
</Button>
43-
<Button type="submit" variant="outline-primary">
111+
<Button
112+
type="submit"
113+
variant="outline-primary"
114+
disabled={isSubmitting}
115+
>
44116
Create
45117
</Button>
46118
</Modal.Footer>

‎src/pages/landscapes.tsx‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import SnapshotSelection from 'explorviz-frontend/src/components/snapshot-selection';
2-
import TokenCreationModal from 'explorviz-frontend/src/components/token-creation-modal';
2+
import TokenCreationModal, {
3+
LandscapeTokenCreateParams,
4+
} from 'explorviz-frontend/src/components/token-creation-modal';
35
import TokenSelection from 'explorviz-frontend/src/components/token-selection';
46
import LandscapeLoader from 'explorviz-frontend/src/pages/landscapes-loading';
57
import { useAuthStore } from 'explorviz-frontend/src/stores/auth';
@@ -129,9 +131,9 @@ export default function Landscapes() {
129131
});
130132
};
131133

132-
const createToken = async (tokenAlias: string) => {
134+
const createToken = async (params: LandscapeTokenCreateParams) => {
133135
try {
134-
const token = await sendTokenCreateRequest(tokenAlias);
136+
const token = await sendTokenCreateRequest(params);
135137
closeTokenCreationModal();
136138
useToastHandlerStore
137139
.getState()
@@ -159,7 +161,7 @@ export default function Landscapes() {
159161
refreshRoute();
160162
};
161163

162-
const sendTokenCreateRequest = (alias = '') => {
164+
const sendTokenCreateRequest = (params: LandscapeTokenCreateParams) => {
163165
let uId = user?.sub;
164166

165167
if (!uId) {
@@ -168,19 +170,27 @@ export default function Landscapes() {
168170

169171
uId = encodeURI(uId);
170172

173+
const body: Record<string, string | boolean> = {
174+
alias: params.alias,
175+
isRequestedFromVSCodeExtension: false,
176+
projectName: '',
177+
commitId: '',
178+
};
179+
if (params.value) {
180+
body.value = params.value;
181+
}
182+
if (params.secret) {
183+
body.secret = params.secret;
184+
}
185+
171186
return new Promise<any>((resolve, reject) => {
172187
fetch(`${import.meta.env.VITE_USER_SERV_URL}/user/${uId}/token`, {
173188
headers: {
174189
Authorization: `Bearer ${accessToken}`,
175190
'Content-Type': 'application/json',
176191
},
177192
method: 'POST',
178-
body: JSON.stringify({
179-
alias,
180-
isRequestedFromVSCodeExtension: false,
181-
projectName: '',
182-
commitId: '',
183-
}),
193+
body: JSON.stringify(body),
184194
})
185195
.then(async (response: Response) => {
186196
if (response.ok) {

‎src/stores/landscape-token.ts‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ interface LandscapeTokenState {
1616
_isValidToken: (token: unknown) => token is LandscapeToken;
1717
_isStringArray: (possibleArray: unknown) => possibleArray is string[];
1818
_isObject: (variable: unknown) => variable is object;
19-
createToken: (alias: string) => Promise<LandscapeToken>;
19+
createToken: (params: {
20+
alias: string;
21+
value?: string;
22+
secret?: string;
23+
}) => Promise<LandscapeToken>;
2024
updateTokenAlias: (tokenValue: string, alias: string) => Promise<void>;
2125
}
2226

@@ -138,19 +142,27 @@ export const useLandscapeTokenStore = create<LandscapeTokenState>(
138142
_isObject: (variable: unknown): variable is object => {
139143
return Object.prototype.toString.call(variable) === '[object Object]';
140144
},
141-
createToken: async (alias: string) => {
145+
createToken: async ({ alias, value, secret }) => {
142146
const userId = encodeURI(useAuthStore.getState().user?.sub || '');
143147
if (!userId) {
144148
throw new Error('User not authenticated');
145149
}
146150

151+
const body: Record<string, string> = { alias };
152+
if (value) {
153+
body.value = value;
154+
}
155+
if (secret) {
156+
body.secret = secret;
157+
}
158+
147159
const response = await fetch(`${userService}/user/${userId}/token`, {
148160
method: 'POST',
149161
headers: {
150162
'Content-Type': 'application/json',
151163
Authorization: `Bearer ${useAuthStore.getState().accessToken}`,
152164
},
153-
body: JSON.stringify({ alias }),
165+
body: JSON.stringify(body),
154166
});
155167

156168
if (!response.ok) {

0 commit comments

Comments
 (0)