admin_lsac 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/README.md +162 -162
  3. package/index.html +13 -0
  4. package/package.json +42 -73
  5. package/tsconfig.app.json +27 -0
  6. package/tsconfig.json +11 -31
  7. package/tsconfig.node.json +13 -0
  8. package/vite.config.ts +7 -0
  9. package/dist/App.d.ts +0 -76
  10. package/dist/App.js +0 -12
  11. package/dist/cjs/App.js +0 -12
  12. package/dist/cjs/components/GenericForm.js +0 -305
  13. package/dist/cjs/components/Page.js +0 -119
  14. package/dist/cjs/index.js +0 -18
  15. package/dist/cjs/pages/event.js +0 -134
  16. package/dist/cjs/pages/page.js +0 -375
  17. package/dist/cjs/reportWebVitals.js +0 -38
  18. package/dist/cjs/schema/schema.config.js +0 -9
  19. package/dist/cjs/schema/schema.js +0 -2
  20. package/dist/cjs/setupTests.js +0 -7
  21. package/dist/cjs/utils/websockets.js +0 -22
  22. package/dist/components/GenericForm.js +0 -305
  23. package/dist/components/Page.js +0 -119
  24. package/dist/esm/App.js +0 -10
  25. package/dist/esm/components/GenericForm.js +0 -275
  26. package/dist/esm/components/Page.js +0 -111
  27. package/dist/esm/index.js +0 -13
  28. package/dist/esm/pages/event.js +0 -128
  29. package/dist/esm/pages/page.js +0 -349
  30. package/dist/esm/reportWebVitals.js +0 -13
  31. package/dist/esm/schema/schema.config.js +0 -6
  32. package/dist/esm/schema/schema.js +0 -1
  33. package/dist/esm/setupTests.js +0 -5
  34. package/dist/esm/utils/websockets.js +0 -15
  35. package/dist/index.js +0 -18
  36. package/dist/pages/event.js +0 -134
  37. package/dist/pages/page.js +0 -375
  38. package/dist/reportWebVitals.js +0 -38
  39. package/dist/schema/schema.config.js +0 -9
  40. package/dist/schema/schema.js +0 -2
  41. package/dist/setupTests.js +0 -7
  42. package/dist/utils/websockets.js +0 -22
  43. package/public/favicon.ico +0 -0
  44. package/public/index.html +0 -43
  45. package/public/logo192.png +0 -0
  46. package/public/logo512.png +0 -0
  47. package/public/manifest.json +0 -25
  48. package/public/robots.txt +0 -3
  49. package/server/.env.example +0 -3
  50. package/server/deploy.js +0 -29
  51. package/server/package-lock.json +0 -712
  52. package/server/package.json +0 -18
  53. package/src/App.css +0 -38
  54. package/src/App.tsx +0 -50
  55. package/src/components/GenericForm.tsx +0 -308
  56. package/src/components/Page.tsx +0 -243
  57. package/src/index.css +0 -13
  58. package/src/index.tsx +0 -20
  59. package/src/logo.svg +0 -1
  60. package/src/pages/event.ts +0 -93
  61. package/src/pages/page.ts +0 -336
  62. package/src/react-app-env.d.ts +0 -1
  63. package/src/reportWebVitals.ts +0 -15
  64. package/src/schema/schema.config.ts +0 -8
  65. package/src/schema/schema.ts +0 -63
  66. package/src/setupTests.ts +0 -5
  67. package/src/utils/websockets.tsx +0 -17
  68. package/tsconfig.esm.json +0 -7
  69. package/webpack.config.js +0 -22
