admin_lsac 1.4.9 → 2.0.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.
package/.eslintrc.cjs CHANGED
@@ -6,7 +6,7 @@ module.exports = {
6
6
  'plugin:@typescript-eslint/recommended',
7
7
  'plugin:react-hooks/recommended',
8
8
  ],
9
- ignorePatterns: ['dist', '.eslintrc.cjs', 'src/testSchemas', 'admin_lsac.d.ts'],
9
+ ignorePatterns: ['dist', '.eslintrc.cjs', 'server'],
10
10
  parser: '@typescript-eslint/parser',
11
11
  plugins: ['react-refresh'],
12
12
  rules: {
@@ -14,5 +14,7 @@ module.exports = {
14
14
  'warn',
15
15
  { allowConstantExport: true },
16
16
  ],
17
+ '@typescript-eslint/no-explicit-any': 'off',
18
+ '@typescript-eslint/no-unused-vars': 'off',
17
19
  },
18
20
  }
package/README.md CHANGED
@@ -2,29 +2,29 @@
2
2
 
3
3
  For configuring your own admin panel, follow the next instructions.
4
4
 
5
- ### `npm install admin_lsac`
5
+ ## How to install it?
6
6
 
7
- This command will download and install the specified package and its dependencies into your project's node_modules directory, making it available for use in your project.
8
-
9
- ### `npm install export-to-csv@1.2.1`
7
+ - `npm install admin_lsac`
10
8
 
11
- Export-to-csv package latest version is not working properly, so make sure you have the last functional version of this package install.
9
+ This command will download and install the specified package and its dependencies into your project's node_modules directory, making it available for use in your project.
12
10
 
13
11
  ## What do you need to import?
14
12
 
15
- ### `import { ChakraProvider } from '@chakra-ui/react'`
13
+ - `import { ChakraProvider } from '@chakra-ui/react'`
16
14
 
17
- ### `import { pageToUrl } from 'admin_lsac'`
15
+ - `import { pageToUrl } from 'admin_lsac'`
18
16
 
19
- ### `import { AdminPage } from 'admin_lsac'`
17
+ - `import { AdminPage } from 'admin_lsac'`
20
18
 
21
- You can import these in App.js / App.ts. If your project is TypeScript-based, you need to write the following line as well:
19
+ You can import these in App.js / App.ts or wherever your routes to the pages are located.
22
20
 
23
- ### `import { PageSchema } from 'admin_lsac'`
21
+ - `import { PageSchema } from 'admin_lsac'`
24
22
 
25
- * Import the component from above where you want to write your schemas. (See below for details about schemas)
23
+ If your project is TypeScript-based, you need to use the line above as well.
26
24
 
27
- You need to create an array of schemas for your own admin panel. You can write the following inside your Routes component from App.js / App.ts:
25
+ Import the component from above where you want to write your schemas! (See below for details about schemas)
26
+
27
+ ## How to add routes to the admin panel's pages?
28
28
 
