Skip to content

Commit 73b629a

Browse files
authored
Merge pull request #108 from dedis/front-ui-fix
Fixes frontend UI
2 parents b4c3875 + bb3ce4a commit 73b629a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1085
-942
lines changed

docs/api.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ SC5:Close │ │
4848
│ │ NS2:Shuffle
4949
│ │
5050
│ ▼
51-
│ DK4:BeginDecryption
51+
│ DK4:ComputePubshares
5252
5353
5454
SC6:CombineShares
@@ -370,7 +370,7 @@ Return:
370370

371371
```json
372372
{
373-
"Action": "beginDecryption"
373+
"Action": "computePubshares"
374374
}
375375
```
376376

web/frontend/src/Routes.ts

-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@ export const ROUTE_ELECTION_CREATE = '/election/create';
99
export const ROUTE_ELECTION_SHOW = '/elections/:electionID';
1010
export const ROUTE_ELECTION_RESULT = '/elections/:electionID/result';
1111

12-
export const ROUTE_BALLOT_INDEX = '/ballot/index';
1312
export const ROUTE_BALLOT_CAST = '/ballot/cast';
1413
export const ROUTE_BALLOT_SHOW = '/ballot/show';

web/frontend/src/components/modal/AddAdminUserModal.tsx

+156-76
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,186 @@
1-
import React, { FC, useState } from 'react';
2-
import Box from '@mui/material/Box';
3-
import Button from '@mui/material/Button';
4-
import Typography from '@mui/material/Typography';
5-
import Modal from '@mui/material/Modal';
6-
import Input from '@mui/material/Input';
7-
import InputLabel from '@mui/material/InputLabel';
8-
import MenuItem from '@mui/material/MenuItem';
9-
import FormControl from '@mui/material/FormControl';
10-
import Select from '@mui/material/Select';
1+
import React, { FC, Fragment, useContext, useRef, useState } from 'react';
112
import PropTypes from 'prop-types';
3+
import { Dialog, Listbox, Transition } from '@headlessui/react';
4+
import { CheckIcon, SelectorIcon } from '@heroicons/react/solid';
125

136
import { ENDPOINT_ADD_ROLE } from 'components/utils/Endpoints';
7+
import { useTranslation } from 'react-i18next';
8+
import SpinnerIcon from 'components/utils/SpinnerIcon';
9+
import { UserAddIcon } from '@heroicons/react/outline';
10+
import ShortUniqueId from 'short-unique-id';
11+
import { FlashContext, FlashLevel } from 'index';
1412

15-
const style = {
16-
position: 'absolute',
17-
top: '50%',
18-
left: '50%',
19-
transform: 'translate(-50%, -50%)',
20-
width: 400,
21-
bgcolor: 'background.paper',
22-
border: '2px solid #000',
23-
boxShadow: 24,
24-
p: 4,
25-
};
13+
const uid = new ShortUniqueId({ length: 8 });
2614

2715
type AddAdminUserModalProps = {
2816
open: boolean;
2917
setOpen(opened: boolean): void;
18+
handleAddRoleUser(user: object): void;
3019
};
3120

