Merge branch 'release/v0.9.0'
|
|
@ -32,3 +32,6 @@ DISABLE_SIGNUPS=
|
|||
APM_VENDOR=
|
||||
SENTRY_DNS=
|
||||
SENTRY_DEBUG=
|
||||
|
||||
# FEATURE TOGGLE
|
||||
COMMENT_FEATURE_ENABLE=
|
||||
2
.version
|
|
@ -1 +1 @@
|
|||
0.8.1
|
||||
0.9.0
|
||||
|
|
@ -12,17 +12,17 @@ We love your input! We want to make contributing to this project as easy and tra
|
|||
[Docker](https://docs.tooljet.io/docs/contributing-guide/setup/docker)
|
||||
[Ubuntu](https://docs.tooljet.io/docs/contributing-guide/setup/ubuntu)
|
||||
|
||||
## We Develop with Github
|
||||
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
## We Develop with GitHub
|
||||
We use GitHub to host code, to track issues and feature requests, as well as accept pull requests.
|
||||
|
||||
## First-time contributors
|
||||
We've tagged some issues to make it easy to get started :)
|
||||
We've tagged some issues to make it easy to get started :smile:
|
||||
[Good first issues](https://github.com/ToolJet/ToolJet/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
|
||||
Looking for ReactJS issues? Check out our [Frontend issues](https://github.com/ToolJet/ToolJet/issues?q=is%3Aissue+is%3Aopen+label%3Afrontend)
|
||||
|
||||
Add a comment on the issue and wait for the issue to be assigned before you start working on it. This helps to avoid multiple people working on similar issues.
|
||||
|
||||
## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
|
||||
## We Use [GitHub Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests
|
||||
Pull requests are the best way to propose changes to the codebase (we use [Git-Flow](https://nvie.com/posts/a-successful-git-branching-model/)). We actively welcome your pull requests:
|
||||
|
||||
1. Fork the repo and create your branch from `develop`. Please create the branch in the format feature/<issue-id>-<issue-name> (eg: feature/176-chart-widget)
|
||||
|
|
@ -35,7 +35,7 @@ Pull requests are the best way to propose changes to the codebase (we use [Git-F
|
|||
## Any contributions you make will be under the AGPL v3 License
|
||||
In short, when you submit code changes, your submissions are understood to be under the same [AGPL v3 License](https://www.gnu.org/licenses/agpl-3.0.en.html) that covers the project.
|
||||
|
||||
## Report bugs using Github's [issues](https://github.com/ToolJet/ToolJet/issues)
|
||||
## Report bugs using GitHub's [issues](https://github.com/ToolJet/ToolJet/issues)
|
||||
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/ToolJet/ToolJet/issues/new/choose). It's that easy!
|
||||
|
||||
**Great Bug Reports** tend to have:
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/7828962/120930301-4ec3fe80-c70a-11eb-91b7-4bdbc31fd928.png" width="200" />
|
||||
<br/>
|
||||
Build and deploy internal tools.
|
||||
Build and deploy internal tools
|
||||
</p>
|
||||
|
||||
ToolJet is an **open-source no-code framework** to build and deploy internal tools quickly without much effort from the engineering teams. You can connect to your data sources such as databases ( like PostgreSQL, MongoDB, Elasticsearch, etc ), API endpoints ( ToolJet supports importing OpenAPI spec & OAuth2 authorization), and external services ( like Stripe, Slack, Google Sheets, Airtable ) and use our pre-built UI widgets to build internal tools.
|
||||
ToolJet is an **open-source no-code framework** to build and deploy internal tools quickly without much effort from the engineering teams. You can connect to your data sources, such as databases (like PostgreSQL, MongoDB, Elasticsearch, etc), API endpoints (ToolJet supports importing OpenAPI spec & OAuth2 authorization), and external services (like Stripe, Slack, Google Sheets, Airtable) and use our pre-built UI widgets to build internal tools.
|
||||
|
||||

|
||||

|
||||
|
|
@ -27,7 +27,7 @@ ToolJet is an **open-source no-code framework** to build and deploy internal too
|
|||
|
||||
## Features
|
||||
|
||||
- Visual app builder with widgets such as tables, charts, modals, buttons, dropdowns, and more.
|
||||
- Visual app builder with widgets, such as tables, charts, modals, buttons, dropdowns, and more.
|
||||
- Mobile 📱 & desktop layouts 🖥
|
||||
- Dark mode 🌛
|
||||
- Connect to databases, APIs, and external services.
|
||||
|
|
@ -52,7 +52,7 @@ You can deploy ToolJet on Heroku for free using the one-click-deployment button
|
|||
|
||||
## Examples
|
||||
|
||||
[Building a Github contributor leaderboard using ToolJet](https://blog.tooljet.io/building-a-github-contributor-leaderboard-using-tooljet/)<br>
|
||||
[Building a GitHub contributor leaderboard using ToolJet](https://blog.tooljet.io/building-a-github-contributor-leaderboard-using-tooljet/)<br>
|
||||
|
||||
## Documentation
|
||||
Documentation is available at https://docs.tooljet.io.
|
||||
|
|
|
|||
5
docs/docs/actions/_category_.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"label": "Actions Reference",
|
||||
"position": 6,
|
||||
"collapsed": true
|
||||
}
|
||||
28
docs/docs/actions/set-localstorage.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
sidebar_position: 1
|
||||
sidebar_label: Set localStorage
|
||||
---
|
||||
|
||||
# Set localStorage
|
||||
|
||||
This action allows you to specify a `key` and its corresponding `value` to be stored in localStorage.
|
||||
|
||||
## Example: App that stores a name in localStorage and displays it on reload
|
||||
|
||||
1. Add an input field, button and a text as shown
|
||||
<img src="/img/actions/localstorage/sample-app-1.png" alt="Set local storage sample app" height="350" />
|
||||
|
||||
2. Select the button and add a `Set localStorage` action with `key` set to `name` and value pointing at the value of the text field
|
||||
<img src="/img/actions/localstorage/sample-app-2.png" alt="Set local storage sample app" height="350" />
|
||||
|
||||
3. Select the text label we've added and set its value to the name item from localStorage
|
||||
<img src="/img/actions/localstorage/sample-app-3.png" alt="Set local storage sample app" height="150" />
|
||||
|
||||
4. Now save the application, this is important as we're about to reload the page.
|
||||
|
||||
5. Type in anything you wish on the input box and click on the button
|
||||
<img src="/img/actions/localstorage/sample-app-4.png" alt="Set local storage sample app" height="150" />
|
||||
|
||||
6. Reload the page, you'll see that the value stored in local storage is persisted and it is displayed on screen!
|
||||
<img src="/img/actions/localstorage/sample-app-5.png" alt="Set local storage sample app" height="350" />
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ sidebar_position: 1
|
|||
---
|
||||
|
||||
# Mac OS
|
||||
Follow these steps to setup and run ToolJet on Mac OS for development purposes. Open terminal and run the commands below. We recommend reading our guide on [architecture](/docs/deployment/architecture) of ToolJet before proceeding.
|
||||
Follow these steps to setup and run ToolJet on macOS for development purposes. Open terminal and run the commands below. We recommend reading our guide on [architecture](/docs/deployment/architecture) of ToolJet before proceeding.
|
||||
|
||||
## Setting up
|
||||
|
||||
|
|
|
|||
|
|
@ -176,4 +176,4 @@ docker-compose run --rm server npm run test <path-to-file>
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
|
||||
Please open a new issue at https://github.com/ToolJet/ToolJet/issues or join our Slack channel (https://join.slack.com/t/tooljet/shared_invite/zt-r2neyfcw-KD1COL6t2kgVTlTtAV5rtg) if you encounter any issues when trying to run ToolJet locally.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Airtable API has a rate limit, and at the time of writing this documentation, th
|
|||
:::
|
||||
|
||||
:::tip
|
||||
This guide assumes that you have already gone throgh [Adding a datasource
|
||||
This guide assumes that you have already gone through [Adding a datasource
|
||||
](/docs/tutorial/adding-a-datasource) tutorial.
|
||||
:::
|
||||
|
||||
|
|
|
|||
35
docs/docs/data-sources/dynamodb.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
# DynamoDB
|
||||
|
||||
ToolJet can connect to DynamoDB to read and write data.
|
||||
|
||||
## Connection
|
||||
|
||||
To add a new DynamoDB, click on the `+` button on data sources panel at the left-bottom corner of the app editor. Select DynamoDB from the modal that pops up.
|
||||
|
||||
ToolJet requires the following to connect to your DynamoDB.
|
||||
|
||||
- **Region**
|
||||
- **Access key**
|
||||
- **Secret key**
|
||||
|
||||
It is recommended to create a new IAM user for the database so that you can control the access levels of ToolJet.
|
||||
|
||||
<img src="/img/datasource-reference/dynamo-connect.png" alt="ToolJet - Dynamo connection" height="250"/>
|
||||
|
||||
Click on 'Test connection' button to verify if the credentials are correct and that the database is accessible to ToolJet server. Click on 'Save' button to save the datasource.
|
||||
|
||||
## Querying DynamoDB
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource. Select the operation that you want to perform and click 'Save' to save the query.
|
||||
|
||||
<img src="/img/datasource-reference/dynamo-query.png" alt="ToolJet - Dynamo query" height="250"/>
|
||||
|
||||
Click on the 'run' button to run the query. NOTE: Query should be saved before running.
|
||||
|
||||
:::tip
|
||||
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/tutorial/transformations)
|
||||
:::
|
||||
|
|
@ -17,9 +17,14 @@ To generate a new key, check out Firestore's official documentation: [https://cl
|
|||
|
||||
Once the key is downloaded, click on `+` button of data sources panel at the left-bottom corner of the app editor. Select Firestore from the modal that pops up. Paste the key in the field for GCP key. Click on 'Test connection' button to verify if the service account can access Firestore from ToolJet server. Click on 'Save' button to save the datasource.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/firestore/firestore-intro.gif" alt="ToolJet - Datasource Firestore" height="420" />
|
||||
|
||||
## Querying Firestore
|
||||
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource.
|
||||
Click on `+` button of the query manager at the bottom panel of the editor and select the database added in the previous step as the datasource.
|
||||
|
||||
<img class="screenshot-full" src="/img/datasource-reference/firestore/firestore-query.png" alt="ToolJet - Firestore connection" height="420"/>
|
||||
|
||||
Select the operation that you want to perform on Firestore and click 'Save' to save the query.
|
||||
|
||||
:::tip
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ ToolJet server uses lockbox to encrypt datasource credentials. You should set th
|
|||
ToolJet server uses a secure 64 byte hexadecimal string to encrypt session cookies. You should set the environment variable `SECRET_KEY_BASE`.
|
||||
|
||||
:::tip
|
||||
If you have `openssl` installed, you can run the following commands to generate the the value for `LOCKBOX_MASTER_KEY` and `SECRET_KEY_BASE`.
|
||||
If you have `openssl` installed, you can run the following commands to generate the value for `LOCKBOX_MASTER_KEY` and `SECRET_KEY_BASE`.
|
||||
|
||||
For `LOCKBOX_MASTER_KEY` use `openssl rand -hex 32`
|
||||
For `SECRET_KEY_BASE` use `openssl rand -hex 64`
|
||||
|
|
@ -42,7 +42,7 @@ For `SECRET_KEY_BASE` use `openssl rand -hex 64`
|
|||
|
||||
#### Disabling signups ( optional )
|
||||
|
||||
If want to restrict the signups and allow new users only by invitations, set the environment variable `DISABLE_SIGNUPS` to `true`.
|
||||
If you want to restrict the signups and allow new users only by invitations, set the environment variable `DISABLE_SIGNUPS` to `true`.
|
||||
|
||||
:::tip
|
||||
You will still be able to see the signup page but won't be able to successfully submit the form.
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ Lastly, supply the environment variable `SSO_GOOGLE_OAUTH2_CLIENT_ID` to your de
|
|||
|
||||
### Restrict to your domain
|
||||
Set the environment variable `RESTRICTED_DOMAIN` to ensure that ToolJet verifies the domain of the user who signs in via SSO, on the server side.
|
||||
If you're setting this environment variable, please make sure that the value does not contain any protocols, sub domains or slashes. It should
|
||||
If you're setting this environment variable, please make sure that the value does not contain any protocols, subdomains or slashes. It should
|
||||
simply be `yourdomain.com`.
|
||||
:::
|
||||
|
||||
|
|
|
|||
29
docs/docs/tutorial/actions.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Adding actions
|
||||
|
||||
ToolJet supports several actions that can be invoked as the handler for any `event` that is triggered in an application.
|
||||
|
||||
## To add actions
|
||||
|
||||
To attach an action for component events, click on the component's handle, and then click on the `Add handler` button on the
|
||||
inspector panel available on the right side.
|
||||
|
||||
To attach an action for query events, select the query, go to the `advanced` tab and then click on the `Add handler` button.
|
||||
|
||||
## Available actions
|
||||
|
||||
Some of the actions that ToolJet Support are
|
||||
|
||||
Action| Description|
|
||||
----| ----------- |
|
||||
Show alert | Show an alert message as a bootstrap toast |
|
||||
Run query | Run any of the data queries that you have created |
|
||||
Open webpage | Go to another webpage in a new tab |
|
||||
Goto app | Go to another ToolJet application |
|
||||
Show modal | Open any modal that you've added |
|
||||
Close modal | Close any modal that you've added if its already open |
|
||||
Copy to clipboard | Copy any available text that you see on the application to clipboard |
|
||||
Set localStorage | Set a key and corresponding value to localStorage |
|
||||
|
|
@ -4,7 +4,7 @@ sidebar_position: 11
|
|||
|
||||
# Debugger
|
||||
|
||||
The debugger captures errors errors that happens while running the queries. For example, when a database query fails due to the unavailability of a database or when a REST API query fails due to an incorrect URL, the errors will be displayed on the debugger. The debugger also displays relevant data related to the error along with the error message. Debugger is located on the left-sidebar.
|
||||
The debugger captures errors that happens while running the queries. For example, when a database query fails due to the unavailability of a database or when a REST API query fails due to an incorrect URL, the errors will be displayed on the debugger. The debugger also displays relevant data related to the error along with the error message. Debugger is located on the left-sidebar.
|
||||
<img class="screenshot-full" src="/img/tutorial/debugger/debugger.gif" alt="ToolJet - Debugger" height="420"/>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ ToolJet does not store any data fetched from the datasources. ToolJet acts as a
|
|||
## Server
|
||||
|
||||
:::tip
|
||||
Self-hosted version of ToolJet pings our server to fetch latest product updates every 24 hours. You can disable this by setting the value of `CHECK_FOR_UPDATES` environment variable to `0`. This feature is enabled by default.
|
||||
Self-hosted version of ToolJet pings our server to fetch the latest product updates every 24 hours. You can disable this by setting the value of `CHECK_FOR_UPDATES` environment variable to `0`. This feature is enabled by default.
|
||||
:::
|
||||
|
||||
## Client
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 7
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# Image
|
||||
|
|
|
|||
109
docs/docs/widgets/calendar.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
---
|
||||
sidebar_position: 22
|
||||
---
|
||||
|
||||
# Calendar
|
||||
Calendar widget comes with the following features:
|
||||
- Day, month and week level views
|
||||
- Events
|
||||
- Resource scheduling
|
||||
|
||||
<img class="screenshot-full" src="/img/widgets/calendar/calendar1.png" alt="ToolJet - Widget Reference - Calendar" height="600"/>
|
||||
|
||||
### Properties
|
||||
|
||||
#### Date format
|
||||
Determines the format in which any date passed to the calendar via any of the properties will be parsed.
|
||||
It also determines the format in which any date made available by the calendar via exposed variables will be displayed.
|
||||
It uses the date format conventions of [moment.js](https://momentjs.com/).
|
||||
#### Default date
|
||||
Determines the date on which the calendar's view will be centered on.
|
||||
If the calendar is on `month` view, it will show the month on which this date exists.
|
||||
If the calendar is on `week` view, it will show the week on which this date exists.
|
||||
This property needs to be formatted using the `Date format` property which is configurable on the inspector.
|
||||
#### Events
|
||||
`Events` property should contain an array of objects, each of which describes the events that the calendar needs to display.
|
||||
|
||||
Assuming that you set the date format to `MM-DD-YYYY HH:mm:ss A Z`, setting the `Events` property to the following code snippet will display an event titled `Sample Event` at the first hour of this day, as displayed in the image of calendar at the beginning of this page.
|
||||
|
||||
```javascript
|
||||
{{[
|
||||
{
|
||||
title: 'Sample event',
|
||||
start: `${moment().startOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,
|
||||
end: `${moment().endOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,
|
||||
allDay: false,
|
||||
tooltip: 'Sample event',
|
||||
color: 'lightgreen',
|
||||
}
|
||||
]}}
|
||||
```
|
||||
|
||||
##### Event object properties
|
||||
|
||||
| Name | Description |
|
||||
|------|-------------|
|
||||
| title | Title of the event |
|
||||
| start | The date(and time) on which this event begins. Needs to be formatted in the `Date format` you've supplied |
|
||||
| end | The date(and time) on which this event ends. Needs to be formatted in the `Date format` you've supplied |
|
||||
| allDay | Optional. Qualifies the event as an 'All day event', which will pin it to date headers on `day` and `week` level views |
|
||||
| tooltip | Tooltip which will be display when the user hovers over the event |
|
||||
| color | Background color of the event, any css supported color name or hex code can be used |
|
||||
| textOrientation | Optional. If it is set to `vertical`, the title of the event will be oriented vertically. |
|
||||
| resourceId | Applicable only if you're using resource scheduling. This is the id of the resource to which this event correspond to. |
|
||||
|
||||
You may supply any other additional property to the event(s). These additional properties will available to you when the calendar widget
|
||||
exposes any of the events via its exposed variables.
|
||||
|
||||
#### Resources
|
||||
|
||||
Specifying resources will make the calendar categorize `week` view and `day` view for each of the resources specified.
|
||||
|
||||
For example, to categorize week/day view into for three rooms, we specify `resources` this way:
|
||||
|
||||
```javascript
|
||||
{{
|
||||
[
|
||||
{resourceId: 1, title: 'Room A'},
|
||||
{resourceId: 2, title: 'Room B'},
|
||||
{resourceId: 3, title: 'Room C'},
|
||||
]
|
||||
}}
|
||||
```
|
||||
|
||||
If we specify the `resourceId` of any of the events as `1`, then that event will be assigned to `Room A`, generating the following calendar, assuming that we've set the view to `day` and are viewing the day on which this event exists.
|
||||
|
||||
<img class="screenshot-full" src="/img/widgets/calendar/calendar-resource.png" alt="ToolJet - Widget Reference - Calendar Resources" height="600"/>
|
||||
|
||||
#### Default view
|
||||
|
||||
Determines whether the calendar would display a `day`, a `week` or a `month`.
|
||||
Setting this property to anything other than these values will make the calendar default to `month` view.
|
||||
|
||||
#### Show toolbar
|
||||
|
||||
Determines whether the calendar toolbar should be displayed or not.
|
||||
|
||||
#### Show view switcher
|
||||
|
||||
Determinues whether the calendar's buttons that allow user to switch between `month`, `week` and `day` level views will be displayed.
|
||||
### Styles
|
||||
#### Cell size in views classified by resource
|
||||
|
||||
When `resources` are specified, the calendar could take up quite a lot of horizontal space, making the horizontal scroll bar of calendar having to be relied upon all the time.
|
||||
|
||||
If we set this property to `compact`, the cell sizes will be smaller in `week` and `day` views.
|
||||
|
||||
### Events
|
||||
|
||||
#### On Event selected
|
||||
|
||||
This event is fired when the user clicks on a calendar event.
|
||||
|
||||
Last selected event is exposed as `selectedEvent`.
|
||||
|
||||
#### on Slot selected
|
||||
|
||||
This event is fired when the user either clicks on an calendar slot(empty cell or empty space of a cell with event) or when they click and drag to select multiple slots.
|
||||
|
||||
Last selected slot(s) are exposed as `selectedSlots`.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 20
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Divider
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 6
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# Dropdown
|
||||
|
|
|
|||
44
docs/docs/widgets/filepicker.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Filepicker
|
||||
|
||||
Filepicker widget allows the user to drag and drop files or upload files by browsing the filesystem and selecting one or more files in a directory.
|
||||
|
||||
<img class="screenshot-full" src="/img/widgets/filepicker/filepicker.gif" alt="ToolJet - Widget Reference - Filepicker " height="420"/>
|
||||
|
||||
:::info
|
||||
File types must be a valid [MIME](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) type according to input element specification or a valid file extension.
|
||||
|
||||
To accept any/all file type(s), set `Accept file types` to an empty value.
|
||||
:::
|
||||
|
||||
<img class="screenshot-full" src="/img/widgets/filepicker/file-types.gif" alt="ToolJet - Widget Reference - Filepicker file types " height="420"/>
|
||||
|
||||
:::tip
|
||||
[MIME](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) type determination is not reliable across platforms. CSV files, for example, are reported as text/plain under macOS but as application/vnd.ms-excel under Windows.
|
||||
:::
|
||||
|
||||
## Event: On file selected
|
||||
|
||||
On file selected event can be triggered when one or more files are selected.
|
||||
|
||||
|
||||
#### Properties
|
||||
|
||||
| properties | description |
|
||||
| ----------- | ----------- |
|
||||
| Use Drop zone | creates a drag & drop zone. Files can be dragged and dropped to the "drag & drop" zone. |
|
||||
| Use File Picker | On clicking it invokes the default OS file prompt.|
|
||||
| Pick mulitple files | Allows drag and drop (or selection from the file dialog) of multiple files. `Pick multiple files` is disabled by default. |
|
||||
| Max file count | The maximum accepted number of files The default value is `2`.|
|
||||
| Accept file types| By providing types, you can make the dropzone accept specific file types and reject the others. |
|
||||
| Max size limit| Maximum file size (in bytes).|
|
||||
| Min size limit| Minimum file size (in bytes).|
|
||||
|
||||
:::tip::
|
||||
Files can be accepted or rejected based on the file types, maximum file count, maximum file size (in bytes) and minimum file size (in bytes).
|
||||
If `Pick mulitple files` is set to false and additional files are dropped, all files besides the first will be rejected.
|
||||
Any file that does not have a size in the range of `Max size limit` and `Min size limit` will be rejected.
|
||||
:::
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
sidebar_position: 8
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
# Map
|
||||
|
||||
The map widget can be used to pick or select locations on the google map with the location's coordinates.
|
||||
The map widget can be used to pick or select locations on the Google map with the location's coordinates.
|
||||
|
||||
<img class="screenshot-full" src="/img/widgets/map/map.gif" alt="ToolJet - Widget Reference - Map" height="420"/>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 9
|
||||
sidebar_position: 10
|
||||
---
|
||||
|
||||
# Modal
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 10
|
||||
sidebar_position: 11
|
||||
---
|
||||
|
||||
# Multiselect
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 16
|
||||
sidebar_position: 12
|
||||
---
|
||||
|
||||
# Number Input
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 11
|
||||
sidebar_position: 13
|
||||
---
|
||||
|
||||
# QR Scanner
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 12
|
||||
sidebar_position: 14
|
||||
---
|
||||
|
||||
# Radio Button
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 13
|
||||
sidebar_position: 15
|
||||
---
|
||||
|
||||
# Rich Text Editor
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 14
|
||||
sidebar_position: 16
|
||||
---
|
||||
|
||||
# Star rating
|
||||
|
|
@ -10,7 +10,7 @@ Star rating widget can be used to display as well as input ratings. The widget s
|
|||
|
||||
### Event: On Change
|
||||
|
||||
This event is triggered when an star is clicked.
|
||||
This event is triggered when a star is clicked.
|
||||
|
||||
|
||||
#### Properties
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 15
|
||||
sidebar_position: 17
|
||||
---
|
||||
|
||||
# Table
|
||||
|
|
@ -112,7 +112,7 @@ If the data of a cell is changed, "save changes" button will be shown at the bot
|
|||
| changeSet | Object with row number as the key and object of edited fields and their values as the value |
|
||||
| dataUpdates | Just like changeSet but includes the data of the entire row |
|
||||
| selectedRow | The data of the row that was last clicked. `selectedRow` also changes when an action button is clicked |
|
||||
| searchText | The value of the search field if server-side paginaton is enabled |
|
||||
| searchText | The value of the search field if server-side pagination is enabled |
|
||||
|
||||
#### Events
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 16
|
||||
sidebar_position: 18
|
||||
---
|
||||
|
||||
# Text Input
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 17
|
||||
sidebar_position: 19
|
||||
---
|
||||
|
||||
# Text
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 18
|
||||
sidebar_position: 20
|
||||
---
|
||||
|
||||
# Textarea
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
sidebar_position: 19
|
||||
sidebar_position: 21
|
||||
---
|
||||
|
||||
# Toggle Switch
|
||||
|
|
|
|||
BIN
docs/static/img/actions/localstorage/sample-app-1.png
vendored
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docs/static/img/actions/localstorage/sample-app-2.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
docs/static/img/actions/localstorage/sample-app-3.png
vendored
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
docs/static/img/actions/localstorage/sample-app-4.png
vendored
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
docs/static/img/actions/localstorage/sample-app-5.png
vendored
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
docs/static/img/datasource-reference/build-query-confirmation.png
vendored
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/static/img/datasource-reference/dynamo-connect.png
vendored
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
docs/static/img/datasource-reference/dynamo-query.png
vendored
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
docs/static/img/datasource-reference/firestore/firestore-intro.gif
vendored
Normal file
|
After Width: | Height: | Size: 8.3 MiB |
BIN
docs/static/img/datasource-reference/firestore/firestore-query.png
vendored
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/static/img/redis/connect.png
vendored
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 103 KiB |
BIN
docs/static/img/widgets/calendar/calendar-day.png
vendored
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
docs/static/img/widgets/calendar/calendar-resource.png
vendored
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/static/img/widgets/calendar/calendar-week.png
vendored
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/static/img/widgets/calendar/calendar1.png
vendored
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/static/img/widgets/filepicker/file-types.gif
vendored
Normal file
|
After Width: | Height: | Size: 9.5 MiB |
BIN
docs/static/img/widgets/filepicker/filepicker.gif
vendored
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
|
|
@ -1,7 +1,12 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
["@babel/plugin-proposal-class-properties"],
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
],
|
||||
[
|
||||
"console-source",
|
||||
{
|
||||
|
|
@ -9,11 +14,13 @@
|
|||
// 0 = full file path (Default)
|
||||
// 1 = file name ONLY
|
||||
// 2 = file name and last segment
|
||||
|
||||
"splitSegment": "/" // How to split the path - NOT REQUIRED
|
||||
// Default is / for Linux and OSX
|
||||
// Windows users can use "\\" here if needed
|
||||
}
|
||||
],
|
||||
[
|
||||
"@babel/transform-runtime"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
frontend/assets/images/icons/editor/comments/send.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
1
frontend/assets/images/icons/editor/datasources/gcs.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="800" width="1200" viewBox="-19.20015 -28.483 166.4013 170.898"><g transform="translate(0 -7.034)"><linearGradient y2="120.789" x2="64" y1="7.034" x1="64" gradientUnits="userSpaceOnUse" id="a"><stop offset="0" stop-color="#4387fd"/><stop offset="1" stop-color="#4683ea"/></linearGradient><path d="M27.79 115.217L1.54 69.749a11.499 11.499 0 010-11.499l26.25-45.467a11.5 11.5 0 019.96-5.75h52.5a11.5 11.5 0 019.959 5.75l26.25 45.467a11.499 11.499 0 010 11.5l-26.25 45.466a11.5 11.5 0 01-9.959 5.75h-52.5a11.499 11.499 0 01-9.96-5.75z" fill="url(#a)"/></g><g transform="translate(0 -7.034)"><defs><path d="M27.791 115.217L1.541 69.749a11.499 11.499 0 010-11.499l26.25-45.467a11.499 11.499 0 019.959-5.75h52.5a11.5 11.5 0 019.96 5.75l26.25 45.467a11.499 11.499 0 010 11.5l-26.25 45.466a11.499 11.499 0 01-9.96 5.75h-52.5a11.499 11.499 0 01-9.959-5.75z" id="b"/></defs><clipPath id="c"><use height="100%" width="100%" xlink:href="#b" overflow="visible"/></clipPath><path clip-path="url(#c)" opacity=".07" d="M49.313 53.875l-7.01 6.99 5.957 5.958-5.898 10.476 44.635 44.636 10.816.002L118.936 84 85.489 50.55z"/></g><path d="M84.7 43.236H43.264c-.667 0-1.212.546-1.212 1.214v8.566c0 .666.546 1.212 1.212 1.212H84.7c.667 0 1.213-.546 1.213-1.212v-8.568c0-.666-.545-1.213-1.212-1.213m-6.416 7.976a2.484 2.484 0 01-2.477-2.48 2.475 2.475 0 012.477-2.477c1.37 0 2.48 1.103 2.48 2.477a2.48 2.48 0 01-2.48 2.48m6.415 8.491l-41.436.002c-.667 0-1.212.546-1.212 1.214v8.565c0 .666.546 1.213 1.212 1.213H84.7c.667 0 1.213-.547 1.213-1.213v-8.567c0-.666-.545-1.214-1.212-1.214m-6.416 7.976a2.483 2.483 0 01-2.477-2.48 2.475 2.475 0 012.477-2.477 2.48 2.48 0 110 4.956" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
1
frontend/assets/images/icons/editor/datasources/s3.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg width="310px" height="310px" viewBox="-27 0 310 310" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M20.624 53.686L0 64v181.02l20.624 10.254.124-.149V53.828l-.124-.142" fill="#8C3123"/><path d="M131 229L20.624 255.274V53.686L131 79.387V229" fill="#E05243"/><path d="M81.178 187.866l46.818 5.96.294-.678.263-76.77-.557-.6-46.818 5.874v66.214" fill="#8C3123"/><path d="M127.996 229.295l107.371 26.035.169-.269-.003-201.195-.17-.18-107.367 25.996v149.613" fill="#8C3123"/><path d="M174.827 187.866l-46.831 5.96v-78.048l46.831 5.874v66.214" fill="#E05243"/><path d="M174.827 89.631l-46.831 8.535-46.818-8.535 46.759-12.256 46.89 12.256" fill="#5E1F18"/><path d="M174.827 219.801l-46.831-8.591-46.818 8.591 46.761 13.053 46.888-13.053" fill="#F2B0A9"/><path d="M81.178 89.631l46.818-11.586.379-.117V.313L127.996 0 81.178 23.413v66.218" fill="#8C3123"/><path d="M174.827 89.631l-46.831-11.586V0l46.831 23.413v66.218" fill="#E05243"/><path d="M127.996 309.428l-46.823-23.405v-66.217l46.823 11.582.689.783-.187 75.906-.502 1.351" fill="#8C3123"/><path d="M127.996 309.428l46.827-23.405v-66.217l-46.827 11.582v78.04M235.367 53.686L256 64v181.02l-20.633 10.31V53.686" fill="#E05243"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.125 1H3.875C3.17942 1.00198 2.51289 1.27918 2.02103 1.77103C1.52918 2.26289 1.25198 2.92942 1.25 3.625V12.625C1.25198 13.3206 1.52918 13.9871 2.02103 14.479C2.51289 14.9708 3.17942 15.248 3.875 15.25H5.75V19L10.1431 15.3372C10.2106 15.2808 10.2957 15.25 10.3836 15.25H18.125C18.8206 15.248 19.4871 14.9708 19.979 14.479C20.4708 13.9871 20.748 13.3206 20.75 12.625V3.625C20.748 2.92942 20.4708 2.26289 19.979 1.77103C19.4871 1.27918 18.8206 1.00198 18.125 1V1Z" stroke="black" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 618 B |
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32px" height="42px" viewBox="0 0 32 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>play</title>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="play" fill="#3C92DC" fill-rule="nonzero">
|
||||
<path d="M31.068,20.176 L2.068,0.176 C1.761,-0.035 1.363,-0.057 1.035,0.114 C0.706,0.287 0.5,0.627 0.5,0.999 L0.5,40.999 C0.5,41.371 0.706,41.712 1.035,41.885 C1.181,41.961 1.341,41.999 1.5,41.999 C1.699,41.999 1.897,41.939 2.068,41.822 L31.068,21.822 C31.339,21.635 31.5,21.328 31.5,20.999 C31.5,20.67 31.338,20.363 31.068,20.176 Z" id="Path"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 740 B |
69
frontend/assets/images/icons/widgets/calendar.svg
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 470 470" style="enable-background:new 0 0 470 470;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M462.5,425H7.5c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h455c4.143,0,7.5-3.358,7.5-7.5S466.643,425,462.5,425z"/>
|
||||
<path d="M462.5,455H7.5c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h455c4.143,0,7.5-3.358,7.5-7.5S466.643,455,462.5,455z"/>
|
||||
<path d="M462.5,30h-25v-7.5C437.5,10.093,427.406,0,415,0s-22.5,10.093-22.5,22.5V30h-75v-7.5C317.5,10.093,307.406,0,295,0
|
||||
s-22.5,10.093-22.5,22.5V30h-75v-7.5C197.5,10.093,187.407,0,175,0s-22.5,10.093-22.5,22.5V30h-75v-7.5C77.5,10.093,67.407,0,55,0
|
||||
S32.5,10.093,32.5,22.5V30h-25C3.358,30,0,33.358,0,37.5v365c0,4.142,3.358,7.5,7.5,7.5h455c4.143,0,7.5-3.358,7.5-7.5v-365
|
||||
C470,33.358,466.643,30,462.5,30z M407.5,22.5c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v30c0,4.136-3.364,7.5-7.5,7.5
|
||||
s-7.5-3.364-7.5-7.5V22.5z M287.5,22.5c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v30c0,4.136-3.364,7.5-7.5,7.5
|
||||
s-7.5-3.364-7.5-7.5V22.5z M167.5,22.5c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v30c0,4.136-3.364,7.5-7.5,7.5
|
||||
s-7.5-3.364-7.5-7.5V22.5z M47.5,22.5c0-4.136,3.364-7.5,7.5-7.5s7.5,3.364,7.5,7.5v30c0,4.136-3.364,7.5-7.5,7.5
|
||||
s-7.5-3.364-7.5-7.5V22.5z M32.5,45v7.5C32.5,64.907,42.593,75,55,75s22.5-10.093,22.5-22.5V45h75v7.5
|
||||
c0,12.407,10.093,22.5,22.5,22.5s22.5-10.093,22.5-22.5V45h75v7.5c0,12.407,10.094,22.5,22.5,22.5s22.5-10.093,22.5-22.5V45h75v7.5
|
||||
c0,12.407,10.094,22.5,22.5,22.5s22.5-10.093,22.5-22.5V45H455v77.3H15V45H32.5z M15,395V137.3h440V395H15z"/>
|
||||
<path d="M412,226.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S416.143,226.8,412,226.8z"/>
|
||||
<path d="M331,226.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S335.143,226.8,331,226.8z"/>
|
||||
<path d="M250,226.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S254.143,226.8,250,226.8z"/>
|
||||
<path d="M169,226.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S173.142,226.8,169,226.8z"/>
|
||||
<path d="M88,226.8H58c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S92.142,226.8,88,226.8z"/>
|
||||
<path d="M331,280.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S335.143,280.8,331,280.8z"/>
|
||||
<path d="M250,280.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S254.143,280.8,250,280.8z"/>
|
||||
<path d="M169,280.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S173.142,280.8,169,280.8z"/>
|
||||
<path d="M88,280.8H58c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S92.142,280.8,88,280.8z"/>
|
||||
<path d="M331,334.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S335.143,334.8,331,334.8z"/>
|
||||
<path d="M412,280.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S416.143,280.8,412,280.8z"/>
|
||||
<path d="M412,334.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S416.143,334.8,412,334.8z"/>
|
||||
<path d="M250,334.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S254.143,334.8,250,334.8z"/>
|
||||
<path d="M169,334.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S173.142,334.8,169,334.8z"/>
|
||||
<path d="M88,334.8H58c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S92.142,334.8,88,334.8z"/>
|
||||
<path d="M412,172.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S416.143,172.8,412,172.8z"/>
|
||||
<path d="M331,172.8h-30c-4.143,0-7.5,3.358-7.5,7.5s3.357,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S335.143,172.8,331,172.8z"/>
|
||||
<path d="M250,172.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.143,0,7.5-3.358,7.5-7.5S254.143,172.8,250,172.8z"/>
|
||||
<path d="M169,172.8h-30c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S173.142,172.8,169,172.8z"/>
|
||||
<path d="M88,172.8H58c-4.142,0-7.5,3.358-7.5,7.5s3.358,7.5,7.5,7.5h30c4.142,0,7.5-3.358,7.5-7.5S92.142,172.8,88,172.8z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
7
frontend/assets/images/icons/widgets/filepicker.svg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-upload" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M14 3v4a1 1 0 0 0 1 1h4"></path>
|
||||
<path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"></path>
|
||||
<line x1="12" y1="11" x2="12" y2="17"></line>
|
||||
<polyline points="9 14 12 11 15 14"></polyline>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
413
frontend/package-lock.json
generated
|
|
@ -5,6 +5,7 @@
|
|||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "frontend",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.4.3",
|
||||
|
|
@ -14,6 +15,7 @@
|
|||
"@react-google-maps/api": "^2.1.1",
|
||||
"@sentry/react": "^6.12.0",
|
||||
"@sentry/tracing": "^6.12.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
|
|
@ -27,6 +29,7 @@
|
|||
"dompurify": "^2.2.7",
|
||||
"draft-js": "^0.11.7",
|
||||
"draft-js-export-html": "^1.4.1",
|
||||
"emoji-mart": "^3.0.1",
|
||||
"fuse.js": "^6.4.6",
|
||||
"history": "^4.9.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
|
|
@ -38,6 +41,7 @@
|
|||
"plotly.js-basic-dist-min": "^1.58.4",
|
||||
"query-string": "^6.13.6",
|
||||
"react": "^16.14.0",
|
||||
"react-big-calendar": "^0.38.0",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-color": "^2.19.3",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
|
|
@ -46,10 +50,13 @@
|
|||
"react-dnd": "^14.0.2",
|
||||
"react-dnd-html5-backend": "^14.0.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-easy-sort": "^0.2.1",
|
||||
"react-hot-toast": "^2.1.1",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-lazyload": "^3.2.0",
|
||||
"react-loading-skeleton": "^2.2.0",
|
||||
"react-mentions": "^4.3.0",
|
||||
"react-plotly.js": "^2.5.1",
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-rnd": "^10.3.0",
|
||||
|
|
@ -73,7 +80,6 @@
|
|||
"@cypress/react": "^5.9.0",
|
||||
"@cypress/webpack-dev-server": "^1.3.1",
|
||||
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"cypress": "^7.4.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
|
|
@ -2692,7 +2698,6 @@
|
|||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
|
||||
"integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2705,7 +2710,6 @@
|
|||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz",
|
||||
"integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2718,7 +2722,6 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz",
|
||||
"integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2731,7 +2734,6 @@
|
|||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz",
|
||||
"integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2744,7 +2746,6 @@
|
|||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz",
|
||||
"integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2757,7 +2758,6 @@
|
|||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz",
|
||||
"integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2770,7 +2770,6 @@
|
|||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz",
|
||||
"integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2783,7 +2782,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz",
|
||||
"integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2796,7 +2794,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz",
|
||||
"integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@svgr/babel-plugin-add-jsx-attribute": "^5.4.0",
|
||||
"@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0",
|
||||
|
|
@ -2819,7 +2816,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz",
|
||||
"integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@svgr/plugin-jsx": "^5.5.0",
|
||||
"camelcase": "^6.2.0",
|
||||
|
|
@ -2837,7 +2833,6 @@
|
|||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
|
||||
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
|
|
@ -2849,7 +2844,6 @@
|
|||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
|
@ -2865,7 +2859,6 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
|
|
@ -2881,7 +2874,6 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
|
|
@ -2899,7 +2891,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
|
@ -2908,7 +2899,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
|
|
@ -2917,7 +2907,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz",
|
||||
"integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.12.6"
|
||||
},
|
||||
|
|
@ -2933,7 +2922,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz",
|
||||
"integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.12.3",
|
||||
"@svgr/babel-preset": "^5.5.0",
|
||||
|
|
@ -2952,7 +2940,6 @@
|
|||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz",
|
||||
"integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.14.5",
|
||||
"@babel/generator": "^7.15.0",
|
||||
|
|
@ -2982,7 +2969,6 @@
|
|||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
|
|
@ -2991,7 +2977,6 @@
|
|||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -3000,7 +2985,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz",
|
||||
"integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
|
|
@ -3018,7 +3002,6 @@
|
|||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
|
@ -3034,7 +3017,6 @@
|
|||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -3043,7 +3025,6 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
|
|
@ -3059,7 +3040,6 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
|
|
@ -3077,7 +3057,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
|
|
@ -3086,7 +3065,6 @@
|
|||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
|
|
@ -3095,7 +3073,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz",
|
||||
"integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.12.3",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.12.1",
|
||||
|
|
@ -3118,7 +3095,6 @@
|
|||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz",
|
||||
"integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.14.5",
|
||||
"@babel/generator": "^7.15.0",
|
||||
|
|
@ -3148,7 +3124,6 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
|
|
@ -3162,7 +3137,6 @@
|
|||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
|
|
@ -3171,7 +3145,6 @@
|
|||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
|
|
@ -4734,6 +4707,14 @@
|
|||
"node": ">= 4.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "9.8.6",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
|
||||
|
|
@ -7614,6 +7595,11 @@
|
|||
"webidl-conversions": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/date-arithmetic": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz",
|
||||
"integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg=="
|
||||
},
|
||||
"node_modules/date-fns": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||
|
|
@ -8263,6 +8249,18 @@
|
|||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
},
|
||||
"node_modules/emoji-mart": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz",
|
||||
"integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"prop-types": "^15.6.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.0 || ^15.0.0-0 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
|
|
@ -10116,6 +10114,22 @@
|
|||
"webpack": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
|
||||
"integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/file-selector/node_modules/tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
},
|
||||
"node_modules/file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
|
@ -14225,6 +14239,11 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||
},
|
||||
"node_modules/memory-fs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||
|
|
@ -17396,6 +17415,47 @@
|
|||
"pure-color": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-big-calendar": {
|
||||
"version": "0.38.0",
|
||||
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-0.38.0.tgz",
|
||||
"integrity": "sha512-eoVkt9gTo+f1HBL09+o7dYLxp6QxHv52fcn50P5PfaWp3S98uGLQqoqsvghT85koMKvGfDVa5V0+J7yHcaF07Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.1.5",
|
||||
"clsx": "^1.0.4",
|
||||
"date-arithmetic": "^4.1.0",
|
||||
"dom-helpers": "^5.1.0",
|
||||
"invariant": "^2.2.4",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash-es": "^4.17.11",
|
||||
"memoize-one": "^5.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-overlays": "^4.1.1",
|
||||
"uncontrollable": "^7.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.6.1 || ^17",
|
||||
"react-dom": "^16.6.1 || ^17"
|
||||
}
|
||||
},
|
||||
"node_modules/react-big-calendar/node_modules/react-overlays": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-4.1.1.tgz",
|
||||
"integrity": "sha512-WtJifh081e6M24KnvTQoNjQEpz7HoLxqt8TwZM7LOYIkYJ8i/Ly1Xi7RVte87ZVnmqQ4PFaFiNHZhSINPSpdBQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@popperjs/core": "^2.5.3",
|
||||
"@restart/hooks": "^0.3.25",
|
||||
"@types/warning": "^3.0.0",
|
||||
"dom-helpers": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"uncontrollable": "^7.0.0",
|
||||
"warning": "^4.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.3.0",
|
||||
"react-dom": ">=16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-bootstrap": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.5.2.tgz",
|
||||
|
|
@ -17843,6 +17903,22 @@
|
|||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dropzone": {
|
||||
"version": "11.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.4.2.tgz",
|
||||
"integrity": "sha512-ocYzYn7Qgp0tFc1gQtUTOaHHSzVTwhWHxxY+r7cj2jJTPfMTZB5GWSJHdIVoxsl+EQENpjJ/6Zvcw0BqKZQ+Eg==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^2.2.1",
|
||||
"file-selector": "^0.2.2",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/react-easy-sort": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-0.2.1.tgz",
|
||||
|
|
@ -17866,6 +17942,35 @@
|
|||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.1.1.tgz",
|
||||
"integrity": "sha512-Odrp4wue0fHh0pOfZt5H+9nWCMtqs3wdlFSzZPp7qsxfzmbE26QmGWIh6hG43CukiPeOjA8WQhBJU8JwtWvWbQ==",
|
||||
"dependencies": {
|
||||
"goober": "^2.0.35"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast/node_modules/csstype": {
|
||||
"version": "2.6.18",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.18.tgz",
|
||||
"integrity": "sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/react-hot-toast/node_modules/goober": {
|
||||
"version": "2.0.41",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.0.41.tgz",
|
||||
"integrity": "sha512-kwjegMT5018zWydhOQlQneCgCtrKJaPsru7TaBWmTYV0nsMeUrM6L6O8JmNYb9UbPMgWcmtf+9p4Y3oJabIH1A==",
|
||||
"peerDependencies": {
|
||||
"csstype": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
|
@ -17911,6 +18016,41 @@
|
|||
"react": "^15.6.1 || ^16.0.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-mentions": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.3.0.tgz",
|
||||
"integrity": "sha512-qr60FhczwzDX/0ZHWKaV8nriDx0a/+totqa7z1jGiz8YipNMp3ReAJMfA+UUlIVj1lnbCzpFUAo/bTQBW0IVlg==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.4.5",
|
||||
"invariant": "^2.2.4",
|
||||
"prop-types": "^15.5.8",
|
||||
"substyle": "^9.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.3",
|
||||
"react-dom": ">=16.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-mentions/node_modules/@babel/runtime": {
|
||||
"version": "7.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
|
||||
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-mentions/node_modules/substyle": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz",
|
||||
"integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.3.4",
|
||||
"invariant": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.3"
|
||||
}
|
||||
},
|
||||
"node_modules/react-moment-proptypes": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-moment-proptypes/-/react-moment-proptypes-1.8.1.tgz",
|
||||
|
|
@ -26359,56 +26499,47 @@
|
|||
"@svgr/babel-plugin-add-jsx-attribute": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz",
|
||||
"integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg=="
|
||||
},
|
||||
"@svgr/babel-plugin-remove-jsx-attribute": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz",
|
||||
"integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg=="
|
||||
},
|
||||
"@svgr/babel-plugin-remove-jsx-empty-expression": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz",
|
||||
"integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA=="
|
||||
},
|
||||
"@svgr/babel-plugin-replace-jsx-attribute-value": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz",
|
||||
"integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ=="
|
||||
},
|
||||
"@svgr/babel-plugin-svg-dynamic-title": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz",
|
||||
"integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg=="
|
||||
},
|
||||
"@svgr/babel-plugin-svg-em-dimensions": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz",
|
||||
"integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw=="
|
||||
},
|
||||
"@svgr/babel-plugin-transform-react-native-svg": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz",
|
||||
"integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q=="
|
||||
},
|
||||
"@svgr/babel-plugin-transform-svg-component": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz",
|
||||
"integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ=="
|
||||
},
|
||||
"@svgr/babel-preset": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz",
|
||||
"integrity": "sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@svgr/babel-plugin-add-jsx-attribute": "^5.4.0",
|
||||
"@svgr/babel-plugin-remove-jsx-attribute": "^5.4.0",
|
||||
|
|
@ -26424,7 +26555,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz",
|
||||
"integrity": "sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@svgr/plugin-jsx": "^5.5.0",
|
||||
"camelcase": "^6.2.0",
|
||||
|
|
@ -26434,14 +26564,12 @@
|
|||
"camelcase": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
|
||||
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg=="
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
|
@ -26454,7 +26582,6 @@
|
|||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
|
|
@ -26464,7 +26591,6 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
|
|
@ -26475,14 +26601,12 @@
|
|||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -26490,7 +26614,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz",
|
||||
"integrity": "sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.12.6"
|
||||
}
|
||||
|
|
@ -26499,7 +26622,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz",
|
||||
"integrity": "sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.12.3",
|
||||
"@svgr/babel-preset": "^5.5.0",
|
||||
|
|
@ -26511,7 +26633,6 @@
|
|||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz",
|
||||
"integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.14.5",
|
||||
"@babel/generator": "^7.15.0",
|
||||
|
|
@ -26533,14 +26654,12 @@
|
|||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -26548,7 +26667,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz",
|
||||
"integrity": "sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
|
|
@ -26559,7 +26677,6 @@
|
|||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz",
|
||||
"integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
|
|
@ -26571,14 +26688,12 @@
|
|||
"deepmerge": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
|
|
@ -26588,7 +26703,6 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
|
|
@ -26599,14 +26713,12 @@
|
|||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -26614,7 +26726,6 @@
|
|||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz",
|
||||
"integrity": "sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.12.3",
|
||||
"@babel/plugin-transform-react-constant-elements": "^7.12.1",
|
||||
|
|
@ -26630,7 +26741,6 @@
|
|||
"version": "7.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.0.tgz",
|
||||
"integrity": "sha512-tXtmTminrze5HEUPn/a0JtOzzfp0nk+UEXQ/tqIJo3WDGypl/2OFQEMll/zSFU8f/lfmfLXvTaORHF3cfXIQMw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.14.5",
|
||||
"@babel/generator": "^7.15.0",
|
||||
|
|
@ -26653,7 +26763,6 @@
|
|||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"big.js": "^5.2.2",
|
||||
"emojis-list": "^3.0.0",
|
||||
|
|
@ -26663,14 +26772,12 @@
|
|||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -27925,6 +28032,11 @@
|
|||
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
|
||||
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
|
||||
},
|
||||
"attr-accept": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.8.6",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
|
||||
|
|
@ -30244,6 +30356,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"date-arithmetic": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz",
|
||||
"integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg=="
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "1.30.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
|
||||
|
|
@ -30786,6 +30903,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"emoji-mart": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-3.0.1.tgz",
|
||||
"integrity": "sha512-sxpmMKxqLvcscu6mFn9ITHeZNkGzIvD0BSNFE/LJESPbCA8s1jM6bCDPjWbV31xHq7JXaxgpHxLB54RCbBZSlg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
|
|
@ -32208,6 +32334,21 @@
|
|||
"schema-utils": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.2.4.tgz",
|
||||
"integrity": "sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
|
@ -35386,6 +35527,11 @@
|
|||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||
},
|
||||
"memory-fs": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
|
||||
|
|
@ -37974,6 +38120,41 @@
|
|||
"pure-color": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"react-big-calendar": {
|
||||
"version": "0.38.0",
|
||||
"resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-0.38.0.tgz",
|
||||
"integrity": "sha512-eoVkt9gTo+f1HBL09+o7dYLxp6QxHv52fcn50P5PfaWp3S98uGLQqoqsvghT85koMKvGfDVa5V0+J7yHcaF07Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.5",
|
||||
"clsx": "^1.0.4",
|
||||
"date-arithmetic": "^4.1.0",
|
||||
"dom-helpers": "^5.1.0",
|
||||
"invariant": "^2.2.4",
|
||||
"lodash": "^4.17.11",
|
||||
"lodash-es": "^4.17.11",
|
||||
"memoize-one": "^5.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-overlays": "^4.1.1",
|
||||
"uncontrollable": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-overlays": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-4.1.1.tgz",
|
||||
"integrity": "sha512-WtJifh081e6M24KnvTQoNjQEpz7HoLxqt8TwZM7LOYIkYJ8i/Ly1Xi7RVte87ZVnmqQ4PFaFiNHZhSINPSpdBQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@popperjs/core": "^2.5.3",
|
||||
"@restart/hooks": "^0.3.25",
|
||||
"@types/warning": "^3.0.0",
|
||||
"dom-helpers": "^5.2.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"uncontrollable": "^7.0.0",
|
||||
"warning": "^4.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-bootstrap": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.5.2.tgz",
|
||||
|
|
@ -38311,6 +38492,16 @@
|
|||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "11.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-11.4.2.tgz",
|
||||
"integrity": "sha512-ocYzYn7Qgp0tFc1gQtUTOaHHSzVTwhWHxxY+r7cj2jJTPfMTZB5GWSJHdIVoxsl+EQENpjJ/6Zvcw0BqKZQ+Eg==",
|
||||
"requires": {
|
||||
"attr-accept": "^2.2.1",
|
||||
"file-selector": "^0.2.2",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"react-easy-sort": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-sort/-/react-easy-sort-0.2.1.tgz",
|
||||
|
|
@ -38332,6 +38523,28 @@
|
|||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||
"integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
|
||||
},
|
||||
"react-hot-toast": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.1.1.tgz",
|
||||
"integrity": "sha512-Odrp4wue0fHh0pOfZt5H+9nWCMtqs3wdlFSzZPp7qsxfzmbE26QmGWIh6hG43CukiPeOjA8WQhBJU8JwtWvWbQ==",
|
||||
"requires": {
|
||||
"goober": "^2.0.35"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "2.6.18",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.18.tgz",
|
||||
"integrity": "sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==",
|
||||
"peer": true
|
||||
},
|
||||
"goober": {
|
||||
"version": "2.0.41",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.0.41.tgz",
|
||||
"integrity": "sha512-kwjegMT5018zWydhOQlQneCgCtrKJaPsru7TaBWmTYV0nsMeUrM6L6O8JmNYb9UbPMgWcmtf+9p4Y3oJabIH1A==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
|
@ -38367,6 +38580,36 @@
|
|||
"@emotion/core": "^10.0.22"
|
||||
}
|
||||
},
|
||||
"react-mentions": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-4.3.0.tgz",
|
||||
"integrity": "sha512-qr60FhczwzDX/0ZHWKaV8nriDx0a/+totqa7z1jGiz8YipNMp3ReAJMfA+UUlIVj1lnbCzpFUAo/bTQBW0IVlg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "7.4.5",
|
||||
"invariant": "^2.2.4",
|
||||
"prop-types": "^15.5.8",
|
||||
"substyle": "^9.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz",
|
||||
"integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
},
|
||||
"substyle": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "https://registry.npmjs.org/substyle/-/substyle-9.4.1.tgz",
|
||||
"integrity": "sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.4",
|
||||
"invariant": "^2.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-moment-proptypes": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-moment-proptypes/-/react-moment-proptypes-1.8.1.tgz",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"@react-google-maps/api": "^2.1.1",
|
||||
"@sentry/react": "^6.12.0",
|
||||
"@sentry/tracing": "^6.12.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
|
|
@ -22,10 +23,11 @@
|
|||
"classnames": "^2.3.1",
|
||||
"dompurify": "^2.2.7",
|
||||
"draft-js": "^0.11.7",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"draft-js-export-html": "^1.4.1",
|
||||
"emoji-mart": "^3.0.1",
|
||||
"fuse.js": "^6.4.6",
|
||||
"history": "^4.9.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.1",
|
||||
|
|
@ -34,6 +36,7 @@
|
|||
"plotly.js-basic-dist-min": "^1.58.4",
|
||||
"query-string": "^6.13.6",
|
||||
"react": "^16.14.0",
|
||||
"react-big-calendar": "^0.38.0",
|
||||
"react-bootstrap": "^1.5.2",
|
||||
"react-color": "^2.19.3",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
|
|
@ -42,10 +45,13 @@
|
|||
"react-dnd": "^14.0.2",
|
||||
"react-dnd-html5-backend": "^14.0.0",
|
||||
"react-dom": "^16.14.0",
|
||||
"react-dropzone": "^11.4.2",
|
||||
"react-easy-sort": "^0.2.1",
|
||||
"react-hot-toast": "^2.1.1",
|
||||
"react-json-view": "^1.21.3",
|
||||
"react-lazyload": "^3.2.0",
|
||||
"react-loading-skeleton": "^2.2.0",
|
||||
"react-mentions": "^4.3.0",
|
||||
"react-plotly.js": "^2.5.1",
|
||||
"react-qr-reader": "^2.2.1",
|
||||
"react-rnd": "^10.3.0",
|
||||
|
|
@ -61,15 +67,14 @@
|
|||
"semver": "^5.7.1",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"uuid": "8.3.2",
|
||||
"yup": "^0.27.0",
|
||||
"webpack": "^5.55.1",
|
||||
"webpack-cli": "^4.8.0"
|
||||
"webpack-cli": "^4.8.0",
|
||||
"yup": "^0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/react": "^5.9.0",
|
||||
"@cypress/webpack-dev-server": "^1.3.1",
|
||||
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"cypress": "^7.4.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
|
|
@ -98,4 +103,4 @@
|
|||
"production": [],
|
||||
"development": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { InvitationPage } from '@/InvitationPage';
|
|||
import { Authorize } from '@/Oauth2';
|
||||
import { Editor, Viewer } from '@/Editor';
|
||||
import '@/_styles/theme.scss';
|
||||
import 'emoji-mart/css/emoji-mart.css';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import { ManageGroupPermissions } from '@/ManageGroupPermissions';
|
||||
|
|
@ -20,6 +21,7 @@ import { OnboardingModal } from '@/Onboarding/OnboardingModal';
|
|||
import { ForgotPassword } from '@/ForgotPassword';
|
||||
import { ResetPassword } from '@/ResetPassword';
|
||||
import { lt } from 'semver';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -63,102 +65,119 @@ class App extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<Router history={history}>
|
||||
<div className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`}>
|
||||
{updateAvailable && (
|
||||
<div className="alert alert-info alert-dismissible" role="alert">
|
||||
<h3 className="mb-1">Update available</h3>
|
||||
<p>A new version of ToolJet has been released.</p>
|
||||
<div className="btn-list">
|
||||
<a
|
||||
href="https://docs.tooljet.io/docs/setup/updating"
|
||||
target="_blank"
|
||||
className="btn btn-info"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read release notes & update
|
||||
</a>
|
||||
<a
|
||||
onClick={() => {
|
||||
tooljetService.skipVersion();
|
||||
this.setState({ updateAvailable: false });
|
||||
}}
|
||||
className="btn"
|
||||
>
|
||||
Skip this version
|
||||
</a>
|
||||
<>
|
||||
<Router history={history}>
|
||||
<div className={`main-wrapper ${darkMode ? 'theme-dark' : ''}`}>
|
||||
{updateAvailable && (
|
||||
<div className="alert alert-info alert-dismissible" role="alert">
|
||||
<h3 className="mb-1">Update available</h3>
|
||||
<p>A new version of ToolJet has been released.</p>
|
||||
<div className="btn-list">
|
||||
<a
|
||||
href="https://docs.tooljet.io/docs/setup/updating"
|
||||
target="_blank"
|
||||
className="btn btn-info"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read release notes & update
|
||||
</a>
|
||||
<a
|
||||
onClick={() => {
|
||||
tooljetService.skipVersion();
|
||||
this.setState({ updateAvailable: false });
|
||||
}}
|
||||
className="btn"
|
||||
>
|
||||
Skip this version
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
|
||||
{!onboarded && <OnboardingModal />}
|
||||
{!onboarded && <OnboardingModal />}
|
||||
|
||||
<ToastContainer />
|
||||
<ToastContainer />
|
||||
|
||||
<PrivateRoute exact path="/" component={HomePage} switchDarkMode={this.switchDarkMode} darkMode={darkMode} />
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/signup" component={SignupPage} />
|
||||
<Route path="/forgot-password" component={ForgotPassword} />
|
||||
<Route path="/reset-password" component={ResetPassword} />
|
||||
<Route path="/invitations/:token" component={InvitationPage} />
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/apps/:id"
|
||||
component={Editor}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:id/versions/:versionId"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:slug"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/oauth2/authorize"
|
||||
component={Authorize}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/users"
|
||||
component={ManageOrgUsers}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/groups"
|
||||
component={ManageGroupPermissions}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/groups/:id"
|
||||
component={ManageGroupPermissionResources}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/settings"
|
||||
component={SettingsPage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
</div>
|
||||
</Router>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/"
|
||||
component={HomePage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<Route path="/login" component={LoginPage} />
|
||||
<Route path="/signup" component={SignupPage} />
|
||||
<Route path="/forgot-password" component={ForgotPassword} />
|
||||
<Route path="/reset-password" component={ResetPassword} />
|
||||
<Route path="/invitations/:token" component={InvitationPage} />
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/apps/:id"
|
||||
component={Editor}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:id/versions/:versionId"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/applications/:slug"
|
||||
component={Viewer}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/oauth2/authorize"
|
||||
component={Authorize}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/users"
|
||||
component={ManageOrgUsers}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/groups"
|
||||
component={ManageGroupPermissions}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/groups/:id"
|
||||
component={ManageGroupPermissionResources}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
<PrivateRoute
|
||||
exact
|
||||
path="/settings"
|
||||
component={SettingsPage}
|
||||
switchDarkMode={this.switchDarkMode}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
</div>
|
||||
</Router>
|
||||
<Toaster
|
||||
toastOptions={{
|
||||
style: {
|
||||
borderRadius: '10px',
|
||||
background: '#333',
|
||||
color: '#fff',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ import { ToggleSwitch } from './Components/Toggle';
|
|||
import { RadioButton } from './Components/RadioButton';
|
||||
import { StarRating } from './Components/StarRating';
|
||||
import { Divider } from './Components/Divider';
|
||||
import { FilePicker } from './Components/FilePicker';
|
||||
import { PasswordInput } from './Components/PasswordInput';
|
||||
import { Calendar } from './Components/Calendar';
|
||||
import { renderTooltip } from '../_helpers/appUtils';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import '@/_styles/custom.scss';
|
||||
|
|
@ -51,7 +53,9 @@ const AllComponents = {
|
|||
RadioButton,
|
||||
StarRating,
|
||||
Divider,
|
||||
FilePicker,
|
||||
PasswordInput,
|
||||
Calendar,
|
||||
};
|
||||
|
||||
export const Box = function Box({
|
||||
|
|
@ -72,6 +76,7 @@ export const Box = function Box({
|
|||
containerProps,
|
||||
darkMode,
|
||||
removeComponent,
|
||||
mode,
|
||||
}) {
|
||||
const backgroundColor = yellow ? 'yellow' : '';
|
||||
|
||||
|
|
@ -91,7 +96,12 @@ export const Box = function Box({
|
|||
const resolvedStyles = resolveStyles(component, currentState);
|
||||
const exposedVariables = currentState?.components[component.name] ?? {};
|
||||
|
||||
const fireEvent = (eventName, options) => onEvent(eventName, { ...options, component });
|
||||
const fireEvent = (eventName, options) => {
|
||||
if (mode === 'edit' && eventName === 'onClick') {
|
||||
onComponentClick(id, component);
|
||||
}
|
||||
onEvent(eventName, { ...options, component });
|
||||
};
|
||||
const validate = (value) =>
|
||||
validateWidget({
|
||||
...{ widgetValue: value },
|
||||
|
|
|
|||
75
frontend/src/Editor/Comment/CommentActions.jsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
|
||||
import OptionsIcon from './icons/options.svg';
|
||||
// import OptionsSelectedIcon from './icons/options-selected.svg';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
import { commentsService } from '@/_services';
|
||||
|
||||
const CommentActions = ({
|
||||
socket,
|
||||
commentId,
|
||||
comment,
|
||||
setEditCommentId,
|
||||
setEditComment,
|
||||
fetchComments,
|
||||
isCommentOwner,
|
||||
}) => {
|
||||
const [open, trigger, content, setOpen] = usePopover(false);
|
||||
const popoverFadeStyle = useSpring({ opacity: open ? 1 : 0 });
|
||||
const router = useRouter();
|
||||
|
||||
const handleDelete = async () => {
|
||||
await commentsService.deleteComment(commentId);
|
||||
fetchComments();
|
||||
setOpen(false);
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'notifications', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const handleEdit = async () => {
|
||||
setEditComment(comment);
|
||||
setEditCommentId(commentId);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="ms-auto cursor-pointer position-relative">
|
||||
{isCommentOwner && (
|
||||
<>
|
||||
<span {...trigger} className="m-2" title="comment options">
|
||||
<OptionsIcon />
|
||||
</span>
|
||||
<animated.div
|
||||
{...content}
|
||||
style={popoverFadeStyle}
|
||||
className={cx('card popover comment-actions-popover', {
|
||||
show: open,
|
||||
hide: !open,
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<div className="comment-action" onClick={handleEdit}>
|
||||
Edit
|
||||
</div>
|
||||
{/* TODO: Add a popup confirmation on delete */}
|
||||
<div className="comment-action border-top" onClick={handleDelete}>
|
||||
Delete
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentActions;
|
||||
88
frontend/src/Editor/Comment/CommentBody.jsx
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
|
||||
import { isEmpty } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import CommentActions from './CommentActions';
|
||||
|
||||
moment.updateLocale('en', {
|
||||
relativeTime: {
|
||||
past: '%s',
|
||||
s: 'just now',
|
||||
},
|
||||
});
|
||||
|
||||
const CommentBody = ({ socket, thread, isLoading, setEditComment, setEditCommentId, fetchComments }) => {
|
||||
const bottomRef = React.useRef();
|
||||
|
||||
const scrollToBottom = () => {
|
||||
bottomRef?.current?.scrollIntoView({
|
||||
behavior: 'instant',
|
||||
block: 'center',
|
||||
});
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [thread]);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
scrollToBottom();
|
||||
}, []);
|
||||
|
||||
const getComment = (comment) => {
|
||||
var regex = /(\()([^)]+)(\))/g;
|
||||
return comment.replace(regex, '<span class=mentioned-user>$2</span>');
|
||||
};
|
||||
|
||||
const getContent = () => {
|
||||
if (isEmpty(thread)) return <div className="text-center">There are no comments to display</div>;
|
||||
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||
return (
|
||||
<div className="divide-y">
|
||||
{thread.map(({ id, comment, createdAt, user = {} }) => {
|
||||
return (
|
||||
<div key={id}>
|
||||
<div className="d-flex card-title comment-author">
|
||||
{`${user?.firstName} ${user?.lastName}`}{' '}
|
||||
<CommentActions
|
||||
socket={socket}
|
||||
fetchComments={fetchComments}
|
||||
comment={comment}
|
||||
commentId={id}
|
||||
isCommentOwner={currentUser.id === user.id}
|
||||
setEditComment={setEditComment}
|
||||
setEditCommentId={setEditCommentId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="card-subtitle comment-time">{moment(createdAt).fromNow()}</div>
|
||||
<p className="cursor-auto comment-body " dangerouslySetInnerHTML={{ __html: getComment(comment) }} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="card-body text-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={cx('comment-body card-body card-body-scrollable card-body-scrollable-shadow')}>
|
||||
{getContent()}
|
||||
<div ref={bottomRef} className="list-bottom"></div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentBody;
|
||||
70
frontend/src/Editor/Comment/CommentFooter.jsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Picker } from 'emoji-mart';
|
||||
|
||||
import TextareaMentions from '@/_ui/Mentions';
|
||||
import Button from '@/_ui/Button';
|
||||
import useShortcuts from '@/_hooks/use-shortcuts';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
|
||||
function CommentFooter({ editComment = '', editCommentId, handleSubmit }) {
|
||||
const [comment, setComment] = React.useState(editComment);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [open, trigger, content, setOpen] = usePopover(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
setComment(editComment);
|
||||
}, [editComment]);
|
||||
|
||||
const handleClick = async () => {
|
||||
setLoading(true);
|
||||
await handleSubmit(comment, editCommentId);
|
||||
setComment('');
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const addEmoji = (emoji) => {
|
||||
setComment(comment + ' ' + emoji.native);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
useShortcuts(['Meta', 'Enter'], () => handleClick(), [comment]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div {...content} className={open ? 'show' : 'hide'}>
|
||||
<Picker style={{ width: 320 }} set="apple" onSelect={addEmoji} />
|
||||
</div>
|
||||
<div className="card-footer">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-8">
|
||||
<TextareaMentions value={comment} setValue={setComment} placeholder="Type your comment here" />
|
||||
</div>
|
||||
<div className="col-1 cursor-pointer">
|
||||
<svg {...trigger} width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6.6665 11.6667C6.6665 11.6667 7.9165 13.3334 9.99984 13.3334C12.0832 13.3334 13.3332 11.6667 13.3332 11.6667M7.5 7.5H7.51M12.5 7.5H12.51M18.3332 10.0001C18.3332 14.6025 14.6022 18.3334 9.99984 18.3334C5.39746 18.3334 1.6665 14.6025 1.6665 10.0001C1.6665 5.39771 5.39746 1.66675 9.99984 1.66675C14.6022 1.66675 18.3332 5.39771 18.3332 10.0001Z"
|
||||
stroke="#5E5E5E"
|
||||
strokeWidth="1.8"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
className={cx('col-3', {
|
||||
'cursor-pointer': !!comment,
|
||||
'cursor-not-allowed': !comment,
|
||||
})}
|
||||
>
|
||||
<Button loading={loading} disabled={!comment} className={`m2 btn-sm rounded-2`} onClick={handleClick}>
|
||||
Send
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CommentFooter;
|
||||
114
frontend/src/Editor/Comment/CommentHeader.jsx
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import { commentsService } from '@/_services';
|
||||
|
||||
import { pluralize } from '@/_helpers/utils';
|
||||
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
import UnResolvedIcon from './icons/unresolved.svg';
|
||||
import ResolvedIcon from './icons/resolved.svg';
|
||||
|
||||
const CommentHeader = ({ socket, count = 0, threadId, isResolved, isThreadOwner, fetchThreads, close }) => {
|
||||
const [spinning, setSpinning] = React.useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
const handleResolved = async () => {
|
||||
setSpinning(true);
|
||||
await commentsService.updateThread(threadId, { isResolved: !isResolved });
|
||||
setSpinning(false);
|
||||
fetchThreads();
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'notifications', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
if (!isResolved) {
|
||||
toast.success('Thread resolved');
|
||||
} else {
|
||||
toast('Thread unresolved');
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
await commentsService.deleteThread(threadId);
|
||||
toast.success('Thread deleted');
|
||||
fetchThreads();
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'notifications', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const getResolveIcon = () => {
|
||||
if (spinning) return <Spinner />;
|
||||
|
||||
if (isResolved) return <ResolvedIcon />;
|
||||
|
||||
return <UnResolvedIcon />;
|
||||
};
|
||||
|
||||
const getIcon = () => {
|
||||
if (isResolved)
|
||||
return (
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="6" cy="6" r="6" fill="#8991A0" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
return (
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="6" cy="6" r="6" fill="#FCAA0D" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card-header">
|
||||
<div className="card-subtitle mt-1">
|
||||
{getIcon()} {pluralize(count, 'comment')}
|
||||
</div>
|
||||
<div className="ms-auto d-flex">
|
||||
<span
|
||||
title={isThreadOwner ? 'toggle resolved' : 'only creator of thread can resolve'}
|
||||
className={cx('m-1 cursor-pointer', { disabled: !isThreadOwner })}
|
||||
onClick={handleResolved}
|
||||
>
|
||||
{getResolveIcon()}
|
||||
</span>
|
||||
<svg
|
||||
onClick={handleDelete}
|
||||
className="m-1 cursor-pointer"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5 15.8333C5 16.75 5.75 17.5 6.66667 17.5H13.3333C14.25 17.5 15 16.75 15 15.8333V5.83333H5V15.8333ZM6.66667 7.5H13.3333V15.8333H6.66667V7.5ZM12.9167 3.33333L12.0833 2.5H7.91667L7.08333 3.33333H5V5H15V3.33333H12.9167Z"
|
||||
fill="#8092AC"
|
||||
/>
|
||||
</svg>
|
||||
<div onClick={close} className="m-1 cursor-pointer">
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.64628 7.62107L11.6712 12.6474L12.647 11.673L7.62067 6.64669L12.647 1.62176L11.6726 0.645996L6.64628 5.6723L1.62136 0.645996L0.646973 1.62176L5.6719 6.64669L0.646973 11.6716L1.62136 12.6474L6.64628 7.62107Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentHeader;
|
||||
6
frontend/src/Editor/Comment/icons/options-selected.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="10" r="10" fill="#B4BBC6"/>
|
||||
<path d="M10.0001 10.8334C10.4603 10.8334 10.8334 10.4603 10.8334 10.0001C10.8334 9.53984 10.4603 9.16675 10.0001 9.16675C9.53984 9.16675 9.16675 9.53984 9.16675 10.0001C9.16675 10.4603 9.53984 10.8334 10.0001 10.8334Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.8333 10.8334C16.2936 10.8334 16.6667 10.4603 16.6667 10.0001C16.6667 9.53984 16.2936 9.16675 15.8333 9.16675C15.3731 9.16675 15 9.53984 15 10.0001C15 10.4603 15.3731 10.8334 15.8333 10.8334Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M4.16671 10.8334C4.62694 10.8334 5.00004 10.4603 5.00004 10.0001C5.00004 9.53984 4.62694 9.16675 4.16671 9.16675C3.70647 9.16675 3.33337 9.53984 3.33337 10.0001C3.33337 10.4603 3.70647 10.8334 4.16671 10.8334Z" stroke="black" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
23
frontend/src/Editor/Comment/icons/options.svg
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M7.99992 8.66659C8.36811 8.66659 8.66659 8.36811 8.66659 7.99992C8.66659 7.63173 8.36811 7.33325 7.99992 7.33325C7.63173 7.33325 7.33325 7.63173 7.33325 7.99992C7.33325 8.36811 7.63173 8.66659 7.99992 8.66659Z"
|
||||
stroke="black"
|
||||
strokeWidth="1.33333"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.6667 8.66659C13.0349 8.66659 13.3333 8.36811 13.3333 7.99992C13.3333 7.63173 13.0349 7.33325 12.6667 7.33325C12.2985 7.33325 12 7.63173 12 7.99992C12 8.36811 12.2985 8.66659 12.6667 8.66659Z"
|
||||
stroke="black"
|
||||
strokeWidth="1.33333"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.33329 8.66659C3.70148 8.66659 3.99996 8.36811 3.99996 7.99992C3.99996 7.63173 3.70148 7.33325 3.33329 7.33325C2.9651 7.33325 2.66663 7.63173 2.66663 7.99992C2.66663 8.36811 2.9651 8.66659 3.33329 8.66659Z"
|
||||
stroke="black"
|
||||
strokeWidth="1.33333"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
3
frontend/src/Editor/Comment/icons/resolved.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0.833252C4.93751 0.833252 0.833344 4.93742 0.833344 9.99992C0.833344 15.0624 4.93751 19.1666 10 19.1666C15.0625 19.1666 19.1667 15.0624 19.1667 9.99992C19.1667 4.93742 15.0625 0.833252 10 0.833252ZM13.9733 8.44992C14.0465 8.36629 14.1022 8.26887 14.1372 8.16339C14.1721 8.05792 14.1856 7.94651 14.1769 7.83574C14.1682 7.72497 14.1374 7.61706 14.0863 7.51836C14.0353 7.41966 13.965 7.33217 13.8796 7.26104C13.7943 7.1899 13.6955 7.13655 13.5893 7.10414C13.483 7.07172 13.3713 7.06089 13.2607 7.07228C13.1502 7.08366 13.0431 7.11704 12.9456 7.17045C12.8482 7.22385 12.7624 7.29621 12.6933 7.38325L9.11001 11.6824L7.25584 9.82742C7.09868 9.67562 6.88817 9.59163 6.66968 9.59352C6.45118 9.59542 6.24217 9.68306 6.08766 9.83757C5.93316 9.99208 5.84551 10.2011 5.84362 10.4196C5.84172 10.6381 5.92571 10.8486 6.07751 11.0058L8.57751 13.5058C8.65939 13.5876 8.75742 13.6515 8.86536 13.6933C8.97329 13.7351 9.08876 13.754 9.2044 13.7488C9.32004 13.7435 9.43332 13.7143 9.53702 13.6628C9.64072 13.6113 9.73255 13.5388 9.80668 13.4499L13.9733 8.44992Z" fill="#2ECC71"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
20
frontend/src/Editor/Comment/icons/unresolved.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5.33325 8.33331L7.33325 10.3333L10.6666 6.33331"
|
||||
stroke="#8991A0"
|
||||
strokeWidth="1.33333"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M7.99992 14.6666C11.6818 14.6666 14.6666 11.6819 14.6666 7.99998C14.6666 4.31808 11.6818 1.33331 7.99992 1.33331C4.31802 1.33331 1.33325 4.31808 1.33325 7.99998C1.33325 11.6819 4.31802 14.6666 7.99992 14.6666Z"
|
||||
stroke="#8991A0"
|
||||
strokeWidth="1.33333"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 561 B |
170
frontend/src/Editor/Comment/index.jsx
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
|
||||
import { useDrag } from 'react-dnd';
|
||||
import { ItemTypes } from '@/Editor/ItemTypes';
|
||||
import CommentHeader from '@/Editor/Comment/CommentHeader';
|
||||
import CommentBody from '@/Editor/Comment/CommentBody';
|
||||
import CommentFooter from '@/Editor/Comment/CommentFooter';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
import { commentsService } from '@/_services';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
const Comment = ({ socket, x, y, threadId, user = {}, isResolved, fetchThreads, appVersionsId }) => {
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [editComment, setEditComment] = React.useState('');
|
||||
const [editCommentId, setEditCommentId] = React.useState('');
|
||||
const [thread, setThread] = React.useState([]);
|
||||
const [placement, setPlacement] = React.useState('left');
|
||||
const [open, trigger, content, setOpen] = usePopover(false);
|
||||
const [, drag] = useDrag(() => ({
|
||||
type: ItemTypes.COMMENT,
|
||||
item: { threadId, name: 'comment' },
|
||||
}));
|
||||
const router = useRouter();
|
||||
|
||||
React.useEffect(() => {
|
||||
// Listen for messages
|
||||
// TODO: add check if user is the initiator of this event, don't fetch data
|
||||
socket?.addEventListener('message', function (event) {
|
||||
if (event.data === threadId) fetchData();
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const { left } = trigger?.ref?.current?.getBoundingClientRect();
|
||||
|
||||
if (left < 460) setPlacement('right');
|
||||
else setPlacement('left');
|
||||
}, [trigger]);
|
||||
|
||||
async function fetchData() {
|
||||
const { data } = await commentsService.getComments(threadId, appVersionsId);
|
||||
setThread(data);
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
fetchData();
|
||||
} else {
|
||||
// resetting the query param
|
||||
router.push(window.location.pathname);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (router.query.threadId === threadId) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
setOpen(false);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
const handleSubmit = async (comment) => {
|
||||
await commentsService.createComment({
|
||||
threadId,
|
||||
comment,
|
||||
appVersionsId,
|
||||
});
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: threadId, appId: router.query.id },
|
||||
})
|
||||
);
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'notifications', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
fetchData();
|
||||
};
|
||||
|
||||
const handleEdit = async (comment, cid) => {
|
||||
await commentsService.updateComment(cid, { comment });
|
||||
fetchData();
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'notifications', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const commentFadeStyle = useSpring({ from: { opacity: 0 }, to: { opacity: 1 } });
|
||||
const popoverFadeStyle = useSpring({ opacity: open ? 1 : 0 });
|
||||
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||
|
||||
return (
|
||||
<>
|
||||
<animated.div
|
||||
ref={drag}
|
||||
id={`thread-${threadId}`}
|
||||
className={cx('comments cursor-move', { open: open })}
|
||||
style={{
|
||||
transform: `translate(${x}px, ${y}px)`,
|
||||
...commentFadeStyle,
|
||||
}}
|
||||
onDragStart={() => setOpen(false)}
|
||||
onDragEnd={() => setOpen(true)}
|
||||
>
|
||||
<label {...trigger} className="form-selectgroup-item">
|
||||
<span
|
||||
className={cx('comment cursor-move avatar avatar-sm shadow-lg bg-white avatar-rounded', {
|
||||
resolved: isResolved,
|
||||
'comment-open': open,
|
||||
})}
|
||||
>
|
||||
{`${user.firstName?.charAt(0)}${user.lastName?.charAt(0)}`}
|
||||
</span>
|
||||
</label>
|
||||
<animated.div
|
||||
{...content}
|
||||
style={popoverFadeStyle}
|
||||
className={cx('card popover comment-popover', {
|
||||
'open-left': placement === 'left',
|
||||
'open-right': placement === 'right',
|
||||
'comment-is-open': open,
|
||||
show: open,
|
||||
hide: !open,
|
||||
})}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<CommentHeader
|
||||
count={thread?.length}
|
||||
close={() =>
|
||||
setTimeout(() => {
|
||||
setOpen(false);
|
||||
}, 0)
|
||||
}
|
||||
socket={socket}
|
||||
threadId={threadId}
|
||||
fetchThreads={fetchThreads}
|
||||
isThreadOwner={currentUser.id === user.id}
|
||||
isResolved={isResolved}
|
||||
/>
|
||||
<CommentBody
|
||||
socket={socket}
|
||||
setEditComment={setEditComment}
|
||||
setEditCommentId={setEditCommentId}
|
||||
fetchComments={fetchData}
|
||||
isLoading={loading}
|
||||
thread={thread}
|
||||
/>
|
||||
<CommentFooter
|
||||
editComment={editComment}
|
||||
editCommentId={editCommentId}
|
||||
handleSubmit={editCommentId ? handleEdit : handleSubmit}
|
||||
/>
|
||||
</animated.div>
|
||||
</animated.div>
|
||||
{open && <div className="comment-overlay" onClick={(e) => e.stopPropagation()} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Comment;
|
||||
133
frontend/src/Editor/CommentNotifications/Content.jsx
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { pluralize } from '@/_helpers/utils';
|
||||
import moment from 'moment';
|
||||
import usePopover from '@/_hooks/use-popover';
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
|
||||
const Content = ({ notifications, loading }) => {
|
||||
const router = useRouter();
|
||||
const [selectedCommentId, setSelectedCommentId] = React.useState(router.query.commentId);
|
||||
const [open, trigger, content] = usePopover(false);
|
||||
const popoverFadeStyle = useSpring({ opacity: open ? 1 : 0 });
|
||||
|
||||
React.useEffect(() => {
|
||||
if (router.query?.commentId) setSelectedCommentId(router.query?.commentId);
|
||||
else setSelectedCommentId('');
|
||||
}, [router]);
|
||||
|
||||
const getComment = (comment) => {
|
||||
var regex = /(\()([^)]+)(\))/g;
|
||||
return comment.replace(regex, '<span class=mentioned-user>$2</span>');
|
||||
};
|
||||
|
||||
const getContent = () => {
|
||||
if (isEmpty(notifications))
|
||||
return (
|
||||
<div className="empty">
|
||||
<p className="empty-title">{loading ? <Spinner /> : 'No messages to show'}</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="cursor-pointer">
|
||||
{notifications.map(({ comment, count }) => {
|
||||
return (
|
||||
<div
|
||||
className={cx('comment-notification', {
|
||||
'comment-notification-selected': selectedCommentId === comment.id,
|
||||
})}
|
||||
onClick={() => {
|
||||
router.push({
|
||||
pathname: window.location.pathname,
|
||||
search: `?threadId=${comment.thread.id}&commentId=${comment.id}`,
|
||||
});
|
||||
}}
|
||||
key={comment.id}
|
||||
>
|
||||
<div className="d-flex justify-content-between">
|
||||
<span className="comment-notification-user">
|
||||
{`${comment.user?.firstName} ${comment.user?.lastName}`}{' '}
|
||||
</span>
|
||||
<div className="comment-notification-count ms-auto">{moment(comment.createdAt).fromNow()}</div>
|
||||
</div>
|
||||
<div
|
||||
className="comment-notification-message"
|
||||
dangerouslySetInnerHTML={{ __html: getComment(comment.comment) }}
|
||||
/>
|
||||
<div className="my-2 count">{`${count - 1} replies`}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: move filter to separate file
|
||||
return (
|
||||
<div className="card">
|
||||
{!loading && (
|
||||
<div className="card-header">
|
||||
<sub className="fw-400 comment-notification-count light-gray">
|
||||
Total {pluralize(notifications.length, 'comment')}
|
||||
</sub>
|
||||
{/* <div className="ms-auto position-relative">
|
||||
<svg {...trigger} width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M14.3333 1H1L6.33333 7.30667V11.6667L9 13V7.30667L14.3333 1Z"
|
||||
stroke="#5E5E5E"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
<animated.div
|
||||
{...content}
|
||||
style={popoverFadeStyle}
|
||||
className={cx('card popover comment-notification-filter-popover', {
|
||||
show: open,
|
||||
hide: !open,
|
||||
})}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div>
|
||||
<div className="d-flex mb-3">
|
||||
<span>Show all</span>
|
||||
<div className="ms-auto">
|
||||
<svg width="12" height="9" viewBox="0 0 12 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M0.799988 4.5999L4.39999 8.1999L11.2 1.3999"
|
||||
stroke="#0565FF"
|
||||
strokeWidth="1.06667"
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
<span>Only mention of you</span>
|
||||
<div className="ms-auto">
|
||||
<svg width="12" height="9" viewBox="0 0 12 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M0.799988 4.5999L4.39999 8.1999L11.2 1.3999"
|
||||
stroke="#0565FF"
|
||||
strokeWidth="1.06667"
|
||||
strokeLinecap="square"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</animated.div>
|
||||
</div> */}
|
||||
</div>
|
||||
)}
|
||||
<div>{getContent()}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Content;
|
||||
83
frontend/src/Editor/CommentNotifications/index.jsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import '@/_styles/editor/comment-notifications.scss';
|
||||
|
||||
import React from 'react';
|
||||
import Tabs from 'react-bootstrap/Tabs';
|
||||
import Tab from 'react-bootstrap/Tab';
|
||||
|
||||
import { commentsService } from '@/_services';
|
||||
|
||||
import TabContent from './Content';
|
||||
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
const CommentNotifications = ({ socket, toggleComments, appVersionsId }) => {
|
||||
const [notifications, setNotifications] = React.useState([]);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [key, setKey] = React.useState('active');
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
async function fetchData(k) {
|
||||
const isResolved = k === 'resolved';
|
||||
setLoading(true);
|
||||
const { data } = await commentsService.getNotifications(router.query.id, isResolved, appVersionsId);
|
||||
setLoading(false);
|
||||
setNotifications(data);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Listen for messages
|
||||
socket?.addEventListener('message', function (event) {
|
||||
if (event.data === 'notifications') fetchData();
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="comment-notification-sidebar editor-sidebar">
|
||||
<div className="card-header">
|
||||
<span className="comment-notification-header">Comments</span>
|
||||
<div className="ms-auto">
|
||||
<svg
|
||||
className="cursor-pointer"
|
||||
onClick={toggleComments}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.64628 7.62107L11.6712 12.6474L12.647 11.673L7.62067 6.64669L12.647 1.62176L11.6726 0.645996L6.64628 5.6723L1.62136 0.645996L0.646973 1.62176L5.6719 6.64669L0.646973 11.6716L1.62136 12.6474L6.64628 7.62107Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<span className="border-bottom" />
|
||||
<Tabs
|
||||
activeKey={key}
|
||||
onSelect={async (k) => {
|
||||
setKey(k);
|
||||
setLoading(true);
|
||||
await fetchData(k);
|
||||
setLoading(false);
|
||||
}}
|
||||
className="dflex justify-content-center"
|
||||
>
|
||||
<Tab className="comment-notification-nav-item" eventKey="active" title="Active">
|
||||
<TabContent notifications={notifications} loading={loading} />
|
||||
</Tab>
|
||||
<Tab className="comment-notification-nav-item" eventKey="resolved" title="Resolved">
|
||||
<TabContent notifications={notifications} loading={loading} />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CommentNotifications;
|
||||
52
frontend/src/Editor/Comments.jsx
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import '@/_styles/editor/comments.scss';
|
||||
|
||||
import React from 'react';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
import Comment from './Comment';
|
||||
import { commentsService } from '@/_services';
|
||||
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
|
||||
const Comments = ({ newThread = {}, appVersionsId, socket }) => {
|
||||
const [threads, setThreads] = React.useState([]);
|
||||
const router = useRouter();
|
||||
|
||||
async function fetchData() {
|
||||
const { data } = await commentsService.getThreads(router.query.id, appVersionsId);
|
||||
setThreads(data);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Listen for messages
|
||||
socket?.addEventListener('message', function (event) {
|
||||
if (event.data === 'threads') fetchData();
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
!isEmpty(newThread) && setThreads([...threads, newThread]);
|
||||
}, [newThread]);
|
||||
|
||||
if (isEmpty(threads)) return null;
|
||||
|
||||
return threads.map((thread) => {
|
||||
const { id } = thread;
|
||||
return (
|
||||
<Comment
|
||||
key={id}
|
||||
appVersionsId={appVersionsId}
|
||||
fetchThreads={fetchData}
|
||||
socket={socket}
|
||||
threadId={id}
|
||||
{...thread}
|
||||
/>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default Comments;
|
||||
84
frontend/src/Editor/Components/Calendar.jsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import React from 'react';
|
||||
import { Calendar as ReactCalendar, momentLocalizer } from 'react-big-calendar';
|
||||
import moment from 'moment';
|
||||
import 'react-big-calendar/lib/css/react-big-calendar.css';
|
||||
|
||||
const localizer = momentLocalizer(moment);
|
||||
|
||||
const prepareEvent = (event, dateFormat) => ({
|
||||
...event,
|
||||
start: moment(event.start, dateFormat).toDate(),
|
||||
end: moment(event.end, dateFormat).toDate(),
|
||||
});
|
||||
|
||||
const parseDate = (date, dateFormat) => moment(date, dateFormat).toDate();
|
||||
|
||||
const allowedCalendarViews = ['month', 'week', 'day'];
|
||||
|
||||
export const Calendar = function ({ height, width, properties, styles, fireEvent, darkMode }) {
|
||||
const style = { height, width };
|
||||
const resourcesParam = properties.resources?.length === 0 ? {} : { resources: properties.resources };
|
||||
|
||||
const events = properties.events ? properties.events.map((event) => prepareEvent(event, properties.dateFormat)) : [];
|
||||
const defaultDate = parseDate(properties.defaultDate, properties.dateFormat);
|
||||
|
||||
const eventPropGetter = (event) => {
|
||||
const backgroundColor = event.color;
|
||||
const textStyle =
|
||||
event.textOrientation === 'vertical' ? { writingMode: 'vertical-rl', textOrientation: 'mixed' } : {};
|
||||
const style = { backgroundColor, ...textStyle, padding: 3, paddingLeft: 5, paddingRight: 5 };
|
||||
|
||||
return { style };
|
||||
};
|
||||
|
||||
const slotSelectHandler = (calendarSlots) => {
|
||||
const { slots, start, end, resourceId, action } = calendarSlots;
|
||||
const formattedSlots = slots.map((slot) => moment(slot).format(properties.dateFormat));
|
||||
const formattedStart = moment(start).format(properties.dateFormat);
|
||||
const formattedEnd = moment(end).format(properties.dateFormat);
|
||||
|
||||
const selectedSlots = {
|
||||
slots: formattedSlots,
|
||||
start: formattedStart,
|
||||
end: formattedEnd,
|
||||
resourceId,
|
||||
action,
|
||||
};
|
||||
|
||||
fireEvent('onCalendarSlotSelect', { selectedSlots });
|
||||
};
|
||||
|
||||
const defaultView = allowedCalendarViews.includes(properties.defaultView)
|
||||
? properties.defaultView
|
||||
: allowedCalendarViews[0];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ReactCalendar
|
||||
className={`calendar-widget
|
||||
${darkMode ? 'dark-mode' : ''}
|
||||
${styles.cellSizeInViewsClassifiedByResource}
|
||||
${properties.highlightToday ? '' : 'dont-highlight-today'}
|
||||
${properties.displayViewSwitcher ? '' : 'hide-view-switcher'}`}
|
||||
localizer={localizer}
|
||||
defaultDate={defaultDate}
|
||||
events={events}
|
||||
startAccessor="start"
|
||||
endAccessor="end"
|
||||
style={style}
|
||||
views={allowedCalendarViews}
|
||||
defaultView={defaultView}
|
||||
{...resourcesParam}
|
||||
resourceIdAccessor="resourceId"
|
||||
resourceTitleAccessor="title"
|
||||
onSelectEvent={(calendarEvent) => fireEvent('onCalendarEventSelect', { calendarEvent })}
|
||||
selectable={true}
|
||||
onSelectSlot={slotSelectHandler}
|
||||
toolbar={properties.displayToolbar}
|
||||
eventPropGetter={eventPropGetter}
|
||||
tooltipAccessor="tooltip"
|
||||
popup={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { resolveReferences, resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
|
||||
// Use plotly basic bundle
|
||||
|
|
@ -8,7 +8,6 @@ const Plot = createPlotlyComponent(Plotly);
|
|||
|
||||
export const Chart = function Chart({ id, width, height, component, onComponentClick, currentState, darkMode }) {
|
||||
const [loadingState, setLoadingState] = useState(false);
|
||||
const [chartData, setChartData] = useState([]);
|
||||
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
|
@ -87,8 +86,8 @@ export const Chart = function Chart({ id, width, height, component, onComponentC
|
|||
|
||||
const data = resolveReferences(dataString, currentState, []);
|
||||
|
||||
useEffect(() => {
|
||||
let rawData = data || [];
|
||||
const computeChartData = (data, dataString) => {
|
||||
let rawData = data;
|
||||
if (typeof rawData === 'string') {
|
||||
try {
|
||||
rawData = JSON.parse(dataString);
|
||||
|
|
@ -122,9 +121,11 @@ export const Chart = function Chart({ id, width, height, component, onComponentC
|
|||
];
|
||||
}
|
||||
|
||||
setChartData(newData);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, chartType]);
|
||||
return newData;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const memoizedChartData = useMemo(() => computeChartData(data, dataString), [data, dataString]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -132,7 +133,7 @@ export const Chart = function Chart({ id, width, height, component, onComponentC
|
|||
style={computedStyles}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
{loadingState === true ? (
|
||||
|
|
@ -143,7 +144,7 @@ export const Chart = function Chart({ id, width, height, component, onComponentC
|
|||
</div>
|
||||
) : (
|
||||
<Plot
|
||||
data={chartData}
|
||||
data={memoizedChartData}
|
||||
layout={layout}
|
||||
config={{
|
||||
displayModeBar: false,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ export const Checkbox = function Checkbox({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<div className="col px-1 py-0 mt-0">
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export const Datepicker = function Datepicker({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<Datetime
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export const DaterangePicker = function DaterangePicker({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<DateRangePicker
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export const DropDown = function DropDown({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<div className="col-auto my-auto">
|
||||
|
|
|
|||
257
frontend/src/Editor/Components/FilePicker.jsx
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
import React, { useEffect, useMemo } from 'react';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { resolveWidgetFieldValue } from '@/_helpers/utils';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
export const FilePicker = ({ width, height, component, currentState, onComponentOptionChanged, onEvent, darkMode }) => {
|
||||
//* properties definitions
|
||||
const enableDropzone = component.definition.properties.enableDropzone?.value ?? true;
|
||||
const enablePicker = component.definition.properties?.enablePicker?.value ?? true;
|
||||
const maxFileCount = component.definition.properties.maxFileCount?.value ?? 2;
|
||||
const enableMultiple = component.definition.properties.enableMultiple?.value ?? false;
|
||||
const fileType = component.definition.properties.fileType?.value ?? 'image/*';
|
||||
const maxSize = component.definition.properties.maxSize?.value ?? 1048576;
|
||||
const minSize = component.definition.properties.minSize?.value ?? 0;
|
||||
|
||||
const parsedEnableDropzone =
|
||||
typeof enableDropzone !== 'boolean' ? resolveWidgetFieldValue(enableDropzone, currentState) : true;
|
||||
const parsedEnablePicker =
|
||||
typeof enablePicker !== 'boolean' ? resolveWidgetFieldValue(enablePicker, currentState) : true;
|
||||
const parsedMaxFileCount =
|
||||
typeof maxFileCount !== 'number' ? resolveWidgetFieldValue(maxFileCount, currentState) : maxFileCount;
|
||||
const parsedEnableMultiple =
|
||||
typeof enableMultiple !== 'boolean' ? resolveWidgetFieldValue(enableMultiple, currentState) : enableMultiple;
|
||||
const parsedFileType = resolveWidgetFieldValue(fileType, currentState);
|
||||
const parsedMinSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(minSize, currentState) : minSize;
|
||||
const parsedMaxSize = typeof fileType !== 'number' ? resolveWidgetFieldValue(maxSize, currentState) : maxSize;
|
||||
|
||||
//* styles definitions
|
||||
const widgetVisibility = component.definition.styles?.visibility?.value ?? true;
|
||||
const disabledState = component.definition.styles?.disabledState?.value ?? false;
|
||||
|
||||
const parsedDisabledState =
|
||||
typeof disabledState !== 'boolean' ? resolveWidgetFieldValue(disabledState, currentState) : disabledState;
|
||||
const parsedWidgetVisibility =
|
||||
typeof widgetVisibility !== 'boolean' ? resolveWidgetFieldValue(widgetVisibility, currentState) : widgetVisibility;
|
||||
|
||||
const bgThemeColor = darkMode ? '#232E3C' : '#fff';
|
||||
|
||||
const baseStyle = {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '20px',
|
||||
borderWidth: 1.5,
|
||||
borderRadius: 2,
|
||||
borderColor: '#42536A',
|
||||
borderStyle: 'dashed',
|
||||
color: '#bdbdbd',
|
||||
outline: 'none',
|
||||
transition: 'border .24s ease-in-out',
|
||||
display: parsedWidgetVisibility ? 'flex' : 'none',
|
||||
width,
|
||||
height,
|
||||
backgroundColor: !parsedDisabledState && bgThemeColor,
|
||||
};
|
||||
|
||||
const activeStyle = {
|
||||
borderColor: '#2196f3',
|
||||
};
|
||||
|
||||
const acceptStyle = {
|
||||
borderColor: '#00e676',
|
||||
};
|
||||
|
||||
const rejectStyle = {
|
||||
borderColor: '#ff1744',
|
||||
};
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, acceptedFiles, fileRejections } =
|
||||
useDropzone({
|
||||
accept: parsedFileType,
|
||||
noClick: !parsedEnablePicker,
|
||||
noDrag: !parsedEnableDropzone,
|
||||
noKeyboard: true,
|
||||
maxFiles: parsedMaxFileCount,
|
||||
minSize: parsedMinSize,
|
||||
maxSize: parsedMaxSize,
|
||||
multiple: parsedEnableMultiple,
|
||||
disabled: parsedDisabledState,
|
||||
});
|
||||
|
||||
const style = useMemo(
|
||||
() => ({
|
||||
...baseStyle,
|
||||
...(isDragActive && parsedEnableDropzone ? activeStyle : {}),
|
||||
...(isDragAccept && parsedEnableDropzone ? acceptStyle : {}),
|
||||
...(isDragReject && parsedEnableDropzone ? rejectStyle : {}),
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[baseStyle, isDragActive, isDragAccept, acceptStyle, isDragReject]
|
||||
);
|
||||
|
||||
const [accepted, setAccepted] = React.useState(false);
|
||||
const [showSelectdFiles, setShowSelectedFiles] = React.useState(false);
|
||||
const [selectedFiles, setSelectedFiles] = React.useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (acceptedFiles.length === 0) {
|
||||
onComponentOptionChanged(component, 'file', []);
|
||||
}
|
||||
|
||||
if (acceptedFiles.length !== 0) {
|
||||
const fileData = parsedEnableMultiple ? [...selectedFiles] : [];
|
||||
acceptedFiles.map((acceptedFile) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (result) => {
|
||||
//* Resolve both the FileReader result and its original file.
|
||||
resolve([result, acceptedFile]);
|
||||
};
|
||||
//* Reads contents of the file as a text string.
|
||||
reader.readAsText(acceptedFile);
|
||||
}).then((zippedResults) => {
|
||||
//? Run the callback after all files have been read.
|
||||
const fileSelected = {
|
||||
name: zippedResults[1].name,
|
||||
content: zippedResults[0].srcElement.result,
|
||||
type: zippedResults[1].type,
|
||||
};
|
||||
|
||||
fileData.push(fileSelected);
|
||||
});
|
||||
});
|
||||
|
||||
setSelectedFiles(fileData);
|
||||
onComponentOptionChanged(component, 'file', fileData).then(() =>
|
||||
onEvent('onFileSelected', { component }).then(() => {
|
||||
setAccepted(true);
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(() => {
|
||||
setShowSelectedFiles(true);
|
||||
setAccepted(false);
|
||||
resolve();
|
||||
}, 600);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (fileRejections.length > 0) {
|
||||
fileRejections.map((rejectedFile) =>
|
||||
toast.error(rejectedFile.errors[0].message, { hideProgressBar: true, autoClose: 3000 })
|
||||
);
|
||||
}
|
||||
|
||||
return () => {
|
||||
setAccepted(false);
|
||||
setShowSelectedFiles(false);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [acceptedFiles.length, fileRejections.length]);
|
||||
|
||||
const clearSelectedFiles = (index) => {
|
||||
setSelectedFiles((prevState) => {
|
||||
const copy = JSON.parse(JSON.stringify(prevState));
|
||||
copy.splice(index, 1);
|
||||
return copy;
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedFiles.length === 0) {
|
||||
setShowSelectedFiles(false);
|
||||
}
|
||||
onComponentOptionChanged(component, 'file', selectedFiles);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [selectedFiles]);
|
||||
|
||||
return (
|
||||
<section>
|
||||
{showSelectdFiles ? (
|
||||
<FilePicker.AcceptedFiles
|
||||
width={width}
|
||||
height={height}
|
||||
showFilezone={setShowSelectedFiles}
|
||||
bgThemeColor={bgThemeColor}
|
||||
>
|
||||
{selectedFiles.map((acceptedFile, index) => (
|
||||
<>
|
||||
<div key={index} className="col-10">
|
||||
<FilePicker.Signifiers
|
||||
signifier={selectedFiles.length > 0}
|
||||
feedback={acceptedFile.name}
|
||||
cls="text-secondary d-flex justify-content-start file-list"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-2 mt-1">
|
||||
<button
|
||||
className="btn badge bg-azure-lt"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
clearSelectedFiles(index);
|
||||
}}
|
||||
>
|
||||
<img src="/assets/images/icons/trash.svg" width="12" height="12" className="mx-1" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</FilePicker.AcceptedFiles>
|
||||
) : (
|
||||
//* Dropzone
|
||||
<div className="container" {...getRootProps({ style, className: 'dropzone' })}>
|
||||
<input {...getInputProps()} />
|
||||
<FilePicker.Signifiers signifier={accepted} feedback={null} cls="spinner-border text-azure p-0" />
|
||||
<FilePicker.Signifiers
|
||||
signifier={!isDragAccept && !accepted & !isDragReject}
|
||||
feedback={'Drag & drop some files here, or click to select files'}
|
||||
cls={`${darkMode ? 'text-secondary' : 'text-dark'} mt-3`}
|
||||
/>
|
||||
|
||||
<FilePicker.Signifiers
|
||||
signifier={isDragAccept}
|
||||
feedback={'All files will be accepted'}
|
||||
cls="text-lime mt-3"
|
||||
/>
|
||||
|
||||
<FilePicker.Signifiers signifier={isDragReject} feedback={'Files will be rejected!'} cls="text-red mt-3" />
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
FilePicker.Signifiers = ({ signifier, feedback, cls }) => {
|
||||
if (signifier) {
|
||||
return <center>{feedback === null ? <div className={cls}></div> : <p className={cls}>{feedback}</p>}</center>;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
FilePicker.AcceptedFiles = ({ children, width, height, showFilezone, bgThemeColor }) => {
|
||||
const styles = {
|
||||
borderWidth: 1.5,
|
||||
borderRadius: 2,
|
||||
borderColor: '#42536A',
|
||||
borderStyle: 'dashed',
|
||||
color: '#bdbdbd',
|
||||
outline: 'none',
|
||||
padding: '5px',
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'auto',
|
||||
scrollbarWidth: 'none',
|
||||
width,
|
||||
height,
|
||||
backgroundColor: bgThemeColor,
|
||||
};
|
||||
return (
|
||||
<aside style={styles} onClick={() => showFilezone(false)}>
|
||||
<span className="text-info">Files</span>
|
||||
<div className="row accepted-files">{children}</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
|
|
@ -31,7 +31,7 @@ export const Image = function Image({ id, width, height, component, onComponentC
|
|||
style={{ display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<LazyLoad width={width} height={height} placeholder={<Placeholder />} debounce={500}>
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ export const Map = function Map({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
className="map-widget"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export const Multiselect = function Multiselect({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<div className="col-auto my-auto">
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export const NumberInput = function NumberInput({
|
|||
disabled={parsedDisabledState}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setNumber(parseInt(e.target.value));
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export const RadioButton = function RadioButton({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<span className="form-check-label col-auto py-0" style={{ color: textColor }}>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const RichTextEditor = function RichTextEditor({
|
|||
style={{ width: `${width}px`, height: `${height}px`, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<DraftEditor
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export const StarRating = function StarRating({
|
|||
className="star-rating"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
style={{ display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export function Table({
|
|||
onComponentOptionChanged,
|
||||
onComponentOptionsChanged,
|
||||
darkMode,
|
||||
fireEvent,
|
||||
}) {
|
||||
const color = component.definition.styles.textColor.value;
|
||||
const actions = component.definition.properties.actions || { value: [] };
|
||||
|
|
@ -189,7 +190,7 @@ export function Table({
|
|||
[index]: { ...obj },
|
||||
};
|
||||
|
||||
onComponentOptionsChanged(component, [
|
||||
return onComponentOptionsChanged(component, [
|
||||
['dataUpdates', newDataUpdates],
|
||||
['changeSet', newChangeset],
|
||||
]);
|
||||
|
|
@ -510,7 +511,15 @@ export function Table({
|
|||
readOnly={!column.isEditable}
|
||||
activeColor={column.activeColor}
|
||||
onChange={(value) => {
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original);
|
||||
handleCellValueChange(cell.row.index, column.key || column.name, value, cell.row.original).then(
|
||||
() => {
|
||||
fireEvent('OnTableToggleCellChanged', {
|
||||
column: column,
|
||||
rowId: cell.row.id,
|
||||
row: cell.row.original,
|
||||
});
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -767,7 +776,7 @@ export function Table({
|
|||
style={{ width: `${width}px`, height: `${height}px`, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
{/* Show top bar unless search box is disabled and server pagination is enabled */}
|
||||
|
|
@ -950,7 +959,7 @@ export function Table({
|
|||
<div className="table-filters card">
|
||||
<div className="card-header row">
|
||||
<div className="col">
|
||||
<h4 className="text-muted">Filters</h4>
|
||||
<h4 className="font-weight-normal">Filters</h4>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<button onClick={() => hideFilters()} className="btn btn-light btn-sm">
|
||||
|
|
@ -1008,7 +1017,10 @@ export function Table({
|
|||
/>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<button onClick={() => removeFilter(index)} className="btn btn-light btn-sm p-2 text-danger">
|
||||
<button
|
||||
onClick={() => removeFilter(index)}
|
||||
className={`btn ${darkMode ? 'btn-dark' : 'btn-light'} btn-sm p-2 text-danger font-weight-bold`}
|
||||
>
|
||||
x
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -1017,16 +1029,16 @@ export function Table({
|
|||
{filters.length === 0 && (
|
||||
<div>
|
||||
<center>
|
||||
<span className="text-muted">no filters yet.</span>
|
||||
<span>no filters yet.</span>
|
||||
</center>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="card-footer">
|
||||
<button onClick={addFilter} className="btn btn-light btn-sm text-muted">
|
||||
<button onClick={addFilter} className="btn btn-light btn-sm">
|
||||
+ add filter
|
||||
</button>
|
||||
<button onClick={() => clearFilters()} className="btn btn-light btn-sm mx-2 text-muted">
|
||||
<button onClick={() => clearFilters()} className="btn btn-light btn-sm mx-2">
|
||||
clear filters
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export const Text = function Text({ id, width, height, component, onComponentCli
|
|||
style={computedStyles}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
{!loadingState && <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(data) }} />}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export const TextInput = function TextInput({
|
|||
disabled={parsedDisabledState}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setText(e.target.value);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export const ToggleSwitch = ({
|
|||
style={{ width, height, display: parsedWidgetVisibility ? '' : 'none' }}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onComponentClick(id, component);
|
||||
onComponentClick(id, component, event);
|
||||
}}
|
||||
>
|
||||
<span className="form-check-label form-check-label col-auto my-auto" style={{ color: textColor }}>
|
||||
|
|
|
|||
|
|
@ -1001,10 +1001,10 @@ export const componentTypes = [
|
|||
value: `{{ [{"lat": 40.7128, "lng": -73.935242}] }}`,
|
||||
},
|
||||
canSearch: {
|
||||
value: `{{true}}` ,
|
||||
value: `{{true}}`,
|
||||
},
|
||||
addNewMarkers: { value: `{{true}}` },
|
||||
},
|
||||
addNewMarkers: { value: '{{false}}' },
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: { value: '{{true}}' },
|
||||
|
|
@ -1133,8 +1133,140 @@ export const componentTypes = [
|
|||
properties: {},
|
||||
events: [],
|
||||
styles: {
|
||||
dividerColor: { value: '#E7E8EA' },
|
||||
visibility: { value: '{{true}}' },
|
||||
dividerColor: { value: '#E7E8EA' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FilePicker',
|
||||
displayName: 'File Picker',
|
||||
description: 'File Picker',
|
||||
component: 'FilePicker',
|
||||
defaultSize: {
|
||||
width: 270,
|
||||
height: 100,
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
|
||||
},
|
||||
properties: {
|
||||
enableDropzone: { type: 'code', displayName: 'Use Drop zone' },
|
||||
enablePicker: { type: 'code', displayName: 'Use File Picker' },
|
||||
enableMultiple: { type: 'code', displayName: 'Pick mulitple files' },
|
||||
maxFileCount: { type: 'code', displayName: 'Max file count' },
|
||||
fileType: { type: 'code', displayName: 'Accept file types' },
|
||||
maxSize: { type: 'code', displayName: 'Max size limit (Bytes)' },
|
||||
minSize: { type: 'code', displayName: 'Min size limit (Bytes)' },
|
||||
},
|
||||
events: { onFileSelected: { displayName: 'On File Selected' } },
|
||||
styles: {
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
disabledState: { type: 'code', displayName: 'Disable' },
|
||||
},
|
||||
exposedVariables: {
|
||||
file: [{ name: [], content: [], type: [] }],
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value: false },
|
||||
},
|
||||
properties: {
|
||||
enableDropzone: { value: '{{true}}' },
|
||||
enablePicker: { value: '{{true}}' },
|
||||
maxFileCount: { value: '{{2}}' },
|
||||
enableMultiple: { value: '{{false}}' },
|
||||
fileType: { value: '{{"image/*"}}' },
|
||||
maxSize: { value: '{{1048576}}' },
|
||||
minSize: { value: '{{50}}' },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: { value: '{{true}}' },
|
||||
disabledState: { value: '{{false}}' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Calendar',
|
||||
displayName: 'Calendar',
|
||||
description: 'Calendar',
|
||||
component: 'Calendar',
|
||||
defaultSize: {
|
||||
width: 700,
|
||||
height: 600,
|
||||
},
|
||||
others: {
|
||||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
|
||||
},
|
||||
properties: {
|
||||
dateFormat: { type: 'code', displayName: 'Date format' },
|
||||
defaultDate: { type: 'code', displayName: 'Default date' },
|
||||
events: { type: 'code', displayName: 'Events' },
|
||||
resources: { type: 'code', displayName: 'Resources' },
|
||||
defaultView: { type: 'code', displayName: 'Default view' },
|
||||
displayToolbar: { type: 'toggle', displayName: 'Show toolbar' },
|
||||
displayViewSwitcher: { type: 'toggle', displayName: 'Show view switcher' },
|
||||
highlightToday: { type: 'toggle', displayName: 'Highlight today' },
|
||||
},
|
||||
events: {
|
||||
onCalendarEventSelect: { displayName: 'On Event Select' },
|
||||
onCalendarSlotSelect: { displayName: 'On Slot Select' },
|
||||
},
|
||||
styles: {
|
||||
visibility: { type: 'code', displayName: 'Visibility' },
|
||||
cellSizeInViewsClassifiedByResource: {
|
||||
type: 'select',
|
||||
displayName: 'Cell size in views classified by resource',
|
||||
options: [
|
||||
{ name: 'Compact', value: 'compact' },
|
||||
{ name: 'Spacious', value: 'spacious' },
|
||||
],
|
||||
},
|
||||
},
|
||||
exposedVariables: {
|
||||
selectedEvent: {},
|
||||
selectedSlots: {},
|
||||
},
|
||||
definition: {
|
||||
others: {
|
||||
showOnDesktop: { value: true },
|
||||
showOnMobile: { value: false },
|
||||
},
|
||||
properties: {
|
||||
dateFormat: {
|
||||
value: 'MM-DD-YYYY HH:mm:ss A Z',
|
||||
},
|
||||
defaultDate: {
|
||||
value: '{{moment().format("MM-DD-YYYY HH:mm:ss A Z")}}',
|
||||
},
|
||||
events: {
|
||||
value:
|
||||
"{{[\n\t\t{\n\t\t\t title: 'Sample event',\n\t\t\t start: `${moment().startOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,\n\t\t\t end: `${moment().endOf('day').format('MM-DD-YYYY HH:mm:ss A Z')}`,\n\t\t\t allDay: false,\n\t\t\t color: '#4D72DA'\n\t\t}\n]}}",
|
||||
},
|
||||
resources: {
|
||||
value: '{{[]}}',
|
||||
},
|
||||
defaultView: {
|
||||
value: "{{'month'}}",
|
||||
},
|
||||
displayToolbar: {
|
||||
value: true,
|
||||
},
|
||||
displayViewSwitcher: {
|
||||
value: true,
|
||||
},
|
||||
highlightToday: {
|
||||
value: true,
|
||||
},
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: { value: '{{true}}' },
|
||||
cellSizeInViewsClassifiedByResource: { value: 'spacious' },
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { useDrop, useDragLayer } from 'react-dnd';
|
||||
import { ItemTypes } from './ItemTypes';
|
||||
import { DraggableBox } from './DraggableBox';
|
||||
|
|
@ -6,6 +7,11 @@ import { snapToGrid as doSnapToGrid } from './snapToGrid';
|
|||
import update from 'immutability-helper';
|
||||
import { componentTypes } from './Components/components';
|
||||
import { computeComponentName } from '@/_helpers/utils';
|
||||
import useRouter from '@/_hooks/use-router';
|
||||
import Comments from './Comments';
|
||||
import { commentsService } from '@/_services';
|
||||
import config from 'config';
|
||||
import Spinner from '@/_ui/Spinner';
|
||||
|
||||
function uuidv4() {
|
||||
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
|
||||
|
|
@ -32,6 +38,9 @@ export const Container = ({
|
|||
scaleValue,
|
||||
selectedComponent,
|
||||
darkMode,
|
||||
showComments,
|
||||
appVersionsId,
|
||||
socket,
|
||||
}) => {
|
||||
const styles = {
|
||||
width: currentLayout === 'mobile' ? deviceWindowWidth : 1292,
|
||||
|
|
@ -44,6 +53,9 @@ export const Container = ({
|
|||
const [boxes, setBoxes] = useState(components);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [isResizing, setIsResizing] = useState(false);
|
||||
const [commentsPreviewList, setCommentsPreviewList] = useState([]);
|
||||
const [newThread, addNewThread] = useState({});
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
setBoxes(components);
|
||||
|
|
@ -87,12 +99,27 @@ export const Container = ({
|
|||
|
||||
const [, drop] = useDrop(
|
||||
() => ({
|
||||
accept: ItemTypes.BOX,
|
||||
drop(item, monitor) {
|
||||
accept: [ItemTypes.BOX, ItemTypes.COMMENT],
|
||||
async drop(item, monitor) {
|
||||
if (item.parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.name === 'comment') {
|
||||
const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
|
||||
const offsetFromTopOfWindow = canvasBoundingRect.top;
|
||||
const offsetFromLeftOfWindow = canvasBoundingRect.left;
|
||||
const currentOffset = monitor.getSourceClientOffset();
|
||||
|
||||
const x = Math.round(currentOffset.x + currentOffset.x * (1 - zoomLevel) - offsetFromLeftOfWindow);
|
||||
const y = Math.round(currentOffset.y + currentOffset.y * (1 - zoomLevel) - offsetFromTopOfWindow);
|
||||
|
||||
const element = document.getElementById(`thread-${item.threadId}`);
|
||||
element.style.transform = `translate(${x}px, ${y}px)`;
|
||||
commentsService.updateThread(item.threadId, { x, y });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let layouts = item['layouts'];
|
||||
const currentLayoutOptions = layouts ? layouts[item.currentLayout] : {};
|
||||
|
||||
|
|
@ -252,8 +279,124 @@ export const Container = ({
|
|||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log('current component => ', selectedComponent);
|
||||
}, [selectedComponent]);
|
||||
|
||||
const handleAddThread = async (e) => {
|
||||
e.stopPropogation && e.stopPropogation();
|
||||
const elementIndex = commentsPreviewList.length;
|
||||
setCommentsPreviewList([
|
||||
...commentsPreviewList,
|
||||
{
|
||||
x: e.nativeEvent.offsetX,
|
||||
y: e.nativeEvent.offsetY,
|
||||
},
|
||||
]);
|
||||
const { data } = await commentsService.createThread({
|
||||
appId: router.query.id,
|
||||
x: e.nativeEvent.offsetX,
|
||||
y: e.nativeEvent.offsetY,
|
||||
appVersionsId,
|
||||
});
|
||||
|
||||
// Remove the temporary loader preview
|
||||
const _commentsPreviewList = [...commentsPreviewList];
|
||||
_commentsPreviewList.splice(elementIndex, 1);
|
||||
setCommentsPreviewList(_commentsPreviewList);
|
||||
|
||||
// Update the threads on all connected clients using websocket
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'threads', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
|
||||
// Update the list of threads on the current users page
|
||||
addNewThread(data);
|
||||
};
|
||||
|
||||
const handleAddThreadOnComponent = async (_, __, e) => {
|
||||
e.stopPropogation && e.stopPropogation();
|
||||
|
||||
const canvasBoundingRect = document.getElementsByClassName('real-canvas')[0].getBoundingClientRect();
|
||||
const offsetFromTopOfWindow = canvasBoundingRect.top;
|
||||
const offsetFromLeftOfWindow = canvasBoundingRect.left;
|
||||
|
||||
const x = Math.round(e.screenX + e.screenX * (1 - zoomLevel) - offsetFromLeftOfWindow);
|
||||
const y = Math.round(e.screenY + e.screenY * (1 - zoomLevel) - offsetFromTopOfWindow);
|
||||
|
||||
const elementIndex = commentsPreviewList.length;
|
||||
setCommentsPreviewList([
|
||||
...commentsPreviewList,
|
||||
{
|
||||
x: e.nativeEvent.offsetX,
|
||||
y: e.nativeEvent.offsetY - 130,
|
||||
},
|
||||
]);
|
||||
const { data } = await commentsService.createThread({
|
||||
appId: router.query.id,
|
||||
x,
|
||||
y: y - 130,
|
||||
appVersionsId,
|
||||
});
|
||||
|
||||
// Remove the temporary loader preview
|
||||
const _commentsPreviewList = [...commentsPreviewList];
|
||||
_commentsPreviewList.splice(elementIndex, 1);
|
||||
setCommentsPreviewList(_commentsPreviewList);
|
||||
|
||||
// Update the threads on all connected clients using websocket
|
||||
socket.send(
|
||||
JSON.stringify({
|
||||
event: 'events',
|
||||
data: { message: 'threads', appId: router.query.id },
|
||||
})
|
||||
);
|
||||
|
||||
// Update the list of threads on the current users page
|
||||
addNewThread(data);
|
||||
};
|
||||
|
||||
if (showComments) {
|
||||
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
|
||||
const currentUserInitials = `${currentUser.first_name?.charAt(0)}${currentUser.last_name?.charAt(0)}`;
|
||||
styles.cursor = `url("data:image/svg+xml,%3Csvg width='34' height='34' viewBox='0 0 34 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='17' cy='17' r='15.25' fill='white' stroke='%23FCAA0D' stroke-width='2.5' opacity='0.5' /%3E%3Ctext x='10' y='20' fill='%23000' opacity='0.5' font-family='inherit' font-size='11.2' font-weight='500' color='%23656d77'%3E%3C/text%3E%3C/svg%3E%0A"), text`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={drop} style={styles} className={`real-canvas ${isDragging || isResizing ? 'show-grid' : ''}`}>
|
||||
<div
|
||||
{...(config.COMMENT_FEATURE_ENABLE && showComments && { onClick: handleAddThread })}
|
||||
ref={drop}
|
||||
style={styles}
|
||||
className={cx('real-canvas', {
|
||||
'show-grid': isDragging || isResizing,
|
||||
})}
|
||||
>
|
||||
{config.COMMENT_FEATURE_ENABLE && showComments && (
|
||||
<>
|
||||
<Comments socket={socket} newThread={newThread} appVersionsId={appVersionsId} />
|
||||
{commentsPreviewList.map((previewComment, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
transform: `translate(${previewComment.x}px, ${previewComment.y}px)`,
|
||||
}}
|
||||
>
|
||||
<label className="form-selectgroup-item comment-preview-bubble">
|
||||
<span
|
||||
className={cx(
|
||||
'comment comment-preview-bubble-border cursor-move avatar avatar-sm shadow-lg bg-white avatar-rounded'
|
||||
)}
|
||||
>
|
||||
<Spinner />
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{Object.keys(boxes).map((key) => {
|
||||
const box = boxes[key];
|
||||
const canShowInCurrentLayout =
|
||||
|
|
@ -262,7 +405,9 @@ export const Container = ({
|
|||
if (!box.parent && canShowInCurrentLayout) {
|
||||
return (
|
||||
<DraggableBox
|
||||
onComponentClick={onComponentClick}
|
||||
onComponentClick={
|
||||
config.COMMENT_FEATURE_ENABLE && showComments ? handleAddThreadOnComponent : onComponentClick
|
||||
}
|
||||
onEvent={onEvent}
|
||||
onComponentOptionChanged={onComponentOptionChanged}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ export const CustomDragLayer = ({ currentLayout }) => {
|
|||
isDragging: monitor.isDragging(),
|
||||
delta: monitor.getDifferenceFromInitialOffset(),
|
||||
}));
|
||||
|
||||
if (itemType === ItemTypes.COMMENT) return null;
|
||||
function renderItem() {
|
||||
switch (itemType) {
|
||||
case ItemTypes.BOX:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,13 @@ import Button from 'react-bootstrap/Button';
|
|||
import { toast } from 'react-toastify';
|
||||
import { defaultOptions } from './DefaultOptions';
|
||||
import { TestConnection } from './TestConnection';
|
||||
import { DataBaseSources, ApiSources, DataSourceTypes, SourceComponents } from './SourceComponents';
|
||||
import {
|
||||
DataBaseSources,
|
||||
ApiSources,
|
||||
DataSourceTypes,
|
||||
SourceComponents,
|
||||
CloudStorageSources,
|
||||
} from './SourceComponents';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import config from 'config';
|
||||
|
||||
|
|
@ -73,6 +79,7 @@ class DataSourceManager extends React.Component {
|
|||
dataSourceMeta: {},
|
||||
selectedDataSource: null,
|
||||
options: {},
|
||||
connectionTestError: null,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -110,23 +117,29 @@ class DataSourceManager extends React.Component {
|
|||
encrypted: keyMeta ? keyMeta.encrypted : false,
|
||||
};
|
||||
});
|
||||
|
||||
if (selectedDataSource.id) {
|
||||
this.setState({ isSaving: true });
|
||||
datasourceService.save(selectedDataSource.id, appId, name, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Saved', { hideProgressBar: true, position: 'top-center' });
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
if (name.trim() !== ''){
|
||||
if (selectedDataSource.id) {
|
||||
this.setState({ isSaving: true });
|
||||
datasourceService.save(selectedDataSource.id, appId, name, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Saved', { hideProgressBar: true, position: 'top-center' });
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
} else {
|
||||
this.setState({ isSaving: true });
|
||||
datasourceService.create(appId, name, kind, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Added', { hideProgressBar: true, position: 'top-center' });
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.setState({ isSaving: true });
|
||||
datasourceService.create(appId, name, kind, parsedOptions).then(() => {
|
||||
this.setState({ isSaving: false });
|
||||
this.hideModal();
|
||||
toast.success('Datasource Added', { hideProgressBar: true, position: 'top-center' });
|
||||
this.props.dataSourcesChanged();
|
||||
});
|
||||
toast.error(
|
||||
"The name of datasource should not be empty",
|
||||
{ hideProgressBar: true, position: 'top-center' }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -228,7 +241,30 @@ class DataSourceManager extends React.Component {
|
|||
<h4 className="mb-2">APIS</h4>
|
||||
{ApiSources.map((dataSource) => (
|
||||
<div className="col-md-2" key={dataSource.name}>
|
||||
<div className="card" role="button" onClick={() => this.selectDataSource(dataSource)}>
|
||||
<div className="card mb-3" role="button" onClick={() => this.selectDataSource(dataSource)}>
|
||||
<div className="card-body">
|
||||
<center>
|
||||
<img
|
||||
src={`/assets/images/icons/editor/datasources/${dataSource.kind.toLowerCase()}.svg`}
|
||||
width="50"
|
||||
height="50"
|
||||
alt=""
|
||||
/>
|
||||
|
||||
<br></br>
|
||||
<br></br>
|
||||
{dataSource.name}
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="row row-deck mt-2">
|
||||
<h4 className="mb-2">CLOUD STORAGES</h4>
|
||||
{CloudStorageSources.map((dataSource) => (
|
||||
<div className="col-md-2" key={dataSource.name}>
|
||||
<div className="card mb-3" role="button" onClick={() => this.selectDataSource(dataSource)}>
|
||||
<div className="card-body">
|
||||
<center>
|
||||
<img
|
||||
|
|
@ -322,13 +358,21 @@ class DataSourceManager extends React.Component {
|
|||
|
||||
{!dataSourceMeta?.hideSave && selectedDataSource && dataSourceMeta.customTesting && (
|
||||
<Modal.Footer>
|
||||
<div className="row mt-3">
|
||||
<div className="col"></div>
|
||||
<div className="col-auto">
|
||||
<Button className="m-2" disabled={isSaving} variant="primary" onClick={this.createDataSource}>
|
||||
{isSaving ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col">
|
||||
<small>
|
||||
<a
|
||||
href={`https://docs.tooljet.io/docs/data-sources/${selectedDataSource.kind}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Read documentation
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
<Button className="m-2" disabled={isSaving} variant="primary" onClick={this.createDataSource}>
|
||||
{isSaving ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Footer>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -85,4 +85,12 @@ export const defaultOptions = {
|
|||
access_key: { value: '' },
|
||||
secret_key: { value: '' },
|
||||
},
|
||||
s3: {
|
||||
access_key: { value: '' },
|
||||
secret_key: { value: '' },
|
||||
region: { value: '' },
|
||||
},
|
||||
gcs: {
|
||||
private_key: { value: '' },
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/",
|
||||
"$id": "https://tooljet.io/Gcs.schema.json",
|
||||
"title": "Google Cloud Storage datasource",
|
||||
"description": "A schema defining GCS datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "GCS",
|
||||
"kind": "gcs",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"private_key": { "type": "string", "encrypted": true }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"private_key": {
|
||||
"$label": "Private key",
|
||||
"$key": "private_key",
|
||||
"type": "textarea",
|
||||
"description": "Enter JSON private key for service account"
|
||||
}
|
||||
},
|
||||
"required": ["private_key"]
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/",
|
||||
"$id": "https://tooljet.io/S3.schema.json",
|
||||
"title": "AWS S3 datasource",
|
||||
"description": "A schema defining AWS S3 datasource",
|
||||
"type": "object",
|
||||
"source": {
|
||||
"name": "AWS S3",
|
||||
"kind": "s3",
|
||||
"exposedVariables": {
|
||||
"isLoading": {},
|
||||
"data": {},
|
||||
"rawData": {}
|
||||
},
|
||||
"options": {
|
||||
"access_key": { "type": "string" },
|
||||
"secret_key": { "type": "string", "encrypted": true },
|
||||
"region": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"access_key": {
|
||||
"$label": "Access key",
|
||||
"$key": "access_key",
|
||||
"type": "text",
|
||||
"description": "Enter access key"
|
||||
},
|
||||
"secret_key": {
|
||||
"$label": "Secret key",
|
||||
"$key": "secret_key",
|
||||
"type": "password",
|
||||
"description": "Enter secret key"
|
||||
},
|
||||
"region": {
|
||||
"$label": "Region",
|
||||
"$key": "region",
|
||||
"type": "dropdown",
|
||||
"description": "Single select dropdown for region",
|
||||
"$options": [
|
||||
{ "name": "US East (Ohio)", "value": "us-east-2" },
|
||||
{ "name": "US East (N. Virginia)", "value": "us-east-1" },
|
||||
{ "name": "US West (N. California)", "value": "us-west-1" },
|
||||
{ "name": "US West (Oregon)", "value": "us-west-2" },
|
||||
{ "name": "Africa (Cape Town)", "value": "af-south-1" },
|
||||
{ "name": "Asia Pacific (Hong Kong)", "value": "ap-east-1" },
|
||||
{ "name": "Asia Pacific (Mumbai)", "value": "ap-south-1" },
|
||||
{ "name": "Asia Pacific (Osaka)", "value": "ap-northeast-3" },
|
||||
{ "name": "Asia Pacific (Seoul)", "value": "ap-northeast-2" },
|
||||
{ "name": "Asia Pacific (Singapore)", "value": "ap-southeast-1" },
|
||||
{ "name": "Asia Pacific (Sydney)", "value": "ap-southeast-2" },
|
||||
{ "name": "Asia Pacific (Tokyo)", "value": "ap-northeast-1" },
|
||||
{ "name": "Canada (Central)", "value": "ca-central-1" },
|
||||
{ "name": "China (Beijing)", "value": "cn-north-1" },
|
||||
{ "name": "China (Ningxia)", "value": "cn-northwest-1" },
|
||||
{ "name": "Europe (Frankfurt)", "value": "eu-central-1" },
|
||||
{ "name": "Europe (Ireland)", "value": "eu-west-1" },
|
||||
{ "name": "Europe (London)", "value": "eu-west-2" },
|
||||
{ "name": "Europe (Milan)", "value": "eu-south-1" },
|
||||
{ "name": "Europe (Paris)", "value": "eu-west-3" },
|
||||
{ "name": "Europe (Stockholm)", "value": "eu-north-1" },
|
||||
{ "name": "Middle East (Bahrain)", "value": "me-south-1" },
|
||||
{ "name": "South America (São Paulo)", "value": "sa-east-1" },
|
||||
{ "name": "AWS GovCloud (US-East)", "value": "us-gov-east-1" },
|
||||
{ "name": "AWS GovCloud (US-West)", "value": "us-gov-west-1" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["access_key", "secret_key", "region"]
|
||||
}
|
||||