@@ -1,18 +0,0 @@
1
- {
2
- "name": "server",
3
- "version": "1.0.0",
4
- "description": "",
5
- "main": "deploy.js",
6
- "scripts": {
7
- "start": "node deploy.js"
8
- },
9
- "author": "",
10
- "license": "ISC",
11
- "dependencies": {
12
- "dotenv": "^16.0.1",
13
- "express": "^4.18.1",
14
- "fs": "^0.0.1-security",
15
- "https": "^1.0.0",
16
- "path": "^0.12.7"
17
- }
18
- }
package/src/App.css DELETED
@@ -1,38 +0,0 @@
1
- .App {
2
- text-align: center;
3
- }
4
-
5
- .App-logo {
6
- height: 40vmin;
7
- pointer-events: none;
8
- }
9
-
10
- @media (prefers-reduced-motion: no-preference) {
11
- .App-logo {
12
- animation: App-logo-spin infinite 20s linear;
13
- }
14
- }
15
-
16
- .App-header {
17
- background-color: #282c34;
18
- min-height: 100vh;
19
- display: flex;
20
- flex-direction: column;
21
- align-items: center;
22
- justify-content: center;
23
- font-size: calc(10px + 2vmin);
24
- color: white;
25
- }
26
-
27
- .App-link {
28
- color: #61dafb;
29
- }
30
-
31
- @keyframes App-logo-spin {
32
- from {
33
- transform: rotate(0deg);
34
- }
35
- to {
36
- transform: rotate(360deg);
37
- }
38
- }
package/src/App.tsx DELETED
@@ -1,50 +0,0 @@
1
- import { ChakraProvider } from "@chakra-ui/react";
2
- import { BrowserRouter, Route, Routes } from "react-router-dom";
3
- import { AdminPage } from "./components/Page";
4
- import { pageToUrl } from "./components/Page";
5
- import { PageSchema } from "./schema/schema";
6
-
7
- function App({
8
- pages,
9
- basePath,
10
- }: {
11
- pages: PageSchema[];
12
- basePath: string;
13
- }) {
14
- return (
15
- <ChakraProvider>
16
- <BrowserRouter>
17
- <Routes>
18
- {pages.map((page) => (
19
- <Route
20
- path={`${basePath}/${pageToUrl(page)}`}
21
- element={
22
- <AdminPage
23
- pages={pages}
24
- selectedPage={page}
25
- basePath={basePath}
26
- apiURL="http://localhost:3001"
27
- />
28
- }
29
- />
30
- ))}
31
- {pages[0] ? (
32
- <Route
33
- path={`${basePath}/*`}
34
- element={
35
- <AdminPage
36
- pages={pages}
37
- selectedPage={pages[0]}
38
- basePath={basePath}
39
- apiURL="http://localhost:3001"
40
- />
41
- }
42
- />
43
- ) : null}
44
- </Routes>
45
- </BrowserRouter>
46
- </ChakraProvider>
47
- );
48
- }
49
-
50
- export default App;
@@ -1,308 +0,0 @@
1
- import {
2
- Box,
3
- Button,
4
- FormControl,
5
- FormErrorMessage,
6
- FormLabel,
7
- Input,
8
- Select,
9
- } from "@chakra-ui/react";
10
- import { Field, Form, Formik, FieldArray, getIn } from "formik";
11
- import {
12
- Action,
13
- FormField,
14
- SimpleField,
15
- DropdownField,
16
- RowAction,
17
- } from "../schema/schema";
18
- import * as Yup from "yup";
19
- import Swal from "sweetalert2";
20
- import { useEffect, useState } from "react";
21
-
22
- interface RowActionWithInitialise extends RowAction {
23
- initialise: (obj: any) => any;
24
- }
25
-
26
- interface GenericFormProps {
27
- action: Action | RowAction;
28
- onClose?: () => void;
29
- rowId?: number;
30
- }
31
-
32
- function parseValidation(fields: FormField[] | undefined) {
33
- if (!fields) {
34
- return {};
35
- }
36
-
37
- return fields.reduce((acc: any, field) => {
38
- if (
39
- field.type !== "dropdown" &&
40
- field.type !== "object" &&
41
- field.type !== "array"
42
- ) {
43
- acc[field.name] = field.validationSchema;
44
- } else if (field.type === "array") {
45
- if (field.element.type === "dropdown") {
46
- acc[field.name] = Yup.array().of(Yup.string().required());
47
- } else if (field.element.type === "object") {
48
- acc[field.name] = Yup.array().of(
49
- Yup.object(parseValidation(field.element.fields))
50
- );
51
- } else {
52
- acc[field.name] = Yup.array().of(field.element.validationSchema);
53
- }
54
- } else if (field.type === "object") {
55
- acc[field.name] = Yup.object(parseValidation(field.fields));
56
- }
57
- if (field.type === "dropdown") {
58
- acc[field.name] = Yup.string().required();
59
- }
60
- return acc;
61
- }, {});
62
- }
63
-
64
- function generateSingleField(
65
- formfield: SimpleField | DropdownField,
66
- fieldName: string,
67
- key: any
68
- ) {
69
- return (
70
- <Field name={fieldName} key={key}>
71
- {({ field, form }: any) => (
72
- <FormControl
73
- isInvalid={
74
- getIn(form.touched, fieldName) && getIn(form.errors, fieldName)
75
- }
76
- >
77
- <FormLabel marginTop={2}>{formfield.label}</FormLabel>
78
-
79
- {formfield.type === "string" || formfield.type === "number" ? (
80
- <Input
81
- {...field}
82
- type={formfield.type === "number" ? "number" : "text"}
83
- placeholder=""
84
- />
85
- ) : formfield.type === "file" ? (
86
- <Input
87
- type="file"
88
- name={fieldName}
89
- onChange={(event) => {
90
- if (event.currentTarget.files != null) {
91
- form.setFieldValue(fieldName, event.currentTarget.files[0]);
92
- }
93
- }}
94
- />
95
- ) : (
96
- <Select {...field} defaultValue="">
97
- <option disabled={true} value="">
98
- Select an option!
99
- </option>
100
- {(formfield as DropdownField).options?.map((option, key) => (
101
- <option key={key} value={`${option.value}`}>
102
- {option.display}
103
- </option>
104
- ))}
105
- </Select>
106
- )}
107
-
108
- <FormErrorMessage>{getIn(form.errors, fieldName)}</FormErrorMessage>
109
- </FormControl>
110
- )}
111
- </Field>
112
- );
113
- }
114
-
115
- function parseFields(
116
- fields: FormField[] | undefined,
117
- previous?: string | undefined
118
- ) {
119
- if (!fields) {
120
- return [];
121
- }
122
-
123
- const computedFields = fields.map((formfield, key) => {
124
- let fieldName: string =
125
- previous !== undefined ? previous + "." + formfield.name : formfield.name;
126
-
127
- if (formfield.type === "object") {
128
- const objectFields = parseFields(formfield.fields, fieldName);
129
- return <FormControl key={fieldName}>{objectFields}</FormControl>;
130
- } else if (formfield.type === "array") {
131
- return (
132
- <FieldArray name={fieldName}>
133
- {({ remove, push, form }) => (
134
- <div>
135
- <FormLabel marginTop={2}>{formfield.label}</FormLabel>
136
- {getIn(form.values, fieldName).length > 0 &&
137
- getIn(form.values, fieldName).map(
138
- (element: any, index: number) => (
139
- <Box paddingLeft={10}>
140
- <h4>
141
- <b>{`${fieldName}.${index}`}</b>
142
- </h4>
143
- <Box paddingLeft={10}>
144
- {formfield.element.type === "object"
145
- ? parseFields(
146
- formfield.element.fields,
147
- `${fieldName}.${index}`
148
- )
149
- : generateSingleField(
150
- formfield.element,
151
- `${fieldName}.${index}`,
152
- key
153
- )}
154
- </Box>
155
- <Button
156
- type="button"
157
- colorScheme="red"
158
- marginY={2}
159
- onClick={() => remove(index)}
160
- >
161
- Remove {`${fieldName}.${index}`}
162
- </Button>
163
- </Box>
164
- )
165
- )}
166
-
167
- <Button
168
- type="button"
169
- marginY={2}
170
- colorScheme="blue"
171
- onClick={() => {
172
- let value = undefined;
173
-
174
- if (formfield.element.type === "object") {
175
- value = getCustomInitialValues(formfield.element.fields);
176
- } else {
177
- value = getCustomInitialValues([formfield.element])[0];
178
- }
179
-
180
- push(value);
181
- }}
182
- >
183
- Add {fieldName}
184
- </Button>
185
- </div>
186
- )}
187
- </FieldArray>
188
- );
189
- } else {
190
- return generateSingleField(formfield, fieldName, key);
191
- }
192
- });
193
-
194
- return computedFields;
195
- }
196
-
197
- function getCustomInitialValues(fields: FormField[] | undefined) {
198
- let obj: any = {};
199
-
200
- if (!fields) {
201
- return obj;
202
- }
203
-
204
- fields.forEach((field) => {
205
- if (field.type === "array") obj[field.name] = [];
206
- else if (field.type === "object")
207
- obj[field.name] = getCustomInitialValues(field.fields);
208
- else if (field.type === "string") obj[field.name] = "";
209
- else if (field.type === "number") obj[field.name] = 0;
210
- else obj[field.name] = null;
211
- });
212
-
213
- return obj;
214
- }
215
-
216
- async function getInitialValues(action: Action | RowAction, rowId: number | undefined) {
217
- let obj: any = {};
218
- if ((action as RowAction).initialise) {
219
- obj = await (action as RowActionWithInitialise).initialise(rowId);
220
- }
221
- const customInitialValues = getCustomInitialValues(action.fields);
222
- return Object.assign({}, customInitialValues, obj);
223
- }
224
-
225
- async function setOptions(fields: FormField[] | undefined) {
226
- if (!fields) {
227
- return;
228
- }
229
-
230
- const promises = fields.map(async (field) => {
231
- if (field.type === "dropdown") field.options = await field.getOptions();
232
- else if (field.type === "array") await setOptions([field.element]);
233
- else if (field.type === "object") await setOptions(field.fields);
234
- });
235
-
236
- await Promise.all(promises);
237
- }
238
-
239
- export function GenericForm({ action, onClose, rowId }: GenericFormProps) {
240
- const [formFields, setFormFields] = useState<any[]>(
241
- parseFields(action.fields)
242
- );
243
- const [initialValues, setInitialValues] = useState<any>(getCustomInitialValues(action.fields));
244
- const [validationSchema, setValidationSchema] = useState<any>(parseValidation(action.fields));
245
-
246
- useEffect(() => {
247
- setOptions(action.fields).then(() => {
248
- setFormFields(parseFields(action.fields));
249
- });
250
- }, []);
251
-
252
- const handleFormReset = async () => {
253
- const newInitialValues = await getInitialValues(action, rowId);
254
- console.log(newInitialValues)
255
- setInitialValues(newInitialValues);
256
- };
257
-
258
- if (!action.fields) return <></>;
259
- return (
260
- <>
261
- <Formik
262
- enableReinitialize={true}
263
- initialValues={initialValues}
264
- validationSchema={Yup.object(validationSchema)}
265
- onReset={handleFormReset}
266
- onSubmit={async (values) => {
267
- try {
268
- let res;
269
- if (rowId) res = await action.onSubmit(values, rowId);
270
- else res = await action.onSubmit(values);
271
-
272
- if (onClose) onClose();
273
-
274
- Swal.fire("Ok");
275
- } catch (e: any) {
276
- if (onClose) onClose();
277
- const err = e.request.response;
278
-
279
- Swal.fire({
280
- text: "Error: " + err,
281
- });
282
- }
283
- }}
284
- >
285
- {({ isSubmitting }) => (
286
- <Form>
287
- {formFields}
288
- <Button
289
- mt={4}
290
- colorScheme="teal"
291
- isLoading={isSubmitting}
292
- type="submit"
293
- >
294
- Submit
295
- </Button>
296
- <Button
297
- mt={4}
298
- colorScheme="teal"
299
- type="reset"
300
- >
301
- Initialise Form
302
- </Button>
303
- </Form>
304
- )}
305
- </Formik>
306
- </>
307
- );
308
- }
@@ -1,243 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { ChakraProvider } from "@chakra-ui/react";
3
- import { PageSchema, Action } from "../schema/schema";
4
- import {
5
- Button,
6
- Modal,
7
- ModalHeader,
8
- ModalBody,
9
- useDisclosure,
10
- ModalOverlay,
11
- ModalContent,
12
- ModalCloseButton,
13
- Box,
14
- Stack,
15
- } from "@chakra-ui/react";
16
- import { GenericForm } from "./GenericForm";
17
- import { ThemeProvider } from "@emotion/react";
18
- import { createTheme } from "@mui/material/styles";
19
- import { MaterialReactTable } from "material-react-table";
20
- import { mkConfig, generateCsv, download } from "export-to-csv";
21
- import Swal from "sweetalert2";
22
- import MenuItem from "@mui/material/MenuItem";
23
- import { io } from "socket.io-client";
24
- import { newSocket } from "../utils/websockets";
25
-
26
- export function pageToUrl(page: PageSchema): string {
27
- return `${page.name.toLowerCase().replace(" ", "_")}`;
28
- }
29
-
30
- function Header({
31
- pages,
32
- data,
33
- basePath,
34
- }: {
35
- pages: PageSchema[];
36
- data: any[];
37
- basePath: string;
38
- }) {
39
- return (
40
- <Box background="blue.50" padding={1}>
41
- <Stack
42
- margin={3}
43
- direction="row"
44
- justifyContent="space-between"
45
- alignItems="center"
46
- >
47
- <Stack margin={4} gap={10} direction="row" alignItems="center">
48
- {pages.map((p) => (
49
- <Box as="a" href={`${basePath}/${pageToUrl(p)}`}>
50
- {p.name}
51
- </Box>
52
- ))}
53
- </Stack>
54
- <Stack direction="row">
55
- <Button
56
- colorScheme="red"
57
- onClick={() => {
58
- const filename = prompt("Enter filename:");
59
- if (filename) exportToCSV(data, filename);
60
- }}
61
- >
62
- Export Data
63
- </Button>
64
- </Stack>
65
- </Stack>
66
- </Box>
67
- );
68
- }
69
-
70
- function exportToCSV(data: any[], filename: string) {
71
- const formatImageData = (obj: any) => {
72
- const formattedObj: any = {};
73
- for (const key in obj) {
74
- if (typeof obj[key] !== "object") {
75
- formattedObj[key] = obj[key];
76
- } else if (typeof obj[key] === "object" && obj[key] != null) {
77
- Object.keys(obj[key]).forEach((k: any) => {
78
- formattedObj[`${key}.${k}`] = obj[key][k];
79
- });
80
- }
81
- }
82
- return formattedObj;
83
- };
84
-
85
- const formattedData = data.map(formatImageData);
86
-
87
- const csvConfig = mkConfig({
88
- useKeysAsHeaders: true,
89
- filename: filename,
90
- });
91
-
92
- if (formattedData.length > 0) {
93
- const csv = generateCsv(csvConfig)(formattedData);
94
- download(csvConfig)(csv);
95
- } else {
96
- Swal.fire({
97
- title: "Error while exporting CSV!",
98
- text: "There is no data to export.",
99
- });
100
- }
101
- }
102
-
103
- export function AdminPage({
104
- pages,
105
- selectedPage,
106
- basePath,
107
- apiURL=''
108
- }: {
109
- pages: PageSchema[];
110
- selectedPage: PageSchema;
111
- basePath: string;
112
- apiURL?: string;
113
- }) {
114
- const { isOpen, onOpen, onClose } = useDisclosure();
115
- const [selectedAction, setSelectedAction] = useState({} as Action);
116
- const [data, setData] = useState<any[]>([]);
117
- const [displayData, setDisplayData] = useState<any[][]>([]);
118
- const [rowId, setRowId] = useState<number>();
119
- const [refetch, setRefetch] = useState<boolean>(false);
120
- const [socket, setSocket] = useState(io);
121
- newSocket(setSocket, apiURL);
122
-
123
- useEffect(() => {
124
- selectedPage.getRequest().then((resArray) => {
125
- setData(resArray);
126
- setRefetch(false);
127
- });
128
- }, [refetch]); // Do not remove 'refetch' from here !!!
129
-
130
- useEffect(() => {
131
- socket.on("updateData", (message) => {
132
- console.log("Message emitted from socket: " + message);
133
- setRefetch(true);
134
- const rowActionsMenu = document.querySelector(
135
- ".MuiPaper-root.MuiPopover-paper.MuiMenu-paper"
136
- );
137
- if (rowActionsMenu) {
138
- rowActionsMenu.remove();
139
- }
140
- });
141
- }, [socket, selectedPage]);
142
-
143
- useEffect(() => {
144
- setDisplayData(
145
- data.map((table_data) =>
146
- selectedPage.tableFields.map((field) => field.access(table_data))
147
- )
148
- );
149
- }, [data]);
150
-
151
- return (
152
- <>
153
- <Header pages={pages} data={data} basePath={basePath} />
154
-
155
- <ThemeProvider theme={createTheme()}>
156
- <MaterialReactTable
157
- columns={selectedPage.tableFields.map((field, index) => ({
158
- accessorFn: (row: any) => row[index],
159
- header: field.name,
160
- }))}
161
- data={displayData}
162
- muiTableContainerProps={{ sx: { maxHeight: "70vh" } }}
163
- enableColumnResizing
164
- enableColumnPinning
165
- enableGlobalFilterModes
166
- enablePagination={true}
167
- paginationDisplayMode="pages"
168
- enableRowVirtualization
169
- rowVirtualizerOptions={{ overscan: 20 }}
170
- renderTopToolbarCustomActions={() => (
171
- <ChakraProvider>
172
- <Stack margin={0} padding={3} direction="row">
173
- {selectedPage.actions.map((action) => (
174
- <Button
175
- colorScheme="blue"
176
- onClick={() => {
177
- setSelectedAction(action);
178
- onOpen();
179
- }}
180
- >
181
- {action.name}
182
- </Button>
183
- ))}
184
- </Stack>
185
- </ChakraProvider>
186
- )}
187
- enableRowActions
188
- renderRowActionMenuItems={(obj: any) => {
189
- return selectedPage.rowActions.map((rowAction) => (
190
- <MenuItem
191
- onClick={() => {
192
- setSelectedAction(rowAction);
193
- setRowId(obj.row.original[0]);
194
- onOpen();
195
-
196
- const rowActionsMenu = document.querySelector(
197
- ".MuiPaper-root.MuiPopover-paper.MuiMenu-paper"
198
- );
199
- if (rowActionsMenu) {
200
- rowActionsMenu.remove();
201
- }
202
- }}
203
- >
204
- {rowAction.name + " " + obj.row.original[0]}
205
- </MenuItem>
206
- ));
207
- }}
208
- />
209
- </ThemeProvider>
210
-
211
- <Modal size={"6xl"} isOpen={isOpen} onClose={onClose}>
212
- <ModalOverlay />
213
- <ModalContent>
214
- <ModalHeader>{selectedAction.name}</ModalHeader>
215
- <ModalCloseButton />
216
- <ModalBody>
217
- {selectedAction.fields && selectedAction.fields?.length > 0 ? (
218
- <GenericForm
219
- action={selectedAction}
220
- onClose={onClose}
221
- rowId={rowId}
222
- />
223
- ) : (
224
- <Button
225
- colorScheme="blue"
226
- mr={3}
227
- onClick={() => {
228
- selectedAction.onSubmit(rowId);
229
- onClose();
230
- }}
231
- >
232
- Confirm Action
233
- </Button>
234
- )}
235
- <Button colorScheme="red" mr={3} onClick={onClose}>
236
- Close
237
- </Button>
238
- </ModalBody>
239
- </ModalContent>
240
- </Modal>
241
- </>
242
- );
243
- }
package/src/index.css DELETED
@@ -1,13 +0,0 @@
1
- html, body {
2
- margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
- sans-serif;
6
- -webkit-font-smoothing: antialiased;
7
- -moz-osx-font-smoothing: grayscale;
8
- }
9
-
10
- code {
11
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12
- monospace;
13
- }
package/src/index.tsx DELETED
@@ -1,20 +0,0 @@
1
- import React from "react";
2
- import ReactDOM from "react-dom/client";
3
- import "./index.css";
4
- import App from "./App";
5
- import reportWebVitals from "./reportWebVitals";
6
- import { pages } from "./schema/schema.config";
7
-
8
- const root = ReactDOM.createRoot(
9
- document.getElementById("root") as HTMLElement
10
- );
11
- root.render(
12
- <React.StrictMode>
13
- <App pages={pages} basePath="" />
14
- </React.StrictMode>
15
- );
16
-
17
- // If you want to start measuring performance in your app, pass a function
18
- // to log results (for example: reportWebVitals(console.log))
19
- // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
20
- reportWebVitals();
package/src/logo.svg DELETED
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>