32-
const AddAdminUserModal: FC<AddAdminUserModalProps> = ({ open, setOpen }) => {
33-
const handleClose = () => setOpen(false);
34-
const ariaLabel = { 'aria-label': 'description' };
21+
const roles = ['Admin', 'Operator'];
3522

36-
const [sciperValue, setSciperValue] = useState('');
23+
const AddAdminUserModal: FC<AddAdminUserModalProps> = ({ open, setOpen, handleAddRoleUser }) => {
24+
const { t } = useTranslation();
25+
const fctx = useContext(FlashContext);
3726

38-
const [roleValue, setRoleValue] = useState('');
27+
const [loading, setLoading] = useState(false);
28+
const [sciperValue, setSciperValue] = useState('');
29+
const [selectedRole, setSelectedRole] = useState(roles[0]);
3930

40-
const handleChange = (event: any) => {
41-
setRoleValue(event.target.value);
42-
};
31+
const handleClose = () => setOpen(false);
4332

4433
const handleUserInput = (e: any) => {
4534
setSciperValue(e.target.value);
4635
};
4736

48-
const handleClick = () => {
37+
const handleAddUser = async () => {
38+
const userToAdd = { id: uid(), sciper: sciperValue, role: selectedRole };
4939
const requestOptions = {
5040
method: 'POST',
5141
headers: { 'Content-Type': 'application/json' },
52-
body: JSON.stringify({ sciper: sciperValue, role: roleValue }),
42+
body: JSON.stringify(userToAdd),
5343
};
54-
fetch(ENDPOINT_ADD_ROLE, requestOptions).then((data) => {
55-
if (data.status === 200) {
56-
alert('User added successfully');
57-
setOpen(false);
44+
45+
try {
46+
setLoading(true);
47+
const res = await fetch(ENDPOINT_ADD_ROLE, requestOptions);
48+
if (res.status !== 200) {
49+
const response = await res.text();
50+
fctx.addMessage(
51+
`Error HTTP ${res.status} (${res.statusText}) : ${response}`,
52+
FlashLevel.Error
53+
);
5854
} else {
59-
alert('Error while adding the user');
55+
setSciperValue('');
56+
setSelectedRole(roles[0]);
57+
handleAddRoleUser(userToAdd);
58+
fctx.addMessage(`${t('successAddUser')}`, FlashLevel.Info);
6059
}
61-
});
60+
} catch (error) {
61+
fctx.addMessage(`${t('errorAddUser')}: ${error.message}`, FlashLevel.Error);
62+
}
63+
setLoading(false);
64+
setOpen(false);
6265
};
66+
const cancelButtonRef = useRef(null);
6367

6468
return (
65-
<div>
66-
<Modal
67-
open={open}
68-
onClose={handleClose}
69-
aria-labelledby="modal-title"
70-
aria-describedby="modal-description">
71-
<Box sx={style}>
72-
<Typography variant="h6" component="h2">
73-
Please give the sciper of the user
74-
</Typography>
75-
<Input
76-
value={sciperValue}
77-
onChange={handleUserInput}
78-
placeholder="Sciper"
79-
inputProps={ariaLabel}
80-
/>
81-
<br />
82-
<br />
83-
<Box sx={{ minWidth: 40 }}>
84-
<FormControl fullWidth>
85-
<InputLabel id="select-label-role">Role</InputLabel>
86-
<Select
87-
labelId="select-label-role"
88-
id="select-role"
89-
value={roleValue}
90-
label="Role"
91-
onChange={handleChange}>
92-
<MenuItem value={'operator'}>Operator</MenuItem>
93-
<MenuItem value={'admin'}>Admin</MenuItem>
94-
</Select>
95-
</FormControl>
96-
</Box>
97-
<br />
98-
<Button onClick={handleClick} variant="contained">
99-
Add User
100-
</Button>
101-
</Box>
102-
</Modal>
103-
</div>
69+
<Transition.Root show={open} as={Fragment}>
70+
<Dialog
71+
as="div"
72+
className="fixed z-10 inset-0 px-4 sm:px-0 overflow-y-auto"
73+
initialFocus={cancelButtonRef}
74+
onClose={setOpen}>
75+
<div className="block items-end justify-center min-h-screen text-center">
76+
<Transition.Child
77+
as={Fragment}
78+
enter="ease-out duration-300"
79+
enterFrom="opacity-0"
80+
enterTo="opacity-100"
81+
leave="ease-in duration-200"
82+
leaveFrom="opacity-100"
83+
leaveTo="opacity-0">
84+
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
85+
</Transition.Child>
86+
87+
{/* This element is to trick the browser into centering the modal contents. */}
88+
<span className="inline-block align-middle h-screen" aria-hidden="true">
89+
&#8203;
90+
</span>
91+
<Transition.Child
92+
as={Fragment}
93+
enter="ease-out duration-300"
94+
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
95+
enterTo="opacity-100 translate-y-0 sm:scale-100"
96+
leave="ease-in duration-200"
97+
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
98+
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95">
99+
<div className="inline-block bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all my-8 align-middle max-w-lg w-full p-6">
100+
<div>
101+
<div className="text-center">
102+
<Dialog.Title as="h3" className="text-lg leading-6 font-medium text-gray-900">
103+
{t('enterSciper')}
104+
</Dialog.Title>
105+
<input
106+
onChange={handleUserInput}
107+
value={sciperValue}
108+
placeholder="Sciper"
109+
className="mt-8 mb-4 border pl-2 w-1/2 py-1 flex rounded-lg"
110+
/>
111+
<div className="mt-2 pb-4">
112+
<Listbox value={selectedRole} onChange={setSelectedRole}>
113+
<div className="relative mt-1">
114+
<Listbox.Button className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left border focus:outline-none focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm">
115+
<span className="block truncate">{selectedRole}</span>
116+
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2">
117+
<SelectorIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
118+
</span>
119+
</Listbox.Button>
120+
<Transition
121+
as={Fragment}
122+
leave="transition ease-in duration-100"
123+
leaveFrom="opacity-100"
124+
leaveTo="opacity-0">
125+
<Listbox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
126+
{roles.map((role, personIdx) => (
127+
<Listbox.Option
128+
key={personIdx}
129+
className={({ active }) =>
130+
`relative cursor-default select-none py-2 pl-10 pr-4 ${
131+
active ? 'bg-indigo-100 text-indigo-900' : 'text-gray-900'
132+
}`
133+
}
134+
value={role}>
135+
{({ selected }) => (
136+
<>
137+
<span
138+
className={`block truncate ${
139+
selected ? 'font-medium' : 'font-normal'
140+
}`}>
141+
{role}
142+
</span>
143+
{selected ? (
144+
<span className="absolute inset-y-0 left-0 flex items-center pl-3 text-indigo-600">
145+
<CheckIcon className="h-5 w-5" aria-hidden="true" />
146+
</span>
147+
) : null}
148+
</>
149+
)}
150+
</Listbox.Option>
151+
))}
152+
</Listbox.Options>
153+
</Transition>
154+
</div>
155+
</Listbox>
156+
</div>
157+
</div>
158+
</div>
159+
<div className="mt-5 sm:mt-6 sm:grid sm:grid-cols-2 sm:gap-3 sm:grid-flow-row-dense">
160+
<button
161+
type="button"
162+
className="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:col-start-2 sm:text-sm"
163+
onClick={handleAddUser}>
164+
{loading ? (
165+
<SpinnerIcon />
166+
) : (
167+
<UserAddIcon className="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
168+
)}
169+
{t('addUser')}
170+
</button>
171+
<button
172+
type="button"
173+
className="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:col-start-1 sm:text-sm"
174+
onClick={handleClose}
175+
ref={cancelButtonRef}>
176+
{t('cancel')}
177+
</button>
178+
</div>
179+
</div>
180+
</Transition.Child>
181+
</div>
182+
</Dialog>
183+
</Transition.Root>
104184
);
105185
};
106186

0 commit comments

Comments
 (0)