29
29
  ```
30
30
  {your_array.map((page) => (
@@ -39,7 +39,7 @@ You need to create an array of schemas for your own admin panel. You can write t
39
39
  }
40
40
  />
41
41
  ))}
42
- {your_array[0] ? (
42
+ {your_array[0] ? ( // recommended default route when accessing admin panel
43
43
  <Route
44
44
  path={`${your_url_to_admin_panel}/*`}
45
45
  element={
@@ -53,7 +53,9 @@ You need to create an array of schemas for your own admin panel. You can write t
53
53
  ) : null}
54
54
  ```
55
55
 
56
- #### ⚠️ Don't forget to wrap the Router component into a ChakraProvider component.
56
+ You will need to create an array of schemas for your own admin panel.
57
+
58
+ #### ⚠️ Don't forget to wrap the Router component into a ChakraProvider component. Otherwise, you will not like the way it looks. :)
57
59
 
58
60
  ## What is a schema?
59
61
 
@@ -121,9 +123,165 @@ export interface RowAction {
121
123
  }
122
124
  ```
123
125
 
124
- A page schema is based on these interfaces, which can be imported into your files. A page schema describes how one table of your admin panel would need to be structured to accomplish what the user needs regarding an entity. (i.e. users, teams, companies, quizzes)
126
+ A page schema is based on these interfaces, and it describes how one table of your admin panel would need to be structured to accomplish what the user needs to do regarding an entity. (i.e. users, teams, companies, quizzes)
127
+
128
+ The most important interface is `PageSchema`, as it is at the top of the relations between all these interfaces.
129
+
130
+ What do the fields of `PageSchema` refer to?
131
+
132
+ - name = name of the table
133
+ - actions = these are operations that can affect all entries in the table (like updating all or some of the entries in the table) or operations that envolve creating new entries (one by one)
134
+ - rowActions = these are operations that can affect just one entry/row (like updating or deleting an entry)
135
+ - getRequest = this is a field containg the function that will retrieve all the entries from one table in the database
136
+ - tableFields = these are the 'definitions' of the displayed table's columns
137
+
138
+ The operations for the rowActions and actions will generally have a form to complete before executing it. There are 4 types of inputs for the form part:
139
+
140
+ - simpleField = simple data like numbers, strings or files
141
+ - dropdownField = enable the user to choose an option from a predefined list
142
+ - arrayField = arrays of data
143
+ - objectField = an object containing more fields/inputs, or even other objects (some cool recursion)
144
+
145
+ #### Schema example
146
+
147
+ ```
148
+ const domain = "http://localhost:3001"; // dev mode
149
+
150
+ // helper function
151
+ const getAuth = () => {
152
+ // return a token or whatever :)
153
+ };
154
+
155
+ const companies: PageSchema = {
156
+ name: "Companies",
157
+
158
+ getRequest: async () => { // retrieve all entries from the database
159
+ const res = await axios.get(domain + "/something", {
160
+ headers: getAuth(),
161
+ });
162
+ return res.data;
163
+ },
164
+
165
+ tableFields: [ // the columns displayed on the page
166
+ {
167
+ name: "Company ID", // the ID must be displayed (and as first column),
168
+ // if you would like to use rowActions ⚠️
169
+ access: (obj: any) => obj.id, // obj.field_name
170
+ },
171
+ {
172
+ name: "Name",
173
+ access: (obj: any) => obj.name,
174
+ },
175
+ {
176
+ name: "Logo",
177
+ access: (obj: any) => // you can have even more complex functions
178
+ obj.logoUrl
179
+ ? React.createElement("img", {
180
+ src: obj.logoUrl,
181
+ alt: "ERROR",
182
+ style: { width: "100px" },
183
+ })
184
+ : "No Image Available",
185
+ },
186
+ ],
187
+
188
+ actions: [
189
+ {
190
+ name: "Create New Quiz",
191
+ fields: [
192
+ {
193
+ name: "name",
194
+ label: "Quiz Name",
195
+ type: "string", // for every FormField, type must be specified
196
+ validationSchema: Yup.string().required(),
197
+ },
198
+ {
199
+ name: "questions",
200
+ label: "Questions",
201
+ type: "array",
202
+ element: {
203
+ name: "",
204
+ label: "",
205
+ type: "object",
206
+ fields: [
207
+ {
208
+ name: "text",
209
+ label: "Question Text",
210
+ type: "string",
211
+ validationSchema: Yup.string().required(),
212
+ },
213
+ {
214
+ name: "type",
215
+ label: "Question Type",
216
+ type: "dropdown",
217
+ getOptions: async () => [
218
+ {
219
+ value: "OPEN",
220
+ display: "Open",
221
+ },
222
+ {
223
+ value: "SINGLE",
224
+ display: "Single",
225
+ },
226
+ {
227
+ value: "MULTIPLE",
228
+ display: "Multiple",
229
+ },
230
+ ],
231
+ },
232
+ ],
233
+ },
234
+ },
235
+ ],
236
+ onSubmit: async (formValues: any) => { // onSubmit function can also be complex
237
+ // for actions, onSubmit will always have one param (formValues - you can change the name though)
238
+ // process formVaues as you wish
239
+
240
+ // Nevertheless, submitting the actual data is the most important thing
241
+ const result = await axios.post(domain + "/something", formValues, {
242
+ headers: getAuth(),
243
+ });
244
+
245
+ return result.data;
246
+ }
247
+ },
248
+ ],
249
+
250
+ rowActions: [
251
+ {
252
+ name: "Delete Event - Variant 1", // without form
253
+ onSubmit: async (eventId: number) => { // onSubmit has only param, the row's ID
254
+ const res = await axios.delete(
255
+ domain + "/something/" + eventId
256
+ );
257
+ return res.data;
258
+ },
259
+ },
260
+ {
261
+ name: "Delete Event - Variant 2", // with form
262
+ fields: [
263
+ {
264
+ name: "input",
265
+ label: "Mock Field",
266
+ type: "string",
267
+ validationSchema: "",
268
+ },
269
+ ],
270
+ onSubmit: async (formValues: any, eventId: number) => { // onSubmit has two params
271
+ // the first param - formValues, while the second is the row's ID
272
+ const res = await axios.delete(
273
+ domain + "/something/" + eventId
274
+ );
275
+ return res.data;
276
+ },
277
+ },
278
+ ],
279
+ }
280
+ ```
281
+
282
+ Please note that the schema above is not related to any entity, and the fields of PageSchema do not necessarily have any relation between them. These are just random examples to teach you how to create your own schemas.
125
283
 
126
- #### An array of schemas would be defined as follows:
284
+ #### An array of schemas must be defined as below:
127
285
 
128
286
  ```
129
287
  const your_array = [
@@ -137,7 +295,7 @@ const your_array = [
137
295
  ];
138
296
  ```
139
297
 
140
- * In TypeScript, it would slightly change:
298
+ - In TypeScript, it would slightly change:
141
299
 
142
300
  ```
143
301
  const your_array: PageSchema[] = [
@@ -153,9 +311,9 @@ const your_array: PageSchema[] = [
153
311
 
154
312
  ## Other Specifications
155
313
 
156
- Due to some corrupt dependencies, you need a Node version >= 20.5.0 to use this package in a Vite app.
314
+ Due to some corrupt dependencies, you will need a Node version >= 20.5.0 to use this NPM package (it may work in some cases, but we still recommend updating Node to a newer version).
157
315
 
158
- For every entity you describe through a page schema, getRequest and onSubmit functions will use routes created for the entity. `getRequest` function will make a GET request, while `onSumbit` will probably make a POST, PATCH or DELETE request.
316
+ For every entity you describe through a page schema, getRequest and onSubmit functions use routes created for the entity. `getRequest` function makes a GET request, while `onSumbit` function generally makes a POST, PATCH or DELETE request.
159
317
 
160
318
  ## Learn More
161
319
 
@@ -1,6 +1,6 @@
1
- import { PageSchema } from "../schema";
1
+ import { PageSchema } from '../schema';
2
2
  export declare function pageToUrl(page: PageSchema): string;
3
- export declare function AdminPage({ pages, selectedPage, basePath, apiURL }: {
3
+ export declare function AdminPage({ pages, selectedPage, basePath, apiURL, }: {
4
4
  pages: PageSchema[];
5
5
  selectedPage: PageSchema;
6
6
  basePath: string;
@@ -1,22 +1,22 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState } from "react";
3
- import { ChakraProvider } from "@chakra-ui/react";
4
- import { Button, Modal, ModalHeader, ModalBody, useDisclosure, ModalOverlay, ModalContent, ModalCloseButton, Box, Stack, } from "@chakra-ui/react";
5
- import { GenericForm } from "./GenericForm";
6
- import { ThemeProvider } from "@emotion/react";
7
- import { createTheme } from "@mui/material/styles";
8
- import { MaterialReactTable } from "material-react-table";
9
- import { mkConfig, generateCsv, download } from "export-to-csv";
10
- import Swal from "sweetalert2";
11
- import MenuItem from "@mui/material/MenuItem";
12
- import { io } from "socket.io-client";
13
- import { newSocket } from "../utils/websockets";
2
+ import { useEffect, useState } from 'react';
3
+ import { ChakraProvider } from '@chakra-ui/react';
4
+ import { Button, Modal, ModalHeader, ModalBody, useDisclosure, ModalOverlay, ModalContent, ModalCloseButton, Box, Stack, } from '@chakra-ui/react';
5
+ import { GenericForm } from './GenericForm';
6
+ import { ThemeProvider } from '@emotion/react';
7
+ import { createTheme } from '@mui/material/styles';
8
+ import { MaterialReactTable } from 'material-react-table';
9
+ import { mkConfig, generateCsv, download } from 'export-to-csv';
10
+ import Swal from 'sweetalert2';
11
+ import MenuItem from '@mui/material/MenuItem';
12
+ import { io } from 'socket.io-client';
13
+ import { newSocket } from '../utils/websockets';
14
14
  export function pageToUrl(page) {
15
- return `${page.name.toLowerCase().replace(" ", "_")}`;
15
+ return `${page.name.toLowerCase().replace(' ', '_')}`;
16
16
  }
17
- function Header({ pages, data, basePath, }) {
17
+ function Header({ pages, data, basePath }) {
18
18
  return (_jsx(Box, { background: "blue.50", padding: 1, children: _jsxs(Stack, { margin: 3, direction: "row", justifyContent: "space-between", alignItems: "center", children: [_jsx(Stack, { margin: 4, gap: 10, direction: "row", alignItems: "center", children: pages.map((p) => (_jsx(Box, { as: "a", href: `${basePath}/${pageToUrl(p)}`, children: p.name }))) }), _jsx(Stack, { direction: "row", children: _jsx(Button, { colorScheme: "red", onClick: () => {
19
- const filename = prompt("Enter filename:");
19
+ const filename = prompt('Enter filename:');
20
20
  if (filename)
21
21
  exportToCSV(data, filename);
22
22
  }, children: "Export Data" }) })] }) }));
@@ -25,10 +25,10 @@ function exportToCSV(data, filename) {
25
25
  const formatImageData = (obj) => {
26
26
  const formattedObj = {};
27
27
  for (const key in obj) {
28
- if (typeof obj[key] !== "object") {
28
+ if (typeof obj[key] !== 'object') {
29
29
  formattedObj[key] = obj[key];
30
30
  }
31
- else if (typeof obj[key] === "object" && obj[key] != null) {
31
+ else if (typeof obj[key] === 'object' && obj[key] != null) {
32
32
  Object.keys(obj[key]).forEach((k) => {
33
33
  formattedObj[`${key}.${k}`] = obj[key][k];
34
34
  });
@@ -47,12 +47,12 @@ function exportToCSV(data, filename) {
47
47
  }
48
48
  else {
49
49
  Swal.fire({
50
- title: "Error while exporting CSV!",
51
- text: "There is no data to export.",
50
+ title: 'Error while exporting CSV!',
51
+ text: 'There is no data to export.',
52
52
  });
53
53
  }
54
54
  }
55
- export function AdminPage({ pages, selectedPage, basePath, apiURL = '' }) {
55
+ export function AdminPage({ pages, selectedPage, basePath, apiURL = '', }) {
56
56
  const { isOpen, onOpen, onClose } = useDisclosure();
57
57
  const [selectedAction, setSelectedAction] = useState({});
58
58
  const [data, setData] = useState([]);
@@ -68,10 +68,10 @@ export function AdminPage({ pages, selectedPage, basePath, apiURL = '' }) {
68
68
  });
69
69
  }, [refetch]); // Do not remove 'refetch' from here !!!
70
70
  useEffect(() => {
71
- socket.on("updateData", (message) => {
72
- console.log("Message emitted from socket: " + message);
71
+ socket.on('updateData', (message) => {
72
+ console.log('Message emitted from socket: ' + message);
73
73
  setRefetch(true);
74
- const rowActionsMenu = document.querySelector(".MuiPaper-root.MuiPopover-paper.MuiMenu-paper");
74
+ const rowActionsMenu = document.querySelector('.MuiPaper-root.MuiPopover-paper.MuiMenu-paper');
75
75
  if (rowActionsMenu) {
76
76
  rowActionsMenu.remove();
77
77
  }
@@ -83,7 +83,7 @@ export function AdminPage({ pages, selectedPage, basePath, apiURL = '' }) {
83
83
  return (_jsxs(_Fragment, { children: [_jsx(Header, { pages: pages, data: data, basePath: basePath }), _jsx(ThemeProvider, { theme: createTheme(), children: _jsx(MaterialReactTable, { columns: selectedPage.tableFields.map((field, index) => ({
84
84
  accessorFn: (row) => row[index],
85
85
  header: field.name,
86
- })), data: displayData, muiTableContainerProps: { sx: { maxHeight: "70vh" } }, enableColumnResizing: true, enableColumnPinning: true, enableGlobalFilterModes: true, enablePagination: true, paginationDisplayMode: "pages", enableRowVirtualization: true, rowVirtualizerOptions: { overscan: 20 }, renderTopToolbarCustomActions: () => (_jsx(ChakraProvider, { children: _jsx(Stack, { margin: 0, padding: 3, direction: "row", children: selectedPage.actions.map((action) => (_jsx(Button, { colorScheme: "blue", onClick: () => {
86
+ })), data: displayData, muiTableContainerProps: { sx: { maxHeight: '70vh' } }, enableColumnResizing: true, enableColumnPinning: true, enableGlobalFilterModes: true, enablePagination: true, paginationDisplayMode: "pages", enableRowVirtualization: true, rowVirtualizerOptions: { overscan: 20 }, renderTopToolbarCustomActions: () => (_jsx(ChakraProvider, { children: _jsx(Stack, { margin: 0, padding: 3, direction: "row", children: selectedPage.actions.map((action) => (_jsx(Button, { colorScheme: "blue", onClick: () => {
87
87
  setSelectedAction(action);
88
88
  onOpen();
89
89
  }, children: action.name }))) }) })), enableRowActions: true, renderRowActionMenuItems: (obj) => {
@@ -91,12 +91,12 @@ export function AdminPage({ pages, selectedPage, basePath, apiURL = '' }) {
91
91
  setSelectedAction(rowAction);
92
92
  setRowId(obj.row.original[0]);
93
93
  onOpen();
94
- const rowActionsMenu = document.querySelector(".MuiPaper-root.MuiPopover-paper.MuiMenu-paper");
94
+ const rowActionsMenu = document.querySelector('.MuiPaper-root.MuiPopover-paper.MuiMenu-paper');
95
95
  if (rowActionsMenu) {
96
96
  rowActionsMenu.remove();
97
97
  }
98
- }, children: rowAction.name + " " + obj.row.original[0] })));
99
- } }) }), _jsxs(Modal, { size: "6xl", isOpen: isOpen, onClose: onClose, children: [_jsx(ModalOverlay, {}), _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: selectedAction.name }), _jsx(ModalCloseButton, {}), _jsxs(ModalBody, { children: [selectedAction.fields && selectedAction.fields?.length > 0 ? (_jsx(GenericForm, { action: selectedAction, onClose: onClose, rowId: rowId })) : (_jsx(Button, { colorScheme: "blue", mr: 3, onClick: () => {
98
+ }, children: rowAction.name + ' ' + obj.row.original[0] })));
99
+ } }) }), _jsxs(Modal, { size: '6xl', isOpen: isOpen, onClose: onClose, children: [_jsx(ModalOverlay, {}), _jsxs(ModalContent, { children: [_jsx(ModalHeader, { children: selectedAction.name }), _jsx(ModalCloseButton, {}), _jsxs(ModalBody, { children: [selectedAction.fields && selectedAction.fields?.length > 0 ? (_jsx(GenericForm, { action: selectedAction, onClose: onClose, rowId: rowId })) : (_jsx(Button, { colorScheme: "blue", mr: 3, onClick: () => {
100
100
  selectedAction.onSubmit(rowId);
101
101
  onClose();
102
102
  }, children: "Confirm Action" })), _jsx(Button, { colorScheme: "red", mr: 3, onClick: onClose, children: "Close" })] })] })] })] }));
@@ -1,4 +1,4 @@
1
- import { Action, RowAction } from "../schema";
1
+ import { Action, RowAction } from '../schema';
2
2
  interface GenericFormProps {
3
3
  action: Action | RowAction;
4
4
  onClose?: () => void;
@@ -1,41 +1,39 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Box, Button, FormControl, FormErrorMessage, FormLabel, Input, Select, } from "@chakra-ui/react";
3
- import { Field, Form, Formik, FieldArray, getIn } from "formik";
4
- import * as Yup from "yup";
5
- import Swal from "sweetalert2";
6
- import { useEffect, useState } from "react";
2
+ import { Box, Button, FormControl, FormErrorMessage, FormLabel, Input, Select, } from '@chakra-ui/react';
3
+ import { Field, Form, Formik, FieldArray, getIn } from 'formik';
4
+ import * as Yup from 'yup';
5
+ import Swal from 'sweetalert2';
6
+ import { useEffect, useState } from 'react';
7
7
  function parseValidation(fields) {
8
8
  if (!fields) {
9
9
  return {};
10
10
  }
11
11
  return fields.reduce((acc, field) => {
12
- if (field.type !== "dropdown" &&
13
- field.type !== "object" &&
14
- field.type !== "array") {
12
+ if (field.type !== 'dropdown' && field.type !== 'object' && field.type !== 'array') {
15
13
  acc[field.name] = field.validationSchema;
16
14
  }
17
- else if (field.type === "array") {
18
- if (field.element.type === "dropdown") {
15
+ else if (field.type === 'array') {
16
+ if (field.element.type === 'dropdown') {
19
17
  acc[field.name] = Yup.array().of(Yup.string().required());
20
18
  }
21
- else if (field.element.type === "object") {
19
+ else if (field.element.type === 'object') {
22
20
  acc[field.name] = Yup.array().of(Yup.object(parseValidation(field.element.fields)));
23
21
  }
24
22
  else {
25
23
  acc[field.name] = Yup.array().of(field.element.validationSchema);
26
24
  }
27
25
  }
28
- else if (field.type === "object") {
26
+ else if (field.type === 'object') {
29
27
  acc[field.name] = Yup.object(parseValidation(field.fields));
30
28
  }
31
- if (field.type === "dropdown") {
29
+ if (field.type === 'dropdown') {
32
30
  acc[field.name] = Yup.string().required();
33
31
  }
34
32
  return acc;
35
33
  }, {});
36
34
  }
37
35
  function generateSingleField(formfield, fieldName, key) {
38
- return (_jsx(Field, { name: fieldName, children: ({ field, form }) => (_jsxs(FormControl, { isInvalid: getIn(form.touched, fieldName) && getIn(form.errors, fieldName), children: [_jsx(FormLabel, { marginTop: 2, children: formfield.label }), formfield.type === "string" || formfield.type === "number" ? (_jsx(Input, { ...field, type: formfield.type === "number" ? "number" : "text", placeholder: "" })) : formfield.type === "file" ? (_jsx(Input, { type: "file", name: fieldName, onChange: (event) => {
36
+ return (_jsx(Field, { name: fieldName, children: ({ field, form }) => (_jsxs(FormControl, { isInvalid: getIn(form.touched, fieldName) && getIn(form.errors, fieldName), children: [_jsx(FormLabel, { marginTop: 2, children: formfield.label }), formfield.type === 'string' || formfield.type === 'number' ? (_jsx(Input, { ...field, type: formfield.type === 'number' ? 'number' : 'text', placeholder: "" })) : formfield.type === 'file' ? (_jsx(Input, { type: "file", name: fieldName, onChange: (event) => {
39
37
  if (event.currentTarget.files != null) {
40
38
  form.setFieldValue(fieldName, event.currentTarget.files[0]);
41
39
  }
@@ -46,18 +44,18 @@ function parseFields(fields, previous) {
46
44
  return [];
47
45
  }
48
46
  const computedFields = fields.map((formfield, key) => {
49
- let fieldName = previous !== undefined ? previous + "." + formfield.name : formfield.name;
50
- if (formfield.type === "object") {
47
+ const fieldName = previous !== undefined ? previous + '.' + formfield.name : formfield.name;
48
+ if (formfield.type === 'object') {
51
49
  const objectFields = parseFields(formfield.fields, fieldName);
52
50
  return _jsx(FormControl, { children: objectFields }, fieldName);
53
51
  }
54
- else if (formfield.type === "array") {
52
+ else if (formfield.type === 'array') {
55
53
  return (_jsx(FieldArray, { name: fieldName, children: ({ remove, push, form }) => (_jsxs("div", { children: [_jsx(FormLabel, { marginTop: 2, children: formfield.label }), getIn(form.values, fieldName).length > 0 &&
56
- getIn(form.values, fieldName).map((_, index) => (_jsxs(Box, { paddingLeft: 10, children: [_jsx("h4", { children: _jsx("b", { children: `${fieldName}.${index}` }) }), _jsx(Box, { paddingLeft: 10, children: formfield.element.type === "object"
54
+ getIn(form.values, fieldName).map((_, index) => (_jsxs(Box, { paddingLeft: 10, children: [_jsx("h4", { children: _jsx("b", { children: `${fieldName}.${index}` }) }), _jsx(Box, { paddingLeft: 10, children: formfield.element.type === 'object'
57
55
  ? parseFields(formfield.element.fields, `${fieldName}.${index}`)
58
56
  : generateSingleField(formfield.element, `${fieldName}.${index}`, key) }), _jsxs(Button, { type: "button", colorScheme: "red", marginY: 2, onClick: () => remove(index), children: ["Remove ", `${fieldName}.${index}`] })] }))), _jsxs(Button, { type: "button", marginY: 2, colorScheme: "blue", onClick: () => {
59
57
  let value = undefined;
60
- if (formfield.element.type === "object") {
58
+ if (formfield.element.type === 'object') {
61
59
  value = getCustomInitialValues(formfield.element.fields);
62
60
  }
63
61
  else {
@@ -73,18 +71,18 @@ function parseFields(fields, previous) {
73
71
  return computedFields;
74
72
  }
75
73
  function getCustomInitialValues(fields) {
76
- let obj = {};
74
+ const obj = {};
77
75
  if (!fields) {
78
76
  return obj;
79
77
  }
80
78
  fields.forEach((field) => {
81
- if (field.type === "array")
79
+ if (field.type === 'array')
82
80
  obj[field.name] = [];
83
- else if (field.type === "object")
81
+ else if (field.type === 'object')
84
82
  obj[field.name] = getCustomInitialValues(field.fields);
85
- else if (field.type === "string")
86
- obj[field.name] = "";
87
- else if (field.type === "number")
83
+ else if (field.type === 'string')
84
+ obj[field.name] = '';
85
+ else if (field.type === 'number')
88
86
  obj[field.name] = 0;
89
87
  else
90
88
  obj[field.name] = null;
@@ -104,11 +102,11 @@ async function setOptions(fields) {
104
102
  return;
105
103
  }
106
104
  const promises = fields.map(async (field) => {
107
- if (field.type === "dropdown")
105
+ if (field.type === 'dropdown')
108
106
  field.options = await field.getOptions();
109
- else if (field.type === "array")
107
+ else if (field.type === 'array')
110
108
  await setOptions([field.element]);
111
- else if (field.type === "object")
109
+ else if (field.type === 'object')
112
110
  await setOptions(field.fields);
113
111
  });
114
112
  await Promise.all(promises);
@@ -137,14 +135,14 @@ export function GenericForm({ action, onClose, rowId }) {
137
135
  await action.onSubmit(values);
138
136
  if (onClose)
139
137
  onClose();
140
- Swal.fire("Ok");
138
+ Swal.fire('Ok');
141
139
  }
142
140
  catch (e) {
143
141
  if (onClose)
144
142
  onClose();
145
143
  const err = e.request.response;
146
144
  Swal.fire({
147
- text: "Error: " + err,
145
+ text: 'Error: ' + err,
148
146
  });
149
147
  }
150
148
  }, children: ({ isSubmitting }) => (_jsxs(Form, { children: [formFields, _jsx(Button, { mt: 4, colorScheme: "teal", isLoading: isSubmitting, type: "submit", children: "Submit" }), _jsx(Button, { mt: 4, colorScheme: "teal", type: "reset", children: "Initialise Form" })] })) }) }));
@@ -4,4 +4,4 @@ import ReactDOM from 'react-dom/client';
4
4
  import App from './App';
5
5
  import { event } from './testSchemas/event';
6
6
  import './index.css';
7
- ReactDOM.createRoot(document.getElementById('root')).render(_jsx(React.StrictMode, { children: _jsx(App, { pages: [event], basePath: '' }) }));
7
+ ReactDOM.createRoot(document.getElementById('root')).render(_jsx(React.StrictMode, { children: _jsx(App, { pages: [event], basePath: "" }) }));
@@ -6,7 +6,7 @@ export interface TableField {
6
6
  export interface SimpleField {
7
7
  name: string;
8
8
  label: string;
9
- type: "string" | "number" | "file";
9
+ type: 'string' | 'number' | 'file';
10
10
  validationSchema: any;
11
11
  }
12
12
  export interface Option {
@@ -16,20 +16,20 @@ export interface Option {
16
16
  export interface DropdownField {
17
17
  name: string;
18
18
  label: string;
19
- type: "dropdown";
19
+ type: 'dropdown';
20
20
  options?: Option[] | undefined;
21
21
  getOptions: () => Promise<Option[]>;
22
22
  }
23
23
  export interface ObjectField {
24
24
  name: string;
25
25
  label: string;
26
- type: "object";
26
+ type: 'object';
27
27
  fields: FormField[];
28
28
  }
29
29
  export interface ArrayField {
30
30
  name: string;
31
31
  label: string;
32
- type: "array";
32
+ type: 'array';
33
33
  element: Exclude<FormField, ArrayField>;
34
34
  }
35
35
  export type FormField = SimpleField | DropdownField | ObjectField | ArrayField;
@@ -1,9 +1,9 @@
1
- import io from "socket.io-client";
2
- import { useEffect } from "react";
1
+ import io from 'socket.io-client';
2
+ import { useEffect } from 'react';
3
3
  const Socket = (setSocket, apiURL) => {
4
4
  useEffect(() => {
5
5
  const socketRef = io(apiURL, {
6
- transports: ["websocket"],
6
+ transports: ['websocket'],
7
7
  });
8
8
  setSocket(socketRef);
9
9
  return () => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "admin_lsac",
3
3
  "private": false,
4
- "version": "1.4.9",
4
+ "version": "2.0.0",
5
5
  "description": "Generic Admin Panel created for storing events' data",
6
6
  "scripts": {
7
7
  "dev": "vite",
package/vite.config.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react-swc'
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react-swc';
3
3
 
4
4
  // https://vitejs.dev/config/
5
5
  export default defineConfig({
6
6
  plugins: [react()],
7
- })
7
+ });