Full Code of plankanban/planka for AI

master b7d3602dcd86 cached
1523 files
6.6 MB
1.8M tokens
683 symbols
1 requests
Download .txt
Showing preview only (7,186K chars total). Download the full file or copy to clipboard to get everything.
Repository: plankanban/planka
Branch: master
Commit: b7d3602dcd86
Files: 1523
Total size: 6.6 MB

Directory structure:
gitextract_7ra8i_oc/

├── .dockerignore
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-bug-report.yml
│   │   └── 2-feature-request.yml
│   └── workflows/
│       ├── build-and-publish-release-package.yml
│       ├── build-and-push-docker-image.yml
│       ├── build-and-push-docker-nightly-image.yml
│       ├── build-and-test.yml
│       ├── lint.yml
│       └── release-helm-chart.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CONTRIBUTOR_LICENSE_AGREEMENT.md
├── Dockerfile
├── Dockerfile.dev
├── LICENSE.md
├── LICENSES/
│   ├── PLANKA Commercial License DE.md
│   ├── PLANKA Commercial License EN.md
│   ├── PLANKA Community License DE.md
│   ├── PLANKA Community License EN.md
│   ├── PLANKA License Guide DE.md
│   └── PLANKA License Guide EN.md
├── README.md
├── SECURITY.md
├── charts/
│   └── planka/
│       ├── .helmignore
│       ├── Chart.yaml
│       ├── README.md
│       ├── templates/
│       │   ├── NOTES.txt
│       │   ├── _helpers.tpl
│       │   ├── configmap-terms.yaml
│       │   ├── deployment.yaml
│       │   ├── hpa.yaml
│       │   ├── ingress.yaml
│       │   ├── pvc.yaml
│       │   ├── secret-oidc.yaml
│       │   ├── service.yaml
│       │   ├── serviceaccount.yaml
│       │   └── tests/
│       │       └── test-connection.yaml
│       └── values.yaml
├── client/
│   ├── .gitignore
│   ├── index.html
│   ├── package.json
│   ├── patches/
│   │   ├── @diplodoc+transform+4.70.2.patch
│   │   ├── @gravity-ui+markdown-editor+15.35.1.patch
│   │   ├── react-mentions+4.4.10.patch
│   │   ├── redux-orm+0.16.2.patch
│   │   ├── sails.io.js+1.2.1.patch
│   │   └── semantic-ui-react+2.1.5.patch
│   ├── public/
│   │   ├── manifest.json
│   │   └── robots.txt
│   ├── src/
│   │   ├── actions/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── login.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── router.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── api/
│   │   │   ├── access-tokens.js
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── card-labels.js
│   │   │   ├── card-memberships.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── http.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── terms.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── assets/
│   │   │   └── docs/
│   │   │       └── whats-new.md
│   │   ├── components/
│   │   │   ├── activities/
│   │   │   │   ├── BoardActivitiesModal/
│   │   │   │   │   ├── BoardActivitiesModal.jsx
│   │   │   │   │   ├── BoardActivitiesModal.module.scss
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── CardActivities/
│   │   │   │       ├── CardActivities.jsx
│   │   │   │       ├── CardActivities.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── attachments/
│   │   │   │   ├── AddAttachmentStep/
│   │   │   │   │   ├── AddAttachmentStep.jsx
│   │   │   │   │   ├── AddAttachmentStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── Attachments/
│   │   │   │       ├── Attachments.jsx
│   │   │   │       ├── Attachments.module.scss
│   │   │   │       ├── ContentViewer.jsx
│   │   │   │       ├── ContentViewer.module.scss
│   │   │   │       ├── CsvViewer.jsx
│   │   │   │       ├── CsvViewer.module.scss
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── ItemContent.jsx
│   │   │   │       ├── ItemContent.module.scss
│   │   │   │       ├── PdfViewer.jsx
│   │   │   │       ├── PdfViewer.module.scss
│   │   │   │       └── index.js
│   │   │   ├── base-custom-field-groups/
│   │   │   │   ├── AddBaseCustomFieldGroupStep/
│   │   │   │   │   ├── AddBaseCustomFieldGroupStep.jsx
│   │   │   │   │   ├── AddBaseCustomFieldGroupStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── BaseCustomFieldGroupChip/
│   │   │   │   │   ├── BaseCustomFieldGroupChip.jsx
│   │   │   │   │   ├── BaseCustomFieldGroupChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── BaseCustomFieldGroupStep/
│   │   │   │       ├── BaseCustomFieldGroupStep.jsx
│   │   │   │       ├── BaseCustomFieldGroupStep.module.scss
│   │   │   │       ├── CustomField.jsx
│   │   │   │       ├── CustomField.module.scss
│   │   │   │       ├── CustomFieldAddStep.jsx
│   │   │   │       ├── CustomFieldAddStep.module.scss
│   │   │   │       ├── CustomFieldEditStep.jsx
│   │   │   │       ├── CustomFieldEditStep.module.scss
│   │   │   │       ├── CustomFieldEditor.jsx
│   │   │   │       ├── CustomFieldEditor.module.scss
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── board-memberships/
│   │   │   │   ├── BoardMemberships/
│   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   ├── AddStep/
│   │   │   │   │   │   ├── AddStep.jsx
│   │   │   │   │   │   ├── AddStep.module.scss
│   │   │   │   │   │   ├── User.jsx
│   │   │   │   │   │   ├── User.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── BoardMemberships.jsx
│   │   │   │   │   ├── BoardMemberships.module.scss
│   │   │   │   │   ├── Group.jsx
│   │   │   │   │   ├── Group.module.scss
│   │   │   │   │   ├── GroupItemsStep.jsx
│   │   │   │   │   ├── SelectPermissionsStep.jsx
│   │   │   │   │   ├── SelectPermissionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardMembershipsStep.jsx
│   │   │   │   └── PureBoardMembershipsStep/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── PureBoardMembershipsStep.jsx
│   │   │   │       ├── PureBoardMembershipsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── boards/
│   │   │   │   ├── AddBoardStep/
│   │   │   │   │   ├── AddBoardStep.jsx
│   │   │   │   │   ├── AddBoardStep.module.scss
│   │   │   │   │   ├── ImportStep.jsx
│   │   │   │   │   ├── ImportStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Board/
│   │   │   │   │   ├── Board.jsx
│   │   │   │   │   ├── EndlessContent.jsx
│   │   │   │   │   ├── FiniteContent.jsx
│   │   │   │   │   ├── GridView.jsx
│   │   │   │   │   ├── GridView.module.scss
│   │   │   │   │   ├── KanbanContent/
│   │   │   │   │   │   ├── AddList.jsx
│   │   │   │   │   │   ├── AddList.module.scss
│   │   │   │   │   │   ├── KanbanContent.jsx
│   │   │   │   │   │   ├── KanbanContent.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ListView.jsx
│   │   │   │   │   ├── ListView.module.scss
│   │   │   │   │   ├── ShortcutsProvider.jsx
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardActions/
│   │   │   │   │   ├── BoardActions.jsx
│   │   │   │   │   ├── BoardActions.module.scss
│   │   │   │   │   ├── Filters.jsx
│   │   │   │   │   ├── Filters.module.scss
│   │   │   │   │   ├── RightSide/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── RightSide.jsx
│   │   │   │   │   │   ├── RightSide.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardSettingsModal/
│   │   │   │   │   ├── BoardSettingsModal.jsx
│   │   │   │   │   ├── GeneralPane/
│   │   │   │   │   │   ├── EditInformation.jsx
│   │   │   │   │   │   ├── EditInformation.module.scss
│   │   │   │   │   │   ├── GeneralPane.jsx
│   │   │   │   │   │   ├── GeneralPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── NotificationsPane.jsx
│   │   │   │   │   ├── NotificationsPane.module.scss
│   │   │   │   │   ├── PreferencesPane/
│   │   │   │   │   │   ├── DefaultCardType.jsx
│   │   │   │   │   │   ├── DefaultCardType.module.scss
│   │   │   │   │   │   ├── DefaultView.jsx
│   │   │   │   │   │   ├── DefaultView.module.scss
│   │   │   │   │   │   ├── Others.jsx
│   │   │   │   │   │   ├── Others.module.scss
│   │   │   │   │   │   ├── PreferencesPane.jsx
│   │   │   │   │   │   ├── PreferencesPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   └── Boards/
│   │   │   │       ├── Boards.jsx
│   │   │   │       ├── Boards.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── cards/
│   │   │   │   ├── AddCard/
│   │   │   │   │   ├── AddCard.jsx
│   │   │   │   │   ├── AddCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ArchiveCardsStep.jsx
│   │   │   │   ├── Card/
│   │   │   │   │   ├── Card.jsx
│   │   │   │   │   ├── Card.module.scss
│   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   ├── InlineContent.jsx
│   │   │   │   │   ├── InlineContent.module.scss
│   │   │   │   │   ├── ProjectContent.jsx
│   │   │   │   │   ├── ProjectContent.module.scss
│   │   │   │   │   ├── StoryContent.jsx
│   │   │   │   │   ├── StoryContent.module.scss
│   │   │   │   │   ├── TaskList/
│   │   │   │   │   │   ├── Task.jsx
│   │   │   │   │   │   ├── Task.module.scss
│   │   │   │   │   │   ├── TaskList.jsx
│   │   │   │   │   │   ├── TaskList.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── CardActionsStep/
│   │   │   │   │   ├── CardActionsStep.jsx
│   │   │   │   │   ├── CardActionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CardModal/
│   │   │   │   │   ├── AddAttachmentZone/
│   │   │   │   │   │   ├── AddAttachmentZone.jsx
│   │   │   │   │   │   ├── AddAttachmentZone.module.scss
│   │   │   │   │   │   ├── AddTextFileModal.jsx
│   │   │   │   │   │   ├── AddTextFileModal.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── CardModal.jsx
│   │   │   │   │   ├── CardModal.module.scss
│   │   │   │   │   ├── Communication.jsx
│   │   │   │   │   ├── Communication.module.scss
│   │   │   │   │   ├── CreationDetailsStep.jsx
│   │   │   │   │   ├── CreationDetailsStep.module.scss
│   │   │   │   │   ├── CustomFieldGroups/
│   │   │   │   │   │   ├── CustomFieldGroups.jsx
│   │   │   │   │   │   ├── CustomFieldGroups.module.scss
│   │   │   │   │   │   ├── DraggableItem.jsx
│   │   │   │   │   │   ├── DraggableItem.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── MoreActionsStep.jsx
│   │   │   │   │   ├── MoreActionsStep.module.scss
│   │   │   │   │   ├── NameField.jsx
│   │   │   │   │   ├── NameField.module.scss
│   │   │   │   │   ├── ProjectContent.jsx
│   │   │   │   │   ├── ProjectContent.module.scss
│   │   │   │   │   ├── StoryContent.jsx
│   │   │   │   │   ├── StoryContent.module.scss
│   │   │   │   │   ├── TaskLists/
│   │   │   │   │   │   ├── EditStep.jsx
│   │   │   │   │   │   ├── EditStep.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   ├── TaskLists.jsx
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Thumbnail.jsx
│   │   │   │   │   ├── Thumbnail.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── DraggableCard/
│   │   │   │   │   ├── DraggableCard.jsx
│   │   │   │   │   ├── DraggableCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── DueDateChip/
│   │   │   │   │   ├── DueDateChip.jsx
│   │   │   │   │   ├── DueDateChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditDueDateStep/
│   │   │   │   │   ├── EditDueDateStep.jsx
│   │   │   │   │   ├── EditDueDateStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditStopwatchStep/
│   │   │   │   │   ├── EditStopwatchStep.jsx
│   │   │   │   │   ├── EditStopwatchStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── MoveCardStep/
│   │   │   │   │   ├── MoveCardStep.jsx
│   │   │   │   │   ├── MoveCardStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── SelectCardType/
│   │   │   │   │   ├── SelectCardType.jsx
│   │   │   │   │   ├── SelectCardType.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── SelectCardTypeStep.jsx
│   │   │   │   └── StopwatchChip/
│   │   │   │       ├── StopwatchChip.jsx
│   │   │   │       ├── StopwatchChip.module.scss
│   │   │   │       └── index.js
│   │   │   ├── comments/
│   │   │   │   └── Comments/
│   │   │   │       ├── Add.jsx
│   │   │   │       ├── Add.module.scss
│   │   │   │       ├── Comments.jsx
│   │   │   │       ├── Comments.module.scss
│   │   │   │       ├── Edit.jsx
│   │   │   │       ├── Edit.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── common/
│   │   │   │   ├── AboutModal/
│   │   │   │   │   ├── AboutModal.jsx
│   │   │   │   │   ├── AboutModal.module.scss
│   │   │   │   │   ├── AboutPane.jsx
│   │   │   │   │   ├── AboutPane.module.scss
│   │   │   │   │   ├── TermsPane.jsx
│   │   │   │   │   ├── TermsPane.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── AdministrationModal/
│   │   │   │   │   ├── AdministrationModal.jsx
│   │   │   │   │   ├── AdministrationModal.module.scss
│   │   │   │   │   ├── SmtpPane.jsx
│   │   │   │   │   ├── SmtpPane.module.scss
│   │   │   │   │   ├── UsersPane/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── AddStep.jsx
│   │   │   │   │   │   ├── AddStep.module.scss
│   │   │   │   │   │   ├── ApiKeyStep.jsx
│   │   │   │   │   │   ├── ApiKeyStep.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   ├── SelectRoleStep.jsx
│   │   │   │   │   │   ├── SelectRoleStep.module.scss
│   │   │   │   │   │   ├── UsersPane.jsx
│   │   │   │   │   │   ├── UsersPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── WebhooksPane.jsx
│   │   │   │   │   ├── WebhooksPane.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ConfirmationStep/
│   │   │   │   │   ├── ConfirmationStep.jsx
│   │   │   │   │   ├── ConfirmationStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Core/
│   │   │   │   │   ├── Core.jsx
│   │   │   │   │   ├── Message.jsx
│   │   │   │   │   ├── Message.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditMarkdown/
│   │   │   │   │   ├── EditMarkdown.jsx
│   │   │   │   │   ├── EditMarkdown.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ExpandableMarkdown/
│   │   │   │   │   ├── ExpandableMarkdown.jsx
│   │   │   │   │   ├── ExpandableMarkdown.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Favicon/
│   │   │   │   │   ├── Favicon.jsx
│   │   │   │   │   ├── Favicon.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Favorites/
│   │   │   │   │   ├── Favorites.jsx
│   │   │   │   │   ├── Favorites.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Fixed/
│   │   │   │   │   ├── Fixed.jsx
│   │   │   │   │   ├── Fixed.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── GhostError/
│   │   │   │   │   ├── GhostError.jsx
│   │   │   │   │   ├── GhostError.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Header/
│   │   │   │   │   ├── Header.jsx
│   │   │   │   │   ├── Header.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Home/
│   │   │   │   │   ├── GridProjectsView.jsx
│   │   │   │   │   ├── GroupedProjectsView.jsx
│   │   │   │   │   ├── Home.jsx
│   │   │   │   │   ├── Home.module.scss
│   │   │   │   │   ├── Projects.jsx
│   │   │   │   │   ├── Projects.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── HomeActions/
│   │   │   │   │   ├── Filters.jsx
│   │   │   │   │   ├── Filters.module.scss
│   │   │   │   │   ├── HomeActions.jsx
│   │   │   │   │   ├── HomeActions.module.scss
│   │   │   │   │   ├── RightSide/
│   │   │   │   │   │   ├── RightSide.jsx
│   │   │   │   │   │   ├── RightSide.module.scss
│   │   │   │   │   │   ├── SelectOrderStep.jsx
│   │   │   │   │   │   ├── SelectOrderStep.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── Linkify/
│   │   │   │   │   ├── Link.jsx
│   │   │   │   │   ├── Linkify.jsx
│   │   │   │   │   └── index.js
│   │   │   │   ├── Login/
│   │   │   │   │   ├── Content.jsx
│   │   │   │   │   ├── Content.module.scss
│   │   │   │   │   ├── Login.jsx
│   │   │   │   │   ├── TermsModal.jsx
│   │   │   │   │   ├── TermsModal.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Markdown.jsx
│   │   │   │   ├── MarkdownEditor/
│   │   │   │   │   ├── MarkdownEditor.jsx
│   │   │   │   │   ├── MarkdownEditor.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Root.jsx
│   │   │   │   ├── Static/
│   │   │   │   │   ├── Static.jsx
│   │   │   │   │   ├── Static.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── TimeAgo/
│   │   │   │   │   ├── ExpirableTime.jsx
│   │   │   │   │   ├── ExpirableTime.module.scss
│   │   │   │   │   ├── TimeAgo.jsx
│   │   │   │   │   └── index.js
│   │   │   │   └── Toaster/
│   │   │   │       ├── EmptyTrashToast.jsx
│   │   │   │       ├── EmptyTrashToast.module.scss
│   │   │   │       ├── FileIsTooBigToast.jsx
│   │   │   │       ├── NotEnoughStorageToast.jsx
│   │   │   │       ├── SourceCardNotCopyableToast.jsx
│   │   │   │       ├── SourceCardNotMovableToast.jsx
│   │   │   │       ├── Toaster.jsx
│   │   │   │       └── index.js
│   │   │   ├── custom-field-groups/
│   │   │   │   ├── AddCustomFieldGroupStep/
│   │   │   │   │   ├── AddCustomFieldGroupStep.jsx
│   │   │   │   │   ├── AddCustomFieldGroupStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroup/
│   │   │   │   │   ├── CustomFieldGroup.jsx
│   │   │   │   │   ├── CustomFieldGroup.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupEditor/
│   │   │   │   │   ├── CustomFieldGroupEditor.jsx
│   │   │   │   │   ├── CustomFieldGroupEditor.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupStep/
│   │   │   │   │   ├── CustomField.jsx
│   │   │   │   │   ├── CustomField.module.scss
│   │   │   │   │   ├── CustomFieldAddStep.jsx
│   │   │   │   │   ├── CustomFieldAddStep.module.scss
│   │   │   │   │   ├── CustomFieldEditStep.jsx
│   │   │   │   │   ├── CustomFieldEditStep.module.scss
│   │   │   │   │   ├── CustomFieldEditor.jsx
│   │   │   │   │   ├── CustomFieldEditor.module.scss
│   │   │   │   │   ├── CustomFieldGroupStep.jsx
│   │   │   │   │   ├── UnbasedContent.jsx
│   │   │   │   │   ├── UnbasedContent.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupsStep/
│   │   │   │   │   ├── CustomFieldGroupsStep.jsx
│   │   │   │   │   ├── CustomFieldGroupsStep.module.scss
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── EditCustomFieldGroupStep/
│   │   │   │       ├── EditCustomFieldGroupStep.jsx
│   │   │   │       ├── EditCustomFieldGroupStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── custom-field-values/
│   │   │   │   └── CustomFieldValueChip/
│   │   │   │       ├── CustomFieldValueChip.jsx
│   │   │   │       ├── CustomFieldValueChip.module.scss
│   │   │   │       └── index.js
│   │   │   ├── custom-fields/
│   │   │   │   └── CustomField/
│   │   │   │       ├── CustomField.jsx
│   │   │   │       ├── CustomField.module.scss
│   │   │   │       ├── ValueField.jsx
│   │   │   │       ├── ValueField.module.scss
│   │   │   │       └── index.js
│   │   │   ├── labels/
│   │   │   │   ├── LabelChip/
│   │   │   │   │   ├── LabelChip.jsx
│   │   │   │   │   ├── LabelChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── LabelsStep/
│   │   │   │       ├── AddStep.jsx
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       ├── Editor.jsx
│   │   │   │       ├── Editor.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── LabelsStep.jsx
│   │   │   │       ├── LabelsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── lists/
│   │   │   │   ├── List/
│   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   ├── EditColorStep.jsx
│   │   │   │   │   ├── EditColorStep.module.scss
│   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   ├── List.jsx
│   │   │   │   │   ├── List.module.scss
│   │   │   │   │   ├── MoveStep.jsx
│   │   │   │   │   ├── MoveStep.module.scss
│   │   │   │   │   ├── SortStep.jsx
│   │   │   │   │   ├── SortStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ListsStep/
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   ├── ListsStep.jsx
│   │   │   │   │   ├── ListsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── SelectListTypeStep/
│   │   │   │       ├── SelectListTypeStep.jsx
│   │   │   │       ├── SelectListTypeStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── notification-services/
│   │   │   │   └── NotificationServices/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── NotificationServices.jsx
│   │   │   │       ├── NotificationServices.module.scss
│   │   │   │       └── index.js
│   │   │   ├── notifications/
│   │   │   │   └── NotificationsStep/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── NotificationsStep.jsx
│   │   │   │       ├── NotificationsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── project-managers/
│   │   │   │   └── ProjectManagers/
│   │   │   │       ├── ActionsStep.jsx
│   │   │   │       ├── ActionsStep.module.scss
│   │   │   │       ├── AddStep/
│   │   │   │       │   ├── AddStep.jsx
│   │   │   │       │   ├── AddStep.module.scss
│   │   │   │       │   ├── User.jsx
│   │   │   │       │   ├── User.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── ProjectManagers.jsx
│   │   │   │       ├── ProjectManagers.module.scss
│   │   │   │       └── index.js
│   │   │   ├── projects/
│   │   │   │   ├── AddProjectModal/
│   │   │   │   │   ├── AddProjectModal.jsx
│   │   │   │   │   ├── AddProjectModal.module.scss
│   │   │   │   │   ├── SelectTypeStep.jsx
│   │   │   │   │   ├── SelectTypeStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Project/
│   │   │   │   │   ├── Project.jsx
│   │   │   │   │   ├── Project.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ProjectBackground/
│   │   │   │   │   ├── ProjectBackground.jsx
│   │   │   │   │   ├── ProjectBackground.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ProjectCard/
│   │   │   │   │   ├── ProjectCard.jsx
│   │   │   │   │   ├── ProjectCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── ProjectSettingsModal/
│   │   │   │       ├── BackgroundPane/
│   │   │   │       │   ├── AddImageZone.jsx
│   │   │   │       │   ├── AddImageZone.module.scss
│   │   │   │       │   ├── BackgroundPane.jsx
│   │   │   │       │   ├── BackgroundPane.module.scss
│   │   │   │       │   ├── Gradients/
│   │   │   │       │   │   ├── Gradients.jsx
│   │   │   │       │   │   ├── Gradients.module.scss
│   │   │   │       │   │   ├── Item.jsx
│   │   │   │       │   │   ├── Item.module.scss
│   │   │   │       │   │   └── index.js
│   │   │   │       │   ├── Image.jsx
│   │   │   │       │   ├── Image.module.scss
│   │   │   │       │   ├── Images/
│   │   │   │       │   │   ├── Images.jsx
│   │   │   │       │   │   ├── Images.module.scss
│   │   │   │       │   │   ├── Item.jsx
│   │   │   │       │   │   ├── Item.module.scss
│   │   │   │       │   │   └── index.js
│   │   │   │       │   └── index.js
│   │   │   │       ├── BaseCustomFieldGroupsPane.jsx
│   │   │   │       ├── BaseCustomFieldGroupsPane.module.scss
│   │   │   │       ├── GeneralPane/
│   │   │   │       │   ├── EditInformation.jsx
│   │   │   │       │   ├── EditInformation.module.scss
│   │   │   │       │   ├── GeneralPane.jsx
│   │   │   │       │   ├── GeneralPane.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── ManagersPane.jsx
│   │   │   │       ├── ManagersPane.module.scss
│   │   │   │       ├── ProjectSettingsModal.jsx
│   │   │   │       ├── ProjectSettingsModal.module.scss
│   │   │   │       └── index.js
│   │   │   ├── task-lists/
│   │   │   │   ├── AddTaskListStep.jsx
│   │   │   │   ├── TaskList/
│   │   │   │   │   ├── AddTask.jsx
│   │   │   │   │   ├── AddTask.module.scss
│   │   │   │   │   ├── Task/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   │   ├── SelectAssigneeStep.jsx
│   │   │   │   │   │   ├── Task.jsx
│   │   │   │   │   │   ├── Task.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── TaskList.jsx
│   │   │   │   │   ├── TaskList.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── TaskListEditor/
│   │   │   │       ├── TaskListEditor.jsx
│   │   │   │       ├── TaskListEditor.module.scss
│   │   │   │       └── index.js
│   │   │   ├── users/
│   │   │   │   ├── EditUserEmailStep/
│   │   │   │   │   ├── EditUserEmailStep.jsx
│   │   │   │   │   ├── EditUserEmailStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserInformation/
│   │   │   │   │   ├── EditUserInformation.jsx
│   │   │   │   │   ├── EditUserInformation.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserInformationStep.jsx
│   │   │   │   ├── EditUserPasswordStep/
│   │   │   │   │   ├── EditUserPasswordStep.jsx
│   │   │   │   │   ├── EditUserPasswordStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserUsernameStep/
│   │   │   │   │   ├── EditUserUsernameStep.jsx
│   │   │   │   │   ├── EditUserUsernameStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── UserActionsStep/
│   │   │   │   │   ├── UserActionsStep.jsx
│   │   │   │   │   ├── UserActionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── UserAvatar/
│   │   │   │   │   ├── UserAvatar.jsx
│   │   │   │   │   ├── UserAvatar.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── UserSettingsModal/
│   │   │   │       ├── AccountPane/
│   │   │   │       │   ├── AccountPane.jsx
│   │   │   │       │   ├── AccountPane.module.scss
│   │   │   │       │   ├── EditAvatarStep.jsx
│   │   │   │       │   ├── EditAvatarStep.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── NotificationsPane.jsx
│   │   │   │       ├── NotificationsPane.module.scss
│   │   │   │       ├── PreferencesPane.jsx
│   │   │   │       ├── PreferencesPane.module.scss
│   │   │   │       ├── UserSettingsModal.jsx
│   │   │   │       └── index.js
│   │   │   └── webhooks/
│   │   │       └── Webhooks/
│   │   │           ├── Editor.jsx
│   │   │           ├── Editor.module.scss
│   │   │           ├── Item.jsx
│   │   │           ├── Item.module.scss
│   │   │           ├── Webhooks.jsx
│   │   │           └── index.js
│   │   ├── configs/
│   │   │   └── markdown-plugins/
│   │   │       ├── index.js
│   │   │       ├── link.js
│   │   │       └── mention.js
│   │   ├── constants/
│   │   │   ├── AccessTokenSteps.js
│   │   │   ├── ActionTypes.js
│   │   │   ├── BackgroundGradients.js
│   │   │   ├── ClipboardTypes.js
│   │   │   ├── Config.js
│   │   │   ├── DroppableTypes.js
│   │   │   ├── Encodings.js
│   │   │   ├── EntryActionTypes.js
│   │   │   ├── Enums.js
│   │   │   ├── ErrorCodes.js
│   │   │   ├── Icons.js
│   │   │   ├── LabelColors.js
│   │   │   ├── ListColors.js
│   │   │   ├── ListTypeStateByType.js
│   │   │   ├── ModalTypes.js
│   │   │   ├── Paths.js
│   │   │   ├── StaticUsers.js
│   │   │   ├── ToastTypes.js
│   │   │   └── WebhookEvents.js
│   │   ├── contexts/
│   │   │   ├── BoardShortcutsContext.js
│   │   │   ├── ClosableContext.js
│   │   │   └── index.js
│   │   ├── entry-actions/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── login.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── history.js
│   │   ├── hooks/
│   │   │   ├── index.js
│   │   │   ├── use-closable-modal.jsx
│   │   │   ├── use-closable.js
│   │   │   ├── use-escape-interceptor.js
│   │   │   ├── use-field.js
│   │   │   ├── use-form.js
│   │   │   ├── use-modal.js
│   │   │   ├── use-nested-ref.js
│   │   │   ├── use-popup-in-closable-context.js
│   │   │   └── use-steps.js
│   │   ├── i18n.js
│   │   ├── index.js
│   │   ├── lib/
│   │   │   ├── custom-ui/
│   │   │   │   ├── assets/
│   │   │   │   │   └── fonts/
│   │   │   │   │       └── icons.otf
│   │   │   │   ├── components/
│   │   │   │   │   ├── FilePicker/
│   │   │   │   │   │   ├── FilePicker.jsx
│   │   │   │   │   │   ├── FilePicker.module.css
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Input/
│   │   │   │   │   │   ├── Input.jsx
│   │   │   │   │   │   ├── InputMask.jsx
│   │   │   │   │   │   ├── InputPassword.jsx
│   │   │   │   │   │   ├── InputPassword.module.css
│   │   │   │   │   │   ├── MaskedInput.jsx
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Masonry/
│   │   │   │   │   │   ├── Masonry.jsx
│   │   │   │   │   │   ├── Masonry.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── Popup/
│   │   │   │   │       ├── Popup.jsx
│   │   │   │   │       ├── PopupHeader.jsx
│   │   │   │   │       ├── PopupHeader.module.css
│   │   │   │   │       └── index.js
│   │   │   │   ├── index.js
│   │   │   │   └── styles.css
│   │   │   ├── hooks/
│   │   │   │   ├── index.js
│   │   │   │   ├── use-click-away-listener.js
│   │   │   │   ├── use-did-update.js
│   │   │   │   ├── use-event-callback.js
│   │   │   │   ├── use-force-update.js
│   │   │   │   ├── use-previous.js
│   │   │   │   ├── use-toggle.js
│   │   │   │   ├── use-transitioning.js
│   │   │   │   └── use-window-width.js
│   │   │   ├── popup/
│   │   │   │   ├── Popup.module.css
│   │   │   │   ├── close-popup.js
│   │   │   │   ├── index.js
│   │   │   │   └── use-popup.jsx
│   │   │   ├── redux-router/
│   │   │   │   ├── ReduxRouter.jsx
│   │   │   │   ├── actions.js
│   │   │   │   ├── create-router-middleware.js
│   │   │   │   ├── create-router-reducer.js
│   │   │   │   └── index.js
│   │   │   └── syntax-highlighter/
│   │   │       ├── index.js
│   │   │       ├── language-definitions/
│   │   │       │   ├── chapel.js
│   │   │       │   ├── dafny.js
│   │   │       │   ├── gn.js
│   │   │       │   ├── godot.js
│   │   │       │   ├── hlsl.js
│   │   │       │   └── terraform.js
│   │   │       ├── languages-map.json
│   │   │       ├── languages.js
│   │   │       └── syntax-highlighter.js
│   │   ├── locales/
│   │   │   ├── ar-YE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── bg-BG/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ca-ES/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── cs-CZ/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── da-DK/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── de-DE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── el-GR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── en-GB/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── en-US/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── es-ES/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── et-EE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fa-IR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fi-FI/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fr-FR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── hu-HU/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── id-ID/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── index.js
│   │   │   ├── it-IT/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ja-JP/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ko-KR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── nl-NL/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pl-PL/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pt-BR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pt-PT/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ro-RO/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ru-RU/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sk-SK/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sr-Cyrl-RS/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sr-Latn-RS/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sv-SE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── tr-TR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── uk-UA/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── uz-UZ/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── vi-VN/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── zh-CN/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   └── zh-TW/
│   │   │       ├── core.js
│   │   │       ├── index.js
│   │   │       ├── login.js
│   │   │       └── markdown-editor.json
│   │   ├── models/
│   │   │   ├── Activity.js
│   │   │   ├── Attachment.js
│   │   │   ├── BackgroundImage.js
│   │   │   ├── BaseCustomFieldGroup.js
│   │   │   ├── BaseModel.js
│   │   │   ├── Board.js
│   │   │   ├── BoardMembership.js
│   │   │   ├── Card.js
│   │   │   ├── Comment.js
│   │   │   ├── CustomField.js
│   │   │   ├── CustomFieldGroup.js
│   │   │   ├── CustomFieldValue.js
│   │   │   ├── Label.js
│   │   │   ├── List.js
│   │   │   ├── Notification.js
│   │   │   ├── NotificationService.js
│   │   │   ├── Project.js
│   │   │   ├── ProjectManager.js
│   │   │   ├── Task.js
│   │   │   ├── TaskList.js
│   │   │   ├── User.js
│   │   │   ├── Webhook.js
│   │   │   └── index.js
│   │   ├── orm.js
│   │   ├── reducers/
│   │   │   ├── auth.js
│   │   │   ├── common.js
│   │   │   ├── core.js
│   │   │   ├── index.js
│   │   │   ├── orm.js
│   │   │   ├── router.js
│   │   │   ├── socket.js
│   │   │   └── ui/
│   │   │       ├── authenticate-form.js
│   │   │       ├── index.js
│   │   │       ├── project-create-form.js
│   │   │       ├── smtp-test-state.js
│   │   │       └── user-create-form.js
│   │   ├── sagas/
│   │   │   ├── core/
│   │   │   │   ├── index.js
│   │   │   │   ├── request.js
│   │   │   │   ├── requests/
│   │   │   │   │   ├── boards.js
│   │   │   │   │   ├── core.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── services/
│   │   │   │   │   ├── activities.js
│   │   │   │   │   ├── attachments.js
│   │   │   │   │   ├── background-images.js
│   │   │   │   │   ├── base-custom-field-groups.js
│   │   │   │   │   ├── board-memberships.js
│   │   │   │   │   ├── boards.js
│   │   │   │   │   ├── bootstrap.js
│   │   │   │   │   ├── cards.js
│   │   │   │   │   ├── comments.js
│   │   │   │   │   ├── config.js
│   │   │   │   │   ├── core.js
│   │   │   │   │   ├── custom-field-groups.js
│   │   │   │   │   ├── custom-field-values.js
│   │   │   │   │   ├── custom-fields.js
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── labels.js
│   │   │   │   │   ├── lists.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── notification-services.js
│   │   │   │   │   ├── notifications.js
│   │   │   │   │   ├── project-managers.js
│   │   │   │   │   ├── projects.js
│   │   │   │   │   ├── router.js
│   │   │   │   │   ├── socket.js
│   │   │   │   │   ├── task-lists.js
│   │   │   │   │   ├── tasks.js
│   │   │   │   │   ├── users.js
│   │   │   │   │   └── webhooks.js
│   │   │   │   └── watchers/
│   │   │   │       ├── activities.js
│   │   │   │       ├── attachments.js
│   │   │   │       ├── background-images.js
│   │   │   │       ├── base-custom-field-groups.js
│   │   │   │       ├── board-memberships.js
│   │   │   │       ├── boards.js
│   │   │   │       ├── bootstrap.js
│   │   │   │       ├── cards.js
│   │   │   │       ├── comments.js
│   │   │   │       ├── config.js
│   │   │   │       ├── core.js
│   │   │   │       ├── custom-field-groups.js
│   │   │   │       ├── custom-field-values.js
│   │   │   │       ├── custom-fields.js
│   │   │   │       ├── index.js
│   │   │   │       ├── labels.js
│   │   │   │       ├── lists.js
│   │   │   │       ├── modals.js
│   │   │   │       ├── notification-services.js
│   │   │   │       ├── notifications.js
│   │   │   │       ├── project-managers.js
│   │   │   │       ├── projects.js
│   │   │   │       ├── router.js
│   │   │   │       ├── socket.js
│   │   │   │       ├── task-lists.js
│   │   │   │       ├── tasks.js
│   │   │   │       ├── users.js
│   │   │   │       └── webhooks.js
│   │   │   ├── index.js
│   │   │   ├── login/
│   │   │   │   ├── index.js
│   │   │   │   ├── services/
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── login.js
│   │   │   │   │   └── router.js
│   │   │   │   └── watchers/
│   │   │   │       ├── index.js
│   │   │   │       ├── login.js
│   │   │   │       └── router.js
│   │   │   └── run-watchers.js
│   │   ├── selectors/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── common.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── positioning.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── router.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── store.js
│   │   ├── styles.module.scss
│   │   ├── utils/
│   │   │   ├── access-token-storage.js
│   │   │   ├── build-search-parts.js
│   │   │   ├── element-helpers.js
│   │   │   ├── event-helpers.js
│   │   │   ├── get-date-format.js
│   │   │   ├── get-filename-and-extension.js
│   │   │   ├── local-id.js
│   │   │   ├── local-id.test.js
│   │   │   ├── markdown-to-text.js
│   │   │   ├── match-paths.js
│   │   │   ├── mentions.js
│   │   │   ├── merge-records.js
│   │   │   ├── parse-dnd-id.js
│   │   │   ├── parse-time.js
│   │   │   ├── record-helpers.js
│   │   │   ├── stopwatch.js
│   │   │   └── validator.js
│   │   └── version.js
│   ├── tests/
│   │   ├── acceptance/
│   │   │   ├── Config.js
│   │   │   ├── cucumber.conf.js
│   │   │   ├── features/
│   │   │   │   └── login.feature
│   │   │   ├── pages/
│   │   │   │   ├── HomePage.js
│   │   │   │   └── LoginPage.js
│   │   │   └── steps/
│   │   │       └── login.step.js
│   │   └── setup-symlinks.sh
│   ├── version-template.ejs
│   └── vite.config.js
├── docker-backup.sh
├── docker-compose-dev.yml
├── docker-compose.yml
├── docker-restore.sh
├── package.json
└── server/
    ├── .buildignore
    ├── .editorconfig
    ├── .eslintignore
    ├── .gitignore
    ├── .npmrc
    ├── .sailsrc
    ├── api/
    │   ├── controllers/
    │   │   ├── .gitkeep
    │   │   ├── _internal/
    │   │   │   └── update-config.js
    │   │   ├── access-tokens/
    │   │   │   ├── accept-terms.js
    │   │   │   ├── create.js
    │   │   │   ├── debug-oidc.js
    │   │   │   ├── delete.js
    │   │   │   ├── exchange-with-oidc.js
    │   │   │   └── revoke-pending-token.js
    │   │   ├── actions/
    │   │   │   ├── index-in-board.js
    │   │   │   └── index-in-card.js
    │   │   ├── attachments/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── background-images/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── base-custom-field-groups/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── board-memberships/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── boards/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── bootstrap/
    │   │   │   └── show.js
    │   │   ├── card-labels/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── card-memberships/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── cards/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── duplicate.js
    │   │   │   ├── index.js
    │   │   │   ├── read-notifications.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── comments/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   └── update.js
    │   │   ├── config/
    │   │   │   ├── show.js
    │   │   │   ├── test-smtp.js
    │   │   │   └── update.js
    │   │   ├── custom-field-groups/
    │   │   │   ├── create-in-board.js
    │   │   │   ├── create-in-card.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── custom-field-values/
    │   │   │   ├── create-or-update.js
    │   │   │   └── delete.js
    │   │   ├── custom-fields/
    │   │   │   ├── create-in-base-custom-field-group.js
    │   │   │   ├── create-in-custom-field-group.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── file-attachments/
    │   │   │   ├── download-thumbnail.js
    │   │   │   └── download.js
    │   │   ├── index.js
    │   │   ├── labels/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── lists/
    │   │   │   ├── clear.js
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── move-cards.js
    │   │   │   ├── show.js
    │   │   │   ├── sort.js
    │   │   │   └── update.js
    │   │   ├── notification-services/
    │   │   │   ├── create-in-board.js
    │   │   │   ├── create-in-user.js
    │   │   │   ├── delete.js
    │   │   │   ├── test.js
    │   │   │   └── update.js
    │   │   ├── notifications/
    │   │   │   ├── index.js
    │   │   │   ├── read-all.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── project-managers/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── projects/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── swagger/
    │   │   │   └── show.js
    │   │   ├── task-lists/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── tasks/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── terms/
    │   │   │   └── show.js
    │   │   ├── users/
    │   │   │   ├── create-api-key.js
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   ├── show.js
    │   │   │   ├── update-avatar.js
    │   │   │   ├── update-email.js
    │   │   │   ├── update-password.js
    │   │   │   ├── update-username.js
    │   │   │   └── update.js
    │   │   └── webhooks/
    │   │       ├── create.js
    │   │       ├── delete.js
    │   │       ├── index.js
    │   │       └── update.js
    │   ├── helpers/
    │   │   ├── .gitkeep
    │   │   ├── access-tokens/
    │   │   │   └── handle-steps.js
    │   │   ├── actions/
    │   │   │   └── create-one.js
    │   │   ├── attachments/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   ├── process-link.js
    │   │   │   ├── process-uploaded-file.js
    │   │   │   └── update-one.js
    │   │   ├── background-images/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   └── process-uploaded-file.js
    │   │   ├── base-custom-field-groups/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── board-memberships/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── boards/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-card-ids.js
    │   │   │   ├── get-kanban-lists-by-id.js
    │   │   │   ├── get-member-user-ids.js
    │   │   │   ├── get-notification-services-total.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── get-subscription-user-ids.js
    │   │   │   ├── import-from-trello.js
    │   │   │   ├── process-uploaded-trello-import-file.js
    │   │   │   └── update-one.js
    │   │   ├── bootstrap/
    │   │   │   └── present-one.js
    │   │   ├── card-labels/
    │   │   │   ├── create-one.js
    │   │   │   └── delete-one.js
    │   │   ├── card-memberships/
    │   │   │   ├── create-one.js
    │   │   │   └── delete-one.js
    │   │   ├── cards/
    │   │   │   ├── copy-custom-fields.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── detach-custom-fields.js
    │   │   │   ├── duplicate-one.js
    │   │   │   ├── get-labels.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── get-subscription-user-ids.js
    │   │   │   ├── read-notifications-for-user.js
    │   │   │   └── update-one.js
    │   │   ├── comments/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── config/
    │   │   │   ├── present-one.js
    │   │   │   └── update-main.js
    │   │   ├── custom-field-groups/
    │   │   │   ├── create-one-in-board.js
    │   │   │   ├── create-one-in-card.js
    │   │   │   ├── delete-one-in-board.js
    │   │   │   ├── delete-one-in-card.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── update-one-in-board.js
    │   │   │   └── update-one-in-card.js
    │   │   ├── custom-field-values/
    │   │   │   ├── create-or-update-one.js
    │   │   │   └── delete-one.js
    │   │   ├── custom-fields/
    │   │   │   ├── create-one-in-base-custom-field-group.js
    │   │   │   ├── create-one-in-custom-field-group.js
    │   │   │   ├── delete-one-in-base-custom-field-group.js
    │   │   │   ├── delete-one-in-custom-field-group.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── update-one-in-base-custom-field-group.js
    │   │   │   └── update-one-in-custom-field-group.js
    │   │   ├── internal-config/
    │   │   │   └── update-main.js
    │   │   ├── labels/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── lists/
    │   │   │   ├── clear-one.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── is-archive-or-trash.js
    │   │   │   ├── is-finite.js
    │   │   │   ├── is-kanban.js
    │   │   │   ├── move-cards.js
    │   │   │   ├── resolve-name.js
    │   │   │   ├── sort-one.js
    │   │   │   └── update-one.js
    │   │   ├── notification-services/
    │   │   │   ├── create-one-in-board.js
    │   │   │   ├── create-one-in-user.js
    │   │   │   ├── delete-one-in-board.js
    │   │   │   ├── delete-one-in-user.js
    │   │   │   ├── get-path-to-user-by-id.js
    │   │   │   ├── update-one-in-board.js
    │   │   │   └── update-one-in-user.js
    │   │   ├── notifications/
    │   │   │   ├── create-many.js
    │   │   │   ├── create-one.js
    │   │   │   ├── read-all-for-user.js
    │   │   │   └── update-one.js
    │   │   ├── project-managers/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   └── get-path-to-project-by-id.js
    │   │   ├── projects/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-board-ids-by-id.js
    │   │   │   ├── get-board-memberships-total-by-id-and-user-id.js
    │   │   │   ├── get-boards-total-by-id.js
    │   │   │   ├── get-lonely-by-ids.js
    │   │   │   ├── get-manager-user-ids.js
    │   │   │   ├── get-project-managers-total-by-id.js
    │   │   │   ├── make-scoper.js
    │   │   │   └── update-one.js
    │   │   ├── sessions/
    │   │   │   └── create-one.js
    │   │   ├── task-lists/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── tasks/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── users/
    │   │   │   ├── build-gravatar-url.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-all-active-ids.js
    │   │   │   ├── get-manager-project-ids.js
    │   │   │   ├── get-notification-services-total.js
    │   │   │   ├── get-or-create-one-with-oidc.js
    │   │   │   ├── get-project-managers-total-by-id.js
    │   │   │   ├── is-admin-or-project-owner.js
    │   │   │   ├── is-board-member.js
    │   │   │   ├── is-board-subscriber.js
    │   │   │   ├── is-card-subscriber.js
    │   │   │   ├── is-project-favorite.js
    │   │   │   ├── is-project-manager.js
    │   │   │   ├── make-scoper.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   ├── process-uploaded-avatar-file.js
    │   │   │   └── update-one.js
    │   │   ├── utils/
    │   │   │   ├── clear-http-only-token-cookie.js
    │   │   │   ├── create-jwt-token.js
    │   │   │   ├── download-favicon.js
    │   │   │   ├── generate-api-key.js
    │   │   │   ├── generate-ids.js
    │   │   │   ├── generate-random-string.js
    │   │   │   ├── get-available-storage.js
    │   │   │   ├── hash.js
    │   │   │   ├── insert-to-positionables.js
    │   │   │   ├── is-preloaded-favicon-exists.js
    │   │   │   ├── make-smtp-transporter.js
    │   │   │   ├── make-translator.js
    │   │   │   ├── map-records.js
    │   │   │   ├── receive-file.js
    │   │   │   ├── remove-unreferenced-uploaded-files.js
    │   │   │   ├── send-email.js
    │   │   │   ├── send-notifications.js
    │   │   │   ├── send-webhooks.js
    │   │   │   ├── set-http-only-token-cookie.js
    │   │   │   └── verify-jwt-token.js
    │   │   └── webhooks/
    │   │       ├── create-one.js
    │   │       ├── delete-one.js
    │   │       └── update-one.js
    │   ├── hooks/
    │   │   ├── .gitkeep
    │   │   ├── current-user/
    │   │   │   └── index.js
    │   │   ├── file-manager/
    │   │   │   ├── LocalFileManager.js
    │   │   │   ├── S3FileManager.js
    │   │   │   └── index.js
    │   │   ├── oidc/
    │   │   │   └── index.js
    │   │   ├── query-methods/
    │   │   │   ├── helpers.js
    │   │   │   ├── index.js
    │   │   │   └── models/
    │   │   │       ├── Action.js
    │   │   │       ├── Attachment.js
    │   │   │       ├── BackgroundImage.js
    │   │   │       ├── BaseCustomFieldGroup.js
    │   │   │       ├── Board.js
    │   │   │       ├── BoardMembership.js
    │   │   │       ├── BoardSubscription.js
    │   │   │       ├── Card.js
    │   │   │       ├── CardLabel.js
    │   │   │       ├── CardMembership.js
    │   │   │       ├── CardSubscription.js
    │   │   │       ├── Comment.js
    │   │   │       ├── Config.js
    │   │   │       ├── CustomField.js
    │   │   │       ├── CustomFieldGroup.js
    │   │   │       ├── CustomFieldValue.js
    │   │   │       ├── IdentityProviderUser.js
    │   │   │       ├── InternalConfig.js
    │   │   │       ├── Label.js
    │   │   │       ├── List.js
    │   │   │       ├── Notification.js
    │   │   │       ├── NotificationService.js
    │   │   │       ├── Project.js
    │   │   │       ├── ProjectFavorite.js
    │   │   │       ├── ProjectManager.js
    │   │   │       ├── Session.js
    │   │   │       ├── StorageUsage.js
    │   │   │       ├── Task.js
    │   │   │       ├── TaskList.js
    │   │   │       ├── UploadedFile.js
    │   │   │       ├── User.js
    │   │   │       └── Webhook.js
    │   │   ├── s3/
    │   │   │   └── index.js
    │   │   ├── terms/
    │   │   │   └── index.js
    │   │   └── watcher/
    │   │       └── index.js
    │   ├── models/
    │   │   ├── .gitkeep
    │   │   ├── Action.js
    │   │   ├── Attachment.js
    │   │   ├── BackgroundImage.js
    │   │   ├── BaseCustomFieldGroup.js
    │   │   ├── Board.js
    │   │   ├── BoardMembership.js
    │   │   ├── BoardSubscription.js
    │   │   ├── Card.js
    │   │   ├── CardLabel.js
    │   │   ├── CardMembership.js
    │   │   ├── CardSubscription.js
    │   │   ├── Comment.js
    │   │   ├── Config.js
    │   │   ├── CustomField.js
    │   │   ├── CustomFieldGroup.js
    │   │   ├── CustomFieldValue.js
    │   │   ├── IdentityProviderUser.js
    │   │   ├── InternalConfig.js
    │   │   ├── Label.js
    │   │   ├── List.js
    │   │   ├── Notification.js
    │   │   ├── NotificationService.js
    │   │   ├── Project.js
    │   │   ├── ProjectFavorite.js
    │   │   ├── ProjectManager.js
    │   │   ├── Session.js
    │   │   ├── StorageUsage.js
    │   │   ├── Task.js
    │   │   ├── TaskList.js
    │   │   ├── UploadedFile.js
    │   │   ├── User.js
    │   │   └── Webhook.js
    │   ├── policies/
    │   │   ├── .gitkeep
    │   │   ├── is-admin-or-project-owner.js
    │   │   ├── is-admin.js
    │   │   ├── is-authenticated.js
    │   │   ├── is-external.js
    │   │   ├── is-internal.js
    │   │   └── is-session.js
    │   └── responses/
    │       ├── .gitkeep
    │       ├── conflict.js
    │       ├── forbidden.js
    │       ├── notFound.js
    │       ├── unauthorized.js
    │       ├── unprocessableEntity.js
    │       └── validationError.js
    ├── app.js
    ├── build.js
    ├── config/
    │   ├── blueprints.js
    │   ├── bootstrap.js
    │   ├── custom.js
    │   ├── datastores.js
    │   ├── env/
    │   │   ├── production.js
    │   │   └── test.js
    │   ├── globals.js
    │   ├── http.js
    │   ├── i18n.js
    │   ├── locales/
    │   │   ├── ar-YE.json
    │   │   ├── bg-BG.json
    │   │   ├── ca-ES.json
    │   │   ├── cs-CZ.json
    │   │   ├── da-DK.json
    │   │   ├── de-DE.json
    │   │   ├── el-GR.json
    │   │   ├── en-GB.json
    │   │   ├── en-US.json
    │   │   ├── es-ES.json
    │   │   ├── et-EE.json
    │   │   ├── fa-IR.json
    │   │   ├── fi-FI.json
    │   │   ├── fr-FR.json
    │   │   ├── hu-HU.json
    │   │   ├── id-ID.json
    │   │   ├── it-IT.json
    │   │   ├── ja-JP.json
    │   │   ├── ko-KR.json
    │   │   ├── nl-NL.json
    │   │   ├── pl-PL.json
    │   │   ├── pt-BR.json
    │   │   ├── pt-PT.json
    │   │   ├── ro-RO.json
    │   │   ├── ru-RU.json
    │   │   ├── sk-SK.json
    │   │   ├── sr-Cyrl-RS.json
    │   │   ├── sr-Latn-RS.json
    │   │   ├── sv-SE.json
    │   │   ├── tr-TR.json
    │   │   ├── uk-UA.json
    │   │   ├── uz-UZ.json
    │   │   ├── vi-VN.json
    │   │   ├── zh-CN.json
    │   │   └── zh-TW.json
    │   ├── log.js
    │   ├── models.js
    │   ├── policies.js
    │   ├── routes.js
    │   ├── security.js
    │   ├── session.js
    │   ├── sockets.js
    │   ├── swagger.js
    │   └── views.js
    ├── constants.js
    ├── data/
    │   └── .gitkeep
    ├── db/
    │   ├── create-admin-user.js
    │   ├── init.js
    │   ├── knexfile.js
    │   ├── migrations/
    │   │   ├── 20250228000022_version_2.js
    │   │   ├── 20250522151122_add_board_activity_log.js
    │   │   ├── 20250523131647_add_comments_counter.js
    │   │   ├── 20250603102521_canonicalize_locale_codes.js
    │   │   ├── 20250703122452_move_webhooks_configuration_from_environment_variable_to_ui.js
    │   │   ├── 20250708200908_persist_closed_state_per_card.js
    │   │   ├── 20250709160208_add_ability_to_link_tasks_to_cards.js
    │   │   ├── 20250721132312_add_ability_to_hide_completed_tasks.js
    │   │   ├── 20250728105713_add_legal_requirements.js
    │   │   ├── 20250820144730_track_storage_usage.js
    │   │   ├── 20250905101408_restore_toggleable_due_dates.js
    │   │   ├── 20250905205438_add_board_setting_to_expand_task_lists_by_default.js
    │   │   ├── 20250917123048_add_ability_to_configure_smtp_via_ui.js
    │   │   ├── 20251105104948_add_api_key_authentication.js
    │   │   ├── 20251121231641_rename_gin_indexes.js
    │   │   ├── 20260122093047_add_internal_runtime_configuration.js
    │   │   └── 20260312000000_add_ability_to_display_card_ages.js
    │   ├── seeds/
    │   │   ├── .gitkeep
    │   │   └── default.js
    │   └── upgrade.js
    ├── generate-swagger.js
    ├── healthcheck.js
    ├── nodemon.json
    ├── package.json
    ├── patches/
    │   ├── sails+1.5.17.patch
    │   ├── skipper-disk+0.5.12.patch
    │   └── waterline+0.15.2.patch
    ├── requirements.txt
    ├── setup-python.js
    ├── start.sh
    ├── terms/
    │   ├── _template/
    │   │   ├── de-DE.md
    │   │   └── en-US.md
    │   └── cloud/
    │       ├── de-DE.md
    │       └── en-US.md
    ├── test/
    │   ├── fixtures/
    │   │   └── .gitkeep
    │   ├── integration/
    │   │   ├── controllers/
    │   │   │   └── .gitkeep
    │   │   ├── helpers/
    │   │   │   └── .gitkeep
    │   │   └── models/
    │   │       └── User.test.js
    │   ├── lifecycle.test.js
    │   ├── mocha.opts
    │   └── utils/
    │       └── remote-address.test.js
    ├── utils/
    │   ├── build-query-parts.js
    │   ├── filenamify.js
    │   ├── inputs.js
    │   ├── logger.js
    │   ├── mentions.js
    │   ├── migrations.js
    │   ├── normalize-values.js
    │   ├── remote-address.js
    │   ├── send_notifications.py
    │   └── validators.js
    ├── version-template.ejs
    ├── version.js
    └── views/
        └── .gitkeep

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
**/.DS_Store

*/node_modules

server/swagger.json
server/.env

server/dist
server/logs
server/test
server/.tmp
server/.venv

server/views/index.ejs

server/data/*
!server/data/.gitkeep

server/terms/*
!server/terms/_template
!server/terms/cloud

client/dist


================================================
FILE: .gitattributes
================================================
client/src/lib/custom-ui/styles.css linguist-vendored


================================================
FILE: .github/ISSUE_TEMPLATE/1-bug-report.yml
================================================
name: "🐛 Bug Report"
description: Report a bug found while using PLANKA
title: "[Bug]: "
labels: ["Type: Bug", "Status: Triage"]
body:
  - type: dropdown
    id: issue-type
    attributes:
      label: Where is the problem occurring?
      description: Select the part of the application where you encountered the issue.
      options:
        - "I encountered the problem while using the application (Frontend)"
        - "I encountered the problem while interacting with the server (Backend)"
        - "I'm not sure"
  - type: dropdown
    id: browsers
    attributes:
      label: What browsers are you seeing the problem on?
      multiple: true
      options:
        - Brave
        - Chrome
        - Firefox
        - Microsoft Edge
        - Safari
        - Other
  - type: textarea
    id: current-behavior
    attributes:
      label: Current behavior
      description: A description of what is currently happening, including screenshots and other useful information (**DO NOT INCLUDE PRIVATE INFORMATION**).
      placeholder: Currently...
    validations:
      required: true
  - type: textarea
    id: desired-behavior
    attributes:
      label: Desired behavior
      description: A clear description of what you think should happen.
      placeholder: In this situation, I expected ...
  - type: textarea
    id: reproduction
    attributes:
      label: Steps to reproduce
      description: Clearly describe which steps or actions you have taken to arrive at the problem. If you have some experience with the code, please link to the specific pieces of code.
      placeholder: I did X, then Y, before arriving at Z, when ERROR...
    validations:
      required: true
  - type: textarea
    id: other
    attributes:
      label: Other information
      description: Any other details?


================================================
FILE: .github/ISSUE_TEMPLATE/2-feature-request.yml
================================================
name: "✨ Feature Request"
description: Suggest a feature or enhancement to improve PLANKA.
labels: ["Type: Idea"]
body:
  - type: dropdown
    id: idea-type
    attributes:
      label: Which part of the project does this feature apply to?
      multiple: true
      options:
        - Backend
        - Frontend
        - Chart
    validations:
      required: true
  - type: textarea
    id: feature
    attributes:
      label: What would you like?
      description: A clear description of the feature or enhancement wanted.
      placeholder: I'd like to be able to...
    validations:
      required: true
  - type: textarea
    id: reason
    attributes:
      label: Why is this needed?
      description: A clear description of why this would be useful to have.
      placeholder: I want this because...
  - type: textarea
    id: other
    attributes:
      label: Other information
      placeholder: Any other details?


================================================
FILE: .github/workflows/build-and-publish-release-package.yml
================================================
name: Build and Publish Release Package

on:
  release:
    types: [created]

jobs:
  build-and-publish-release-package:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Update npm
        run: npm install npm --global

      - name: Install server dependencies
        run: npm install --omit=prod --ignore-scripts
        working-directory: ./server

      - name: Build server
        run: npm run build
        working-directory: ./server

      - name: Install client dependencies
        run: npm install --omit=dev
        working-directory: ./client

      - name: Build client
        run: INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
        working-directory: ./client

      - name: Include licenses into dist
        run: |
          mv LICENSE.md server/dist
          mv "LICENSES/PLANKA Community License DE.md" server/dist/LICENSE_DE.md

      - name: Include built client into dist
        run: |
          mv ../../client/dist/* public
          mv public/index.ejs views
        working-directory: ./server/dist

      - name: Create release package
        run: |
          mv dist planka
          zip -r planka-prebuild.zip planka
        working-directory: ./server

      - name: Publish release package
        env:
          GH_TOKEN: ${{ github.token }}
        run: gh release upload ${{ github.event.release.tag_name }} planka-prebuild.zip
        working-directory: ./server


================================================
FILE: .github/workflows/build-and-push-docker-image.yml
================================================
name: Build and Push Docker Image

on:
  release:
    types: [created]

jobs:
  build-and-push-docker-image:
    runs-on: self-hosted

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Install client dependencies
        run: npm install --omit=dev
        working-directory: ./client

      - name: Build client
        run: |
          INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
          mv dist build
        working-directory: ./client

      - name: Update Dockerfile to use prebuilt client
        run: |
          sed -i '/^FROM node:22 AS client/,/^  && INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build$/c\
          FROM node:22 AS client\n\
          WORKDIR /app\n\
          COPY client/build /app/dist' Dockerfile
          cat Dockerfile

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set version from release tag
        uses: actions/github-script@v6
        id: set-version
        with:
          result-encoding: string
          script: return context.payload.release.tag_name.replace('v', '')

      - name: Generate Docker image tags
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: |
            name=ghcr.io/${{ github.repository }}
          tags: |
            type=raw,value=${{ steps.set-version.outputs.result }}
            type=raw,value=latest
          labels: |
            org.opencontainers.image.licenses=Fair Use License
            org.opencontainers.image.url=https://planka.app

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          push: true
          tags: ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/build-and-push-docker-nightly-image.yml
================================================
name: Build and Push Docker Nightly Image

on:
  push:
    paths-ignore:
      - '.github/**'
      - 'charts/**'
      - 'docker-*.yml'
      - '*.md'
    branches: [master]
  workflow_dispatch:

jobs:
  build-and-push-docker-nightly-image:
    runs-on: self-hosted

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Update version with build number
        run: |
          npm version "$(node -p "require('./package.json').version")-nightly.$(git rev-list --count HEAD)" --no-git-tag-version
          npx --yes genversion --source . --template server/version-template.ejs server/version.js
          npx --yes genversion --source . --template client/version-template.ejs client/src/version.js

      - name: Install client dependencies
        run: npm install --omit=dev
        working-directory: ./client

      - name: Build client
        run: |
          INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build
          mv dist build
        working-directory: ./client

      - name: Update Dockerfile to use prebuilt client
        run: |
          sed -i '/^FROM node:22 AS client/,/^  && INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build$/c\
          FROM node:22 AS client\n\
          WORKDIR /app\n\
          COPY client/build /app/dist' Dockerfile
          cat Dockerfile

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate Docker image tags
        id: metadata
        uses: docker/metadata-action@v5
        with:
          images: |
            name=ghcr.io/${{ github.repository }}
          tags: |
            type=raw,value=nightly
          labels: |
            org.opencontainers.image.licenses=Fair Use License
            org.opencontainers.image.url=https://planka.app

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          push: true
          tags: ${{ steps.metadata.outputs.tags }}
          labels: ${{ steps.metadata.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max


================================================
FILE: .github/workflows/build-and-test.yml
================================================
name: Build and Test

on:
  pull_request:
    branches:
      - master
  push:
    branches:
      - master

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    env:
      POSTGRES_USERNAME: planka
      POSTGRES_PASSWORD: planka
      POSTGRES_DATABASE: planka

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Set up PostgreSQL
        uses: ikalnytskyi/action-setup-postgres@v5
        with:
          username: ${{ env.POSTGRES_USERNAME }}
          password: ${{ env.POSTGRES_PASSWORD }}
          database: ${{ env.POSTGRES_DATABASE }}

      - name: Cache Node.js modules
        uses: actions/cache@v3
        with:
          path: client/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies and build client
        run: |
          npm install
          cd client
          INDEX_FORMAT=ejs npm run build

      - name: Set up and start server for testing
        env:
          DEFAULT_ADMIN_EMAIL: demo@demo.demo
          DEFAULT_ADMIN_PASSWORD: demo
          DEFAULT_ADMIN_NAME: Demo Demo
          DEFAULT_ADMIN_USERNAME: demo
        run: |
          client/tests/setup-symlinks.sh
          cd server
          cp .env.sample .env
          sed -i "s|^DATABASE_URL=.*|DATABASE_URL=postgresql://${POSTGRES_USERNAME}:${POSTGRES_PASSWORD}@localhost/${POSTGRES_DATABASE}|" .env
          npm run db:init
          npm start --prod &

      - name: Wait for server to start
        run: |
          sudo apt-get install wait-for-it -y
          wait-for-it -h localhost -p 1337 -t 10

      - name: Seed database with terms signature
        run: |
          TERMS_SIGNATURE=$(sha256sum terms/_template/en-US.md | awk '{print $1}')
          PGPASSWORD=$POSTGRES_PASSWORD psql -h localhost -U $POSTGRES_USERNAME -d $POSTGRES_DATABASE -c "UPDATE user_account SET terms_signature = '$TERMS_SIGNATURE';"
        working-directory: ./server

      - name: Run UI tests
        run: |
          npx playwright install chromium
          npm run test:acceptance
        working-directory: ./client


================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint

on:
  pull_request:
    branches:
      - master

jobs:
  lint:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '22'
          cache: 'npm'

      - name: Cache Node.js modules
        uses: actions/cache@v3
        with:
          path: client/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm install

      - name: Run linter
        run: npm run lint


================================================
FILE: .github/workflows/release-helm-chart.yml
================================================
name: Release Charts

on:
  push:
    paths:
      - "charts/**"
    branches:
      - master

jobs:
  release-helm-chart:
    # depending on default permission settings for your org (contents being read-only or read-write for workloads), you will have to add permissions
    # see: https://docs.github.com/en/actions/security-guides/automatic-token-authentication#modifying-the-permissions-for-the-github_token
    permissions:
      contents: write

    runs-on: self-hosted

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Configure Git
        run: |
          git config user.name "$GITHUB_ACTOR"
          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

      - name: Install Helm
        uses: azure/setup-helm@v3

      - name: Add Helm chart repositories
        run: |
          for dir in $(ls -d charts/*/); do
            helm dependency list $dir 2> /dev/null | tail +2 | head -n -1 | awk '{ print "helm repo add " $1 " " $3 }' | while read cmd; do $cmd; done
          done

      - name: Run chart-releaser
        uses: helm/chart-releaser-action@v1.6.0
        with:
          charts_dir: charts
          mark_as_latest: false
        env:
          CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"


================================================
FILE: .gitignore
================================================
docker-compose.override.yml
.idea
.DS_Store

node_modules

# Prevent another lockfile than package-lock.json (npm) from being created
# If some case you are using pnpm or yarn, don't forget to generate npm lockfile
# before commiting your code by running:
# `npm i --package-lock-only`
pnpm-lock.yaml
yarn.lock

# Chart dependencies
**/charts/*.tgz


================================================
FILE: .husky/pre-commit
================================================
#!/bin/sh
npx lint-staged


================================================
FILE: .vscode/extensions.json
================================================
{
  "recommendations": [
    "dbaeumer.vscode-eslint"
  ]
}


================================================
FILE: .vscode/settings.json
================================================
{
  "editor.tabSize": 2,
  "editor.formatOnSave": true,
  "files.insertFinalNewline": true,
  "files.trimFinalNewlines": true,
  "files.trimTrailingWhitespace": true,
  "css.format.spaceAroundSelectorSeparator": true,
  "scss.format.spaceAroundSelectorSeparator": true,
  "eslint.format.enable": true,
  "eslint.workingDirectories": [
    "./client",
    "./server"
  ],
  "[javascript]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "dbaeumer.vscode-eslint"
  }
}


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
 advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
 address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
 professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [github@planka.group](mailto:github@planka.group).
All complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to PLANKA

First off, thanks for taking the time to contribute!

## Code of Conduct

This project and everyone participating in it is governed by the [PLANKA Code of Conduct](https://github.com/plankanban/planka/blob/master/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code.

## How Can I Contribute?

### Reporting Bugs

Feel free to create a bug report as a new issue on GitHub. Before creating, please check if there is already existing one. When creating a bug report, please include as many details as possible.

### Suggesting Enhancements

Feel free to create an enhancement suggestion as a new issue on GitHub. Before creating, please check if there is already existing one. When creating an enhancement suggestion, please include as many details as possible.

### Pull Requests

Before submitting a pull request please discuss with the core team by creating or commenting in an issue on GitHub – we'd also love to hear from you in the discussions. This way we can ensure that an approach is agreed on before code is written. This will result in a much higher liklihood of your code being accepted.

If you’re looking for ways to get started, here's a list of ways to help us improve PLANKA:

- [Translation](https://github.com/plankanban/planka/issues/66) into other languages
- Issues with [`good first issue`](https://github.com/plankanban/planka/labels/good%20first%20issue) label
- Performance improvements, both on client and server
- Developer happiness and documentation
- Bugs and other issues listed on GitHub

## Styleguides

### Git Commit Messages

Commit messages should follow the [commit message convention](https://conventionalcommits.org), so changelogs could be generated automatically by that.

Additional rules:

- Separate subject from body with a blank line
- Limit the subject line to 70 characters
- Capitalize the subject line
- Do not end the subject line with a period
- Use the imperative mood in the subject line
- Use the body to explain what and why vs. how
- Each commit should be a single, stable change

### JavaScript

All JavaScript code should follow this [JavaScript style guide](https://github.com/airbnb/javascript). The pre-commit hook will help you find linting errors before committing.


================================================
FILE: CONTRIBUTOR_LICENSE_AGREEMENT.md
================================================
# PLANKA Contributor License Agreement

I give PLANKA Software GmbH permission to license my contributions on any terms they like. I am giving them this license in order to make it possible for them to accept my contributions into their project.

AS FAR AS THE LAW ALLOWS, MY CONTRIBUTIONS COME AS IS, WITHOUT ANY WARRANTY OR CONDITION, AND I WILL NOT BE LIABLE TO ANYONE FOR ANY DAMAGES RELATED TO THIS SOFTWARE OR THIS LICENSE, UNDER ANY KIND OF LEGAL CLAIM.


================================================
FILE: Dockerfile
================================================
# Stage 1: Server build
FROM node:22-alpine AS server

RUN apk -U upgrade \
  && apk add build-base python3 --no-cache

WORKDIR /app

COPY server .

RUN npm install npm --global \
  && npm install \
  && npm run build \
  && npm prune --production

# Stage 2: Client build
FROM node:22 AS client

WORKDIR /app

COPY client .

RUN npm install npm --global \
  && npm install --omit=dev \
  && INDEX_FORMAT=ejs DISABLE_ESLINT_PLUGIN=true npm run build

# Stage 3: Final image
FROM node:22-alpine

RUN apk -U upgrade \
  && apk add bash python3 squid --no-cache \
  && npm install npm --global

USER node
WORKDIR /app

COPY --chown=node:node LICENSE.md .
COPY --chown=node:node ["LICENSES/PLANKA Community License DE.md", "LICENSE_DE.md"]

COPY --from=server --chown=node:node /app/node_modules node_modules
COPY --from=server --chown=node:node /app/dist .

COPY --from=client --chown=node:node /app/dist public

RUN python3 -m venv .venv \
  && .venv/bin/pip3 install --upgrade pip \
  && .venv/bin/pip3 install -r requirements.txt --no-cache-dir \
  && mv .env.sample .env \
  && mv public/index.ejs views \
  && npm config set update-notifier false

VOLUME /app/data
EXPOSE 1337

HEALTHCHECK --interval=10s --timeout=2s --start-period=15s \
  CMD node ./healthcheck.js

CMD ["./start.sh"]


================================================
FILE: Dockerfile.dev
================================================
FROM node:22-alpine

RUN apk -U upgrade \
  && apk add bash build-base python3 xdg-utils --no-cache \
  && npm install npm --global

WORKDIR /app


================================================
FILE: LICENSE.md
================================================
**PLANKA Community License**

Version 1.1 - Last updated: May 20, 2025

Related files in English:

- PLANKA Community License EN.md (this file)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Related files in German:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

# PLANKA Community License

Files accessible to and marked for community use are licensed as follows:

- Content of branches other than the main branch (usually "master" or "main") is not licensed.
- Source code files or other files that contain ".pe." (for "PLANKA Pro/Enterprise") in their file names or folder names or are otherwise marked as "PLANKA Pro/Enterprise" in their file headers or folders are NOT licensed under the "Fair Use License". These files are "PLANKA Pro/Enterprise" files and are licensed under the "PLANKA Pro/Enterprise License".
- To use any "PLANKA Pro/Enterprise" files or sources, you must own a valid "PLANKA Pro/Enterprise License". You can read more about our commercial license in the [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md).
- All third-party components incorporated into our software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above-mentioned files or restrictions is available under the "Fair Use License" as defined below.

## Fair Use License

Version 1.1

### Acceptance

By using the software, you agree to all of the terms and conditions above and below.

### Copyright License

The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations below.

### Trademark

"You may use the PLANKA name or logo only to describe that your service incorporates the software. Any other trademark use (e.g., in product names, domains, or marketing material) requires our prior written consent."

### Permitted Use

You may use or modify PLANKA (a) for personal, hobby, or educational purposes, (b) internally within your own organization, (c) for private hosting for a typical number of friends, family, or personal projects, (d) to provide free access to non-profit organizations (as recognized by applicable tax authorities), or if you are a recognized non-profit organization yourself involving external users into your mission, and (e) public educational institutions for academic/research purposes only.

### Restricted Use

Sharing accounts/credentials with third parties for business purposes or operating PLANKA as a hosted service for third parties for any commercial gain whatsoever is prohibited. Commercial gain includes any form of payment, advertising revenue, data monetization, or indirect commercial benefit or business advantage.

For all other PLANKA-based hosting services or shared use of PLANKA accounts across organizations, you need to buy a commercial PLANKA license.

### Limitations

You may not alter, remove, or obscure any licensing, copyright, or other notices from the software provided by the licensor. Any use of the licensor's trademarks is subject to applicable law.

### Patents

The licensor grants you a license, under any patent claims the licensor can license or becomes able to license, to make, have made, use, sell, offer for sale, import, and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company and everyone connected to your company.

### Notices

You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms. If you modify the software, you must include in any modified copies of the software a prominent notice stating that you have modified the software.

### No Other Rights

These terms do not imply any licenses other than those expressly granted in these terms.

### Termination

If you use the software in violation of these terms, such use is not licensed, and your license will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your license will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your license to terminate automatically and permanently.

### Violation

Violation of our restricted use clauses will constitute a material breach of terms. PLANKA Software GmbH reserves the right to immediately terminate your access to its services and to pursue all available legal and equitable remedies.

### No Liability

As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim. Additionally, we are not responsible for bugs and mistakes in any third-party submodule or their referring license definition. If you find something problematic, please report it to us.

### Definitions

The "licensor" is the entity offering these terms.

The "software" is the software the licensor makes available under these terms, including any portion of it.

"You" refers to the individual or entity agreeing to these terms.

"Your company" is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. Control means ownership of substantially all the assets of an entity.

"Your license" is the license granted to you for the software under these terms.

"Use" means anything you do with the software requiring your license.

"Trademark" means trademarks, service marks, and similar rights.


================================================
FILE: LICENSES/PLANKA Commercial License DE.md
================================================
**PLANKA Commercial License**

Version 1.2 - Zuletzt aktualisiert: 28. Nov 2025

Zugehörige Dateien in Englisch:

- [PLANKA Community License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Zugehörige Dateien in Deutsch:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- PLANKA Commercial License DE.md (diese Datei)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

# PLANKA Pro/Enterprise-Lizenz

Copyright (c) 2025 bis heute von PLANKA Software GmbH.

Unsere Software und zugehörige Dokumentationsdateien (die "Software") dürfen nur dann produktiv genutzt werden, wenn Sie (und jede juristische Person, die Sie vertreten) eine gültige "PLANKA Pro/Enterprise-Lizenz" besitzen, die Ihrer Nutzung entspricht. Sie stimmen zu, dass die PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen und/oder Patches behalten, und alle solche Modifikationen und/oder Patches dürfen nur mit einer gültigen "PLANKA Pro/Enterprise-Lizenz" für die entsprechende Nutzung verwendet, kopiert, modifiziert, angezeigt, verteilt oder anderweitig genutzt werden. Ungeachtet des Vorstehenden dürfen Sie die Software für Entwicklungs- und Testzwecke ohne Abonnement kopieren und modifizieren. Sie stimmen zu, dass PLANKA Software GmbH und/oder ihre Lizenzgeber (falls zutreffend) alle Rechte, Titel und Ansprüche an und auf alle solche Modifikationen behalten. Es werden Ihnen keine anderen Rechte gewährt als die, die hier ausdrücklich genannt sind. Vorbehaltlich des Vorstehenden ist es verboten, die Software zu kopieren, zusammenzuführen, zu veröffentlichen, zu verteilen, zu unterlizenzieren und/oder zu verkaufen.

#### Komponenten von Drittanbietern

Für alle Komponenten von Drittanbietern, die in unsere Software integriert sind, werden diese Komponenten unter der ursprünglichen Lizenz lizenziert, die vom Eigentümer der jeweiligen Komponente bereitgestellt wird.

## PLANKA Pro/Enterprise Repositories

Nach dem Erwerb einer "PLANKA Pro/Enterprise-Lizenz" erhalten Sie Zugang zu unseren "PLANKA Pro/Enterprise"-Repositories. Dort finden Sie unsere neuesten stabilen Builds, die umfangreiche Tests durchlaufen haben und als produktionsreif gelten.

Wichtiger Hinweis zum Zugriffsumfang: Der Standardzugang umfasst ausschließlich die vorkompilierten Versionen unserer Software. Zugang zum Quellcode wird nur in Ausnahmefällen und nach gesonderter Vereinbarung mit PLANKA Software GmbH gewährt.

Unabhängig vom Zugriffsumfang gilt: Die Weitergabe von Dateien, Quellcode oder Teilen davon aus unseren "PLANKA Pro/Enterprise"-Repositories an Dritte, die nicht zugriffsberechtigt sind, ist ohne vorherige schriftliche Genehmigung von PLANKA Software GmbH untersagt.

## Eingeschränkte Garantie

UNSERE SOFTWARE WIRD "WIE SIE IST" BEREITGESTELLT, OHNE JEGLICHE GARANTIE, AUSDRÜCKLICH ODER IMPLIZIERT, EINSCHLIEßLICH, ABER NICHT BESCHRÄNKT AUF DIE GARANTIEN DER MARKTGÄNGIGKEIT, EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND NICHTVERLETZUNG VON RECHTEN DRITTER. IN KEINEM FALL HAFTEN DIE AUTOREN ODER URHEBERRECHTSINHABER FÜR ANSPRÜCHE, SCHÄDEN ODER ANDERE HAFTUNG, OB AUS VERTRAG, UNERLAUBTER HANDLUNG ODER ANDERWEITIG, DIE SICH AUS, AUS ODER IN VERBINDUNG MIT DER SOFTWARE ODER DER NUTZUNG ODER ANDEREN GESCHÄFTEN MIT DER SOFTWARE ERGEBEN.


================================================
FILE: LICENSES/PLANKA Commercial License EN.md
================================================
**PLANKA Commercial License**

Version 1.2 - Last updated: Nov 28, 2025

Related files in English:

- [PLANKA Community License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md)
- PLANKA Commercial License EN.md (this file)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Related files in German:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

# PLANKA Pro/Enterprise License

Copyright (c) 2025 to present by PLANKA Software GmbH.

Our software and associated documentation files (the "Software") may only be used in production if you (and any entity that you represent) hold a valid "PLANKA Pro/Enterprise License" corresponding to your usage. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid "PLANKA Pro/Enterprise License" for the corresponding usage. Notwithstanding the foregoing, you may copy and modify the Software for development and testing purposes without requiring a subscription. You agree that PLANKA Software GmbH and/or its licensors (as applicable) retain all right, title, and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense, and/or sell the Software.

#### Third-Party Components

For all third-party components incorporated into our Software, those components are licensed under the original license provided by the owner of the applicable component.

## PLANKA Pro/Enterprise Repositories

After purchasing a "PLANKA Pro/Enterprise License", you will receive access to our "PLANKA Pro/Enterprise" repositories. There you will find our latest stable builds, which have undergone extensive testing and are considered production-ready.

Important note on access scope: Standard access includes only the precompiled versions of our software. Access to the source code is granted only in exceptional cases and requires a separate agreement with PLANKA Software GmbH.

Regardless of access scope: The distribution of files, source code, or any parts thereof from our "PLANKA Pro/Enterprise" repositories to third parties who are not authorized for access is prohibited without prior written permission from PLANKA Software GmbH.

## Limited Warranty

OUR SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: LICENSES/PLANKA Community License DE.md
================================================
**PLANKA Community License**

Version 1.1 - Zuletzt aktualisiert: 20. Mai 2025

Zugehörige Dateien in Englisch:

- [PLANKA Community License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Zugehörige Dateien in Deutsch:

- PLANKA Community License DE.md (diese Datei)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

# PLANKA Community-Lizenz

Für die Gemeinschaft zugängliche und gekennzeichnete Dateien sind wie folgt lizenziert:

- Inhalte von Sourcecode-Branches außer dem Hauptbranch (üblicherweise "master" oder "main") sind nicht lizenziert.
- Quellcode-Dateien oder andere Dateien, die ".pe." (für "PLANKA Pro/Enterprise") in ihren Datei- oder Ordnernamen enthalten oder anderweitig durch "PLANKA Pro/Enterprise" in ihren Dateiköpfen oder Ordnern gekennzeichnet sind, sind NICHT unter der "Fair Use Lizenz" lizenziert. Diese Dateien sind "PLANKA Pro/Enterprise"-Dateien und sind unter der "PLANKA Pro/Enterprise-Lizenz" lizenziert.
- Um "PLANKA Pro/Enterprise"-Dateien oder Quellen zu nutzen, müssen Sie eine gültige "PLANKA Pro/Enterprise-Lizenz" besitzen. Sie können mehr über unsere kommerzielle Lizenz in der [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md) lesen.
- Alle Komponenten von Drittanbietern, die in unsere Software integriert sind, sind unter der ursprünglichen Lizenz lizenziert, die vom Eigentümer der jeweiligen Komponente bereitgestellt wird.
- Inhalte außerhalb der oben genannten Dateien oder Einschränkungen sind unter der "Fair Use Lizenz" verfügbar, wie unten definiert.

## Fair Use Lizenz

Version 1.1

### Annahme

Durch die Nutzung der Software stimmen Sie allen oben und unten aufgeführten Bedingungen zu.

### Urheberrechtslizenz

Der Lizenzgeber gewährt Ihnen eine nicht-exklusive, gebührenfreie, weltweite, nicht unterlizenzierbare, nicht übertragbare Lizenz zur Nutzung, Kopie, Verteilung, Verfügbarmachung und Erstellung abgeleiteter Werke der Software, in jedem Fall vorbehaltlich der unten genannten Einschränkungen.

### Marke

"Sie dürfen den Namen oder das Logo von PLANKA nur verwenden, um zu beschreiben, dass Ihr Dienst die Software enthält. Jede andere Markennutzung (z.B. in Produktnamen, Domains oder Marketingmaterial) bedarf unserer vorherigen schriftlichen Zustimmung."

### Erlaubte Nutzung

Sie dürfen PLANKA nutzen oder modifizieren (a) für persönliche, Hobby- oder Bildungszwecke, (b) intern innerhalb Ihrer eigenen Organisation, (c) für privates Hosting für eine typische Anzahl von Freunden, Familie oder persönlichen Projekten, (d) um gemeinnützigen Organisationen (wie von den jeweiligen Steuerbehörden anerkannt) kostenlosen Zugang zu gewähren, oder wenn Sie selbst eine anerkannte gemeinnützige Organisation sind, die externe Nutzer in Ihre Mission einbezieht, und (e) öffentliche Bildungseinrichtungen ausschließlich für akademische/Forschungszwecke.

### Eingeschränkte Nutzung

Das Teilen von Konten/Zugangsdaten mit Dritten für geschäftliche Zwecke oder der Betrieb von PLANKA als gehosteter Dienst für Dritte zu jeglichen kommerziellen Gewinn ist untersagt. Kommerzieller Gewinn umfasst jede Form von Zahlung, Werbeeinnahmen, Datenmonetarisierung oder indirekten kommerziellen Nutzen oder Geschäftsvorteil.

Für alle anderen PLANKA-basierten Hosting-Dienste oder die gemeinsame Nutzung von PLANKA-Konten zwischen Organisationen müssen Sie eine kommerzielle PLANKA-Lizenz erwerben.

### Einschränkungen

Sie dürfen keine vom Lizenzgeber bereitgestellten Lizenz-, Urheber- oder anderen Hinweise in der Software verändern, entfernen oder verschleiern. Jede Nutzung der Marken des Lizenzgebers unterliegt dem geltenden Recht.

### Patente

Der Lizenzgeber gewährt Ihnen eine Lizenz unter allen Patentansprüchen, die der Lizenzgeber lizenzieren kann oder lizenzieren können wird, um die Software herzustellen, herstellen zu lassen, zu nutzen, zu verkaufen, zum Verkauf anzubieten, zu importieren und importieren zu lassen, jeweils vorbehaltlich der Einschränkungen und Bedingungen in dieser Lizenz. Diese Lizenz erstreckt sich nicht auf Patentansprüche, die Sie durch Modifikationen oder Ergänzungen der Software verletzen lassen. Wenn Sie oder Ihr Unternehmen einen schriftlichen Anspruch geltend machen, dass die Software ein Patent verletzt oder zur Verletzung beiträgt, endet Ihre unter diesen Bedingungen gewährte Patentlizenz für die Software sofort. Wenn Ihr Unternehmen einen solchen Anspruch geltend macht, endet Ihre Patentlizenz sofort für Arbeiten im Auftrag Ihres Unternehmens und für alle mit Ihrem Unternehmen verbundenen Personen.

### Hinweise

Sie müssen sicherstellen, dass jeder, der eine Kopie eines Teils der Software von Ihnen erhält, auch eine Kopie dieser Bedingungen erhält. Wenn Sie die Software modifizieren, müssen Sie in allen modifizierten Kopien der Software einen auffälligen Hinweis aufnehmen, der besagt, dass Sie die Software modifiziert haben.

### Keine weiteren Rechte

Diese Bedingungen implizieren keine anderen Lizenzen als die, die in diesen Bedingungen ausdrücklich gewährt werden.

### Kündigung

Wenn Sie die Software unter Verletzung dieser Bedingungen nutzen, ist eine solche Nutzung nicht lizenziert, und Ihre Lizenz wird automatisch gekündigt. Wenn der Lizenzgeber Ihnen eine Mitteilung über Ihre Verletzung zukommen lässt und Sie alle Verletzungen dieser Lizenz spätestens 30 Tage nach Erhalt dieser Mitteilung einstellen, wird Ihre Lizenz rückwirkend wiederhergestellt. Wenn Sie jedoch nach einer solchen Wiederherstellung gegen diese Bedingungen verstoßen, führt jeder weitere Verstoß gegen diese Bedingungen dazu, dass Ihre Lizenz automatisch und dauerhaft gekündigt wird.

### Verletzung

Die Verletzung unserer Nutzungsbeschränkungsklauseln stellt eine wesentliche Vertragsverletzung dar. Die PLANKA Software GmbH behält sich das Recht vor, Ihren Zugang zu seinen Diensten sofort zu beenden und alle verfügbaren rechtlichen und durchsetzbaren Rechtsmittel zu verfolgen.

### Keine Haftung

Soweit es das Gesetz erlaubt, wird die Software wie sie ist, ohne jegliche Garantie oder Bedingung geliefert, und der Lizenzgeber haftet Ihnen gegenüber nicht für Schäden, die sich aus diesen Bedingungen oder der Nutzung oder Art der Software ergeben, unter keiner Art von Rechtsanspruch. Darüber hinaus sind wir nicht verantwortlich für Fehler und Irrtümer in Submodulen von Drittanbietern oder deren jeweiligen Lizenzdefinitionen. Wenn Sie etwas Problematisches finden, melden Sie es uns bitte.

### Definitionen

Der "Lizenzgeber" ist die juristische Person, die diese Bedingungen anbietet.

Die "Software" ist die Software, die der Lizenzgeber unter diesen Bedingungen verfügbar macht, einschließlich einzelner Teile davon.

"Sie" bezieht sich auf die natürliche oder juristische Person, die diesen Bedingungen zustimmt.

"Ihr Unternehmen" ist jede juristische Person, Einzelunternehmen oder eine andere Art von Organisation, für die Sie arbeiten, sowie alle Organisationen, die die Kontrolle über diese Organisation haben, unter der Kontrolle dieser Organisation stehen oder unter gemeinsamer Kontrolle mit dieser Organisation stehen. Kontrolle bedeutet Eigentum an im Wesentlichen allen Vermögenswerten einer Einheit.

"Ihre Lizenz" ist die Lizenz, die Ihnen unter diesen Bedingungen für die Software gewährt wird.

"Nutzung" bedeutet alles, was Sie mit der Software tun, wofür Ihre Lizenz erforderlich ist.

"Marke" bedeutet Marken, Dienstleistungsmarken und ähnliche Rechte.


================================================
FILE: LICENSES/PLANKA Community License EN.md
================================================
**PLANKA Community License**

Version 1.1 - Last updated: May 20, 2025

Related files in English:

- PLANKA Community License EN.md (this file)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Related files in German:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

# PLANKA Community License

Files accessible to and marked for community use are licensed as follows:

- Content of branches other than the main branch (usually "master" or "main") is not licensed.
- Source code files or other files that contain ".pe." (for "PLANKA Pro/Enterprise") in their file names or folder names or are otherwise marked as "PLANKA Pro/Enterprise" in their file headers or folders are NOT licensed under the "Fair Use License". These files are "PLANKA Pro/Enterprise" files and are licensed under the "PLANKA Pro/Enterprise License".
- To use any "PLANKA Pro/Enterprise" files or sources, you must own a valid "PLANKA Pro/Enterprise License". You can read more about our commercial license in the [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md).
- All third-party components incorporated into our software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above-mentioned files or restrictions is available under the "Fair Use License" as defined below.

## Fair Use License

Version 1.1

### Acceptance

By using the software, you agree to all of the terms and conditions above and below.

### Copyright License

The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations below.

### Trademark

"You may use the PLANKA name or logo only to describe that your service incorporates the software. Any other trademark use (e.g., in product names, domains, or marketing material) requires our prior written consent."

### Permitted Use

You may use or modify PLANKA (a) for personal, hobby, or educational purposes, (b) internally within your own organization, (c) for private hosting for a typical number of friends, family, or personal projects, (d) to provide free access to non-profit organizations (as recognized by applicable tax authorities), or if you are a recognized non-profit organization yourself involving external users into your mission, and (e) public educational institutions for academic/research purposes only.

### Restricted Use

Sharing accounts/credentials with third parties for business purposes or operating PLANKA as a hosted service for third parties for any commercial gain whatsoever is prohibited. Commercial gain includes any form of payment, advertising revenue, data monetization, or indirect commercial benefit or business advantage.

For all other PLANKA-based hosting services or shared use of PLANKA accounts across organizations, you need to buy a commercial PLANKA license.

### Limitations

You may not alter, remove, or obscure any licensing, copyright, or other notices from the software provided by the licensor. Any use of the licensor's trademarks is subject to applicable law.

### Patents

The licensor grants you a license, under any patent claims the licensor can license or becomes able to license, to make, have made, use, sell, offer for sale, import, and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company and everyone connected to your company.

### Notices

You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms. If you modify the software, you must include in any modified copies of the software a prominent notice stating that you have modified the software.

### No Other Rights

These terms do not imply any licenses other than those expressly granted in these terms.

### Termination

If you use the software in violation of these terms, such use is not licensed, and your license will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your license will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your license to terminate automatically and permanently.

### Violation

Violation of our restricted use clauses will constitute a material breach of terms. PLANKA Software GmbH reserves the right to immediately terminate your access to its services and to pursue all available legal and equitable remedies.

### No Liability

As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim. Additionally, we are not responsible for bugs and mistakes in any third-party submodule or their referring license definition. If you find something problematic, please report it to us.

### Definitions

The "licensor" is the entity offering these terms.

The "software" is the software the licensor makes available under these terms, including any portion of it.

"You" refers to the individual or entity agreeing to these terms.

"Your company" is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. Control means ownership of substantially all the assets of an entity.

"Your license" is the license granted to you for the software under these terms.

"Use" means anything you do with the software requiring your license.

"Trademark" means trademarks, service marks, and similar rights.


================================================
FILE: LICENSES/PLANKA License Guide DE.md
================================================
**PLANKA License Guide**

Version 1.1 - Zuletzt aktualisiert: 20. Mai 2025

Zugehörige Dateien in Englisch:

- [PLANKA Community License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- [PLANKA License Guide EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md)

Zugehörige Dateien in Deutsch:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- PLANKA License Guide DE.md (diese Datei)

---

## PLANKAs "Fair Use Lizenz" und die "PLANKA Pro/Enterprise-Lizenz"

Unsere [Fair Use Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md) und unsere [PLANKA Pro/Enterprise-Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md) basieren auf dem [fair-code](http://faircode.io)-Modell.

### Spezielle Lizenzierung für Bildungseinrichtungen und gemeinnützige Organisationen

Bildungseinrichtungen und gemeinnützige Organisationen, die kommerzielle Funktionen oder eine Nutzung über unsere "Fair Use Lizenz" hinaus benötigen, sind eingeladen, uns für maßgeschneiderte Lizenzlösungen und Bildungspreise zu kontaktieren. Wir freuen uns darauf, Ihrer Organisation dabei zu helfen, ihre Mission zu erfüllen!

# Lizenz-FAQs

### Unter welcher Lizenz wird PLANKA angeboten?

PLANKA verwendet die [Fair Use Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md) und die [PLANKA Pro/Enterprise-Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md). Diese Lizenzen basieren auf dem [fair-code](http://faircode.io)-Modell.

### Welcher Quellcode ist durch PLANKAs "Fair Use Lizenz" abgedeckt?

Die [Fair Use Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md) gilt für unseren Quellcode, der in unserem [GitHub-Hauptrepository](https://github.com/plankanban/planka) gehostet wird, mit folgenden Ausnahmen:

- Inhalte von Branches außer dem Hauptbranch (üblicherweise "master" oder "main").

- Quellcode-Dateien oder andere Dateien, die ".pe." (für "PLANKA Pro/Enterprise") in ihren Datei- oder Ordnernamen enthalten.

- Quellcode-Dateien, die in den Dateien oder Ordnern als "PLANKA Pro/Enterprise" gekennzeichnet sind.

- Quellcode in Ordnern, die separate Lizenzdateien enthalten, die sie eindeutig als "PLANKA Pro/Enterprise" kennzeichnen.

Diese Ausnahmen sind unter der [PLANKA Pro/Enterprise-Lizenz](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md) lizenziert.

### Was genau ist die "Fair Use Lizenz"?

Die "Fair Use Lizenz" fällt in die Kategorie der sogenannten [fair-code](http://faircode.io)-Lizenzen. PLANKAs Lizenz basiert auf und erweitert die "Sustainable Use License", die von [n8n](https://n8n.io) eingeführt wurde und für deren Beratung wir sehr dankbar sind. Mit ähnlichen Zielen vor Augen haben wir beschlossen, deren Beispiel zu folgen und dieses Lizenz-Modell für unsere eigenen Bedürfnisse anzupassen. [Weiter unten](#warum-haben-sie-diese-lizenz-gewählt) können Sie nachlesen, warum wir diese Lizenzart gewählt haben.

Die Lizenz gewährt Ihnen das kostenlose Recht, die Software zu nutzen, zu modifizieren, abgeleitete Werke zu erstellen und weiterzugeben unter folgenden Bedingungen:

- Sie dürfen PLANKA nutzen oder modifizieren (a) für persönliche, Hobby- oder Bildungszwecke, (b) intern innerhalb Ihrer eigenen Organisation, (c) für privates Hosting für eine typische Anzahl von Freunden, Familie oder persönlichen Projekten, (d) um gemeinnützigen Organisationen (wie von den jeweiligen Steuerbehörden anerkannt) kostenlosen Zugang zu gewähren, oder wenn Sie selbst eine anerkannte gemeinnützige Organisation sind, die externe Nutzer in Ihre Mission einbezieht, und (e) öffentliche Bildungseinrichtungen ausschließlich für akademische/Forschungszwecke.

- Das Teilen von Konten/Zugangsdaten mit Dritten für geschäftliche Zwecke oder der Betrieb von PLANKA als gehosteter Dienst für Dritte zu jeglichen kommerziellen Gewinn ist untersagt. Kommerzieller Gewinn umfasst jede Form von Zahlung, Werbeeinnahmen, Datenmonetarisierung oder indirekten kommerziellen Nutzen oder Geschäftsvorteil.

- Sie dürfen keine vom Lizenzgeber bereitgestellten Lizenz-, Urheber- oder anderen Hinweise in der Software verändern, entfernen oder verschleiern. Jede Nutzung der Marken des Lizenzgebers unterliegt dem geltenden Recht.

### Was ist im Rahmen der Lizenz im Kontext von PLANKAs Produkten erlaubt und was NICHT?

Unsere Lizenz erlaubt die Nutzung für "interne Geschäftszwecke" sowie für persönliche, Hobby-, Bildungs- und begrenzte private Hosting-Szenarien. Sie verbietet jedoch die Nutzung von PLANKA für jede Form des kommerziellen Gewinns, wie z.B. den Verkauf eines Produkts oder einer Dienstleistung, bei dem der Wert in irgendeiner Form von PLANKAs Funktionalität abgeleitet wird, den Betrieb als kostenpflichtiger gehosteter Dienst, die Monetarisierung von Zugang oder Benutzerdaten oder die Erzielung anderer indirekter kommerzieller Vorteile.

##### Hier sind einige Beispiele, die nicht erlaubt wären:

- PLANKA unter einem White-Label anzubieten und es Ihren Kunden oder Partnern gegen Geld anzubieten.

- PLANKA zu hosten und Dritten für den Zugriff Geld berechnen.

- PLANKAs API für Dienste zu nutzen, für die Geld verlangt wird.

- Die Verwendung von PLANKA, um Ihre eigene juristische Person mit juristischen Personen oder Personen außerhalb Ihrer Organisation im Kontext einer kommerziellen Beziehung zu verbinden.

- PLANKA zur Durchführung oder Unterstützung illegaler oder rechtswidriger Aktivitäten zu verwenden.

##### Die folgenden Beispiele sind unter unserer Lizenz erlaubt:

- Verwendung von PLANKA zur Kontrolle Ihrer internen Prozesse und Verwaltung Ihrer internen Projekte.

- Integration von PLANKA in andere intern genutzte Produkte, um deren Fähigkeiten zu erweitern.

- Bereitstellung von Beratungs- oder öffentlichen Bildungsdienstleistungen im Zusammenhang mit PLANKA, zum Beispiel zum Aufbau oder zur Integration von Arbeitsabläufen für oder in Verbindung mit PLANKA oder zur Entwicklung benutzerdefinierter Module zur Erweiterung seiner Funktionalitäten.

- Unterstützung von PLANKA, zum Beispiel durch Einrichtung oder Wartung auf einem internen Firmenserver.

### Ist es erlaubt, PLANKA als Backend-Integration zu nutzen?

Wenn Sie PLANKA und seine Backend-Dienste über interne Betriebsabläufe innerhalb Ihrer eigenen Organisation hinaus nutzen, indem Sie Dritten Zugang für kommerziellen Gewinn ermöglichen, wie z.B. das Teilen von Konten mit Kunden oder den Verkauf eines Produkts oder einer Dienstleistung, bei dem der Wert in irgendeiner Form von PLANKAs Funktionalität abgeleitet wird, den Betrieb als kostenpflichtiger gehosteter Dienst, die Monetarisierung von Zugang oder Benutzerdaten oder die Erzielung anderer indirekter kommerzieller Vorteile, sind Sie verpflichtet, eine "PLANKA Pro/Enterprise-Lizenz" zu erwerben.

Dies umfasst Szenarien, in denen PLANKA als Kerninfrastruktur für Drittanbieterlösungen dient, von Benutzern außerhalb Ihrer juristischen Person als Teil eines kostenpflichtigen oder kommerziellen Angebots genutzt wird oder bei denen Sie PLANKA unterlizenzieren, neu verpacken oder anderweitig externen Parteien zur Verfügung stellen. Jede Vereinbarung, die die Integration von PLANKA in ein anderes Produkt beinhaltet, um als primärer Betriebsmotor für dieses Produkt zu dienen, erfordert ebenfalls eine gültige "PLANKA Pro/Enterprise-Lizenz" oder proprietäre Lizenz, die für unsere Unternehmenskunden verfügbar ist.

### Können Sie mir einige schnelle Beispiele geben, um kostenlose Nutzung vs. Unternehmensnutzung zu verdeutlichen?

---

##### Beispiel 1: Verwendung der PLANKA-API zur Steuerung oder Reaktion auf Fertigungsprozess-Ereignissen

Nutzen Sie unsere API, um das Feedback aus Fertigungsschritten in Ihrem Unternehmen anzuzeigen und zu steuern oder um Produktionslinien-Ereignisse durch das Verschieben von Karten zu steuern.

**ERLAUBT** unter der "Fair Use Lizenz". Sie können PLANKA in Ihre anderen Systeme integrieren, um seine API zur Steuerung Ihrer internen Prozesse zu nutzen.

##### Beispiel 2: Kommerzielle Beratungs- oder Supportdienste anbieten

Sie bieten Ihrem Kunden einen Dienst an, der ihm hilft, neue Arbeitsabläufe und Board-Konzepte in einer PLANKA-Instanz zu implementieren.

**ERLAUBT** unter der "Fair Use Lizenz". Sie können frei kommerzielle Beratungs- oder Integrations- und Supportdienste für PLANKA anbieten, ohne dass eine separate Lizenzvereinbarung mit uns erforderlich ist.

##### Beispiel 3: PLANKA in einem kostenlosen und öffentlichen Docker-Image bündeln

Eine Schule oder gemeinnützige Organisation bündelt PLANKA in einem kostenlosen, öffentlichen Docker-Image für Studenten, die PLANKA zur Organisation ihrer Studienbereiche nutzen möchten.

**ERLAUBT** unter der "Fair Use Lizenz". Da PLANKA kostenlos und ohne kommerzielle Einnahmen im Sinn weitergegeben wird, sind Sie mehr als willkommen, gemeinnützigen Einrichtungen und Schulen einen besseren Zugang zu PLANKA zu ermöglichen.

**JEDOCH** wenn Sie auch planen, Schülern und Studenten Zugang zu einer ansonsten schulinternen PLANKA-Instanz zu gewähren, würde dies unsere "PLANKA-Bildungslizenz" erfordern, die wir sehr gerne auf Anfrage anbieten.

##### Beispiel 4: PLANKA-Logins für Kunden und Partner bereitstellen

Um ein besseres Projekt-Feedback zu ermöglichen, bieten Sie Ihrem Kunden und jemandem aus einem Joint Venture, mit denen Sie beide eine kommerzielle Beziehung haben, Zugang zu Projektboards innerhalb Ihres Unternehmens an. Sie können nun z.B. Karten kommentieren und auch von PLANKAs Echtzeit-Update-Funktionen profitieren.

**NICHT ERLAUBT** unter der "Fair Use Lizenz". Das Anbieten von PLANKA als Teil eines kostenpflichtigen Dienstes für Dritte oder die Bereitstellung von PLANKA-Zugang für Drittbenutzer außerhalb Ihrer eigenen juristischen Person im Kontext einer kommerziellen Beziehung erfordert, dass Sie eine "PLANKA Pro/Enterprise-Lizenz" registrieren.

##### Beispiel 5: PLANKA als gehostetes Produkt für andere Unternehmen anbieten

Sie möchten Geld damit verdienen, indem Sie PLANKA Unternehmen, Freiberuflern und anderen Personen zur Verfügung stellen. Auf diese Weise haben sie einfachen Zugang zu Projektmanagement und -kontrolle von überall.

**NICHT ERLAUBT** unter der "Fair Use Lizenz". Der Verkauf PLANKA-basierter Dienste erfordert, dass Sie einer "PLANKA-Wiederverkäuferlizenz" zustimmen. Wir haben eine spezielle Hosting-Vereinbarung für diejenigen, die kostenpflichtige PLANKA-Dienste für Kunden anbieten oder einfach als Wiederverkäufer für unsere eigenen "PLANKA Corporate Hosting Services" fungieren möchten.

---

### Kurzübersicht der "Fair Use Lizenz"

- Persönliche, Bildungs-, Hobby- oder interne Geschäftsnutzung:

    Kostenlos nutzbar. Sie können PLANKA für sich selbst, Ihr Team oder Ihre Organisation für persönliche, Bildungs- oder interne operative Zwecke nutzen, modifizieren und hosten.

- Nutzung durch gemeinnützige Organisationen und öffentliche Bildungseinrichtungen:

    Kostenlos für akademische oder Forschungszwecke nutzbar, vorausgesetzt die Organisation qualifiziert sich als gemeinnützig unter den geltenden Steuergesetzen. Keine Anmeldung oder spezielle Lizenz erforderlich.

- Bereitstellung von Zugang für Dritte für kommerziellen Gewinn:

    Nicht erlaubt. Das Teilen von Konten oder Zugangsdaten mit Dritten für kommerziellen Gewinn ist unter der Lizenz verboten, auch wenn Sie nicht direkt für den Zugang berechnen.

- Hosting von PLANKA als kommerzieller Dienst:

    Nicht erlaubt. Sie dürfen PLANKA nicht als gehosteten Dienst anbieten oder in ein kostenpflichtiges Produkt oder eine Dienstleistung integrieren, bei dem der Wert wesentlich von PLANKA stammt. Dies umfasst Werbeeinnahmen, Datenmonetarisierung oder indirekten kommerziellen Nutzen oder Geschäftsvorteil.

### Was, wenn ich PLANKA für etwas nutzen möchte, das nicht durch die Lizenz erlaubt ist?

Um die Grenzen der selbst gehosteten Community-Version zu überschreiten, benötigen Sie eine "PLANKA Pro/Enterprise-Lizenz". Sobald Sie sich bei uns registrieren, werden die Beschränkungen der "Fair Use Lizenz" nicht nur rechtlich aufgehoben, sondern Sie erhalten auch Zugang zum vollständigen Enterprise-Funktionsumfang.

Alternativ können Sie diese Grenzen durch ein Abonnement einer gehosteten Version - entweder Community oder Pro/Enterprise - überschreiten. Dies ermöglicht beispielsweise die Bereitstellung von Konten für Ihre Kunden.

Wenn Sie PLANKA als Dienstleistung für andere Organisationen anbieten oder PLANKA in Ihre eigene Software integrieren möchten, müssen Sie eine separate Umsatzvereinbarung mit uns abschließen. Wir ermutigen aktiv Softwareintegratoren und technisches Personal, PLANKA in ihre anderen Produkte zu integrieren und zu verbinden und unsere umfangreiche API zu nutzen, um auf Prozesse innerhalb ihres Unternehmens zu reagieren, sie zu steuern und zu beherrschen; wir bitten sie lediglich, eine Vereinbarung zu unterzeichnen, die die Nutzungsbedingungen und die von PLANKA für die Nutzung des Produkts erforderlichen Lizenzgebühren festlegt. Über PLANKAs API kann es externe Systeme steuern und auf sie reagieren. Sie können [hier](https://docs.planka.cloud/docs/category/api-reference/) mehr darüber erfahren oder uns kontaktieren.

Wenn Sie sich nicht sicher sind, ob der von Ihnen ins Auge gefasste Anwendungsfall einen internen Geschäftszweck darstellt oder nicht, werfen Sie einen Blick auf die [Beispiele oben](#können-sie-mir-einige-schnelle-beispiele-geben-um-kostenlose-nutzung-vs-unternehmensnutzung-zu-verdeutlichen), und falls Sie immer noch unsicher sind, kontaktieren Sie uns bitte unter [license@planka.group](mailto:license@planka.group).

### Warum gibt es keine kommerzielle Lizenz für eine selbst-gehostete Community-Version um die "Fair Use License"-Beschränkungen aufzuheben?

Wir unterstützen die selbst-gehostete Community-Version über unsere GitHub- und Discord-Community-Kanäle, aber wir können nicht effizient direkten Telefon- oder E-Mail-Support für mehrere Versionen bereitstellen, während wir unseren Verpflichtungen gegenüber zahlenden Kunden nachkommen. Deshalb ist direkter Support ausschließlich Pro/Enterprise-Kunden vorbehalten.

Wir bieten jedoch professionelle Installationsdienste und monatliche Serviceverträge für die Einrichtung, Aktualisierung und Überwachung aller Versionen an, um Organisationen dabei zu helfen, das Beste aus ihrer PLANKA-Bereitstellung herauszuholen - bitte kontaktieren Sie uns unter [license@planka.group](mailto:license@planka.group).

### Warum nutzt PLANKA keine Standard-Open-Source-Lizenz?

Wir verbringen viel Zeit damit, ein einfaches, aber leistungsstarkes Tool zu erstellen, welches die Kontrolle und Beherrschung von Projekten zu einer angenehmen Erfahrung macht. Außerdem wollten wir, dass PLANKA so weit als möglich frei verfügbar ist, während wir gleichzeitig sicherstellen müssen, dass wir ein nachhaltiges und tragfähiges Geschäft aufbauen können. Indem wir unser Produkt kostenlos nutzbar, einfach verteilbar und quelloffen machten, helfen wir allen, auf das Produkt zuzugreifen. Indem wir als Unternehmen tätig sind, können wir langfristig neue Funktionen entwickeln und veröffentlichen, Fehler beheben und zuverlässige Software in großem Maßstab bereitstellen.

### Warum haben Sie diese Lizenz gewählt?

Wir glauben, dass die "Fair Use Lizenz" sowohl für die Gemeinschaft als auch für die Entwickler von Vorteil ist. Entwicklung ist ein kostspieliges Unterfangen, und eine Community-Version kostenlos weiterzugeben, ist ein Risiko, welches viele Unternehmen nicht überleben, ohne ihre Software oder ihr Unternehmen zu veräußern. Daher leben viele Open-Source-Unternehmen von Spenden oder Finanzinvestoren. Anstatt unsere Seele zu verkaufen, verkaufen wir Dienstleistungen und Softwarelizenzen. Auf diese Weise können wir weiter wachsen, programmieren und unsere Community unterstützen. Die kurze Antwort lautet also "Leben und leben lassen" - so denken wir über PLANKA.

Daher helfen wir dabei, [fair-code](https://faircode.io)-Software zu fördern, mit dem Ziel, sie zu einem bekannten Sammelbegriff zu machen, um Softwaremodelle wie unseres zu beschreiben. Um jegliche Reibung um unsere proprietäre Lizenz auf ein absolutes Minimum zu beschränken, konzentrieren wir uns auf zwei Dinge:

1. Klare Sprache und minimale Länge - die Lizenz ist in klarem, präzisem Deutsch (eine englische Version existiert ebenfalls) geschrieben, mit nur den unbedingt notwendigen Klauseln.

2. Förderung von fair-code - wir fördern aktiv das fair-code-Modell, damit die Menschen es als unkomplizierte, nachhaltige Möglichkeit erkennen, Software wie PLANKA zu teilen und zu verbessern.

### Mein Unternehmen hat eine Richtlinie gegen die Verwendung von Code, der die kommerzielle Nutzung einschränkt - kann ich PLANKA trotzdem nutzen?

Vorausgesetzt, Sie nutzen PLANKA für interne Geschäftszwecke und stellen PLANKA nicht Ihren Kunden oder Partnern zur Verfügung, so sollten Sie natürlich auch PLANKA nutzen können. Wenn Sie sich nicht sicher sind, ob der von Ihnen ins Auge gefasste Anwendungsfall einen internen Geschäftszweck darstellt oder nicht, werfen Sie einen Blick auf die [Beispiele oben](#können-sie-mir-einige-schnelle-beispiele-geben-um-kostenlose-nutzung-vs-unternehmensnutzung-zu-verdeutlichen), und wenn Sie immer noch unsicher sind, schreiben Sie uns eine E-Mail an [license@planka.group](mailto:license@planka.group).

### Was passiert mit Code, den ich zu PLANKA beitrage, in Bezug auf seine "Fair Use Lizenz"?

Jeder Code, den Sie auf GitHub beitragen, unterliegt GitHubs [Nutzungsbedingungen](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#d_user_generated_content). Einfach ausgedrückt bedeutet dies, dass Sie alles, was Sie beitragen, besitzen und dafür verantwortlich sind, dass Sie jedoch anderen GitHub-Benutzern bestimmte Rechte zur Nutzung dieses Codes einräumen. Wenn Sie Code zu einem Repository beitragen, das einen Hinweis auf eine Lizenz enthält, lizenzieren Sie den Code unter denselben Bedingungen.

PLANKA bittet jeden Mitwirkenden, unsere [Contributor License Agreement](https://github.com/plankanban/planka/blob/master/CONTRIBUTOR_LICENSE_AGREEMENT.md) zu unterzeichnen. Zusätzlich zu den oben genannten Punkten gibt dies PLANKA die Möglichkeit, seine Lizenz zu ändern, ohne zusätzliche Genehmigung einzuholen. Es bedeutet auch, dass Sie nicht für Ihre Beiträge haftbar sind (z.B. falls sie den Geschäftsbetrieb einer anderen Person schädigen sollten).

Es ist einfach, mit den Code-Beiträgen zu PLANKA auf [GitHub](https://github.com/plankanban) zu beginnen, und wir haben weitere Möglichkeiten zur Teilnahme an unserer Community [hier](https://github.com/plankanban/planka/blob/master/CONTRIBUTING.md) aufgelistet.

### Ist PLANKA Open Source?

PLANKAs Quellcode ist unter der "Fair Use Lizenz" frei verfügbar. Während dies nicht mit der strengen Definition der Open Source Initiative übereinstimmt (die keine Nutzungsbeschränkungen erlaubt), bietet PLANKA den meisten Benutzern, einschließlich Unternehmen, dennoch fast die gleichen Vorteile wie traditionelle Open-Source-Software.

Wir befürworten, was oft als 'fair-code'-Modell bezeichnet wird - unser Code ist quelloffen und folgt einer einfachen "Leben und leben lassen"-Philosophie. Dieser Ansatz ermöglicht es uns, ein nachhaltiges Unternehmen zu führen und gleichzeitig Transparenz und Flexibilität für unsere Community zu bieten. Viele Unternehmen übernehmen diesen ausgewogenen Lizenzierungsansatz, der den Geist der Offenheit bewahrt und gleichzeitig die langfristige Lebensfähigkeit des Projekts sicherstellt. Wir sind stolz darauf, Teil dieser Bewegung zu sein!

### Was ist fair-code, und wie verhält sich die "Fair Use Lizenz" dazu?

Fair-code ist keine Softwarelizenz. Es beschreibt ein Softwaremodell, bei dem Software:

- Allgemein frei verfügbar ist und von jedermann verbreitet werden kann.

- Ihren Quellcode öffentlich verfügbar hat.

- Von jedermann in öffentlichen und privaten Gemeinschaften erweitert werden kann.

- Von ihren Autoren kommerziell eingeschränkt wird.

Die "Fair Use Lizenz" ist eine fair-code-Lizenz. Sie können mehr darüber lesen und andere Beispiele für fair-code-Lizenzen [hier](https://faircode.io) sehen. Um mit uns bzgl. Lizenzfragen in Verbindung zu treten, senden Sie bitte eine E-Mail an [license@planka.group](mailto:license@planka.group).

### Kann ich die "Fair Use Lizenz" für mein eigenes Projekt verwenden?

Ja! Wir selbst haben die "Fair Use Lizenz" genutzt, indem wir den Fußstapfen anderer folgten, die auf ihrer Website und in ihrer Lizenz offen andere dazu einladen, dem fair-code-Pfad zu folgen. Wie sie sind auch wir gespannt darauf, mehr Software zu sehen, die die "Fair Use Lizenz" verwendet.


================================================
FILE: LICENSES/PLANKA License Guide EN.md
================================================
**PLANKA License Guide**

Version 1.1 - Last updated: May 20, 2025

Related files in English:

- [PLANKA Community License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md)
- [PLANKA Commercial License EN.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md)
- PLANKA License Guide EN.md (this file)

Related files in German:

- [PLANKA Community License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20DE.md)
- [PLANKA Commercial License DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20DE.md)
- [PLANKA License Guide DE.md](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20DE.md)

---

## PLANKA's "Fair Use License" and the "PLANKA Pro/Enterprise License"

Our [Fair Use License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md) and our [PLANKA Pro/Enterprise License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md) are based on the [fair-code](http://faircode.io) model.

### Special licensing for educational & non-profit organizations

Educational institutions and non-profit organizations requiring commercial-level features or usage beyond our "Fair Use License" are encouraged to contact us for tailored licensing solutions and educational pricing. We look forward to helping your organization accomplish its mission!

# License FAQs

### What license do you use for PLANKA?

PLANKA uses the [Fair Use License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md) and the [PLANKA Pro/Enterprise License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md). These licenses are based on the [fair-code](http://faircode.io) model.

### What source code is covered by the PLANKA's "Fair Use License"?

The [Fair Use License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md) applies to our source code hosted in our [main GitHub repository](https://github.com/plankanban/planka) except:

- Content of branches other than the main branch (usually "master" or "main").

- Source code files or other files that contain ".pe." (for "PLANKA Pro/Enterprise") in their file names or folder names.

- Source code files that are marked as "PLANKA Pro/Enterprise" in their file headers or folders.

- Source code in folders that contain separate license files that clearly mark them as "PLANKA Pro/Enterprise".

These exceptions are licensed under the [PLANKA Pro/Enterprise License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md).

### What is the "Fair Use License"?

The "Fair Use License" falls under the so-called [fair-code](http://faircode.io) licenses category. PLANKA's license is based on and extends the "Sustainable Use License" introduced by [n8n](https://n8n.io) who's advice was greatly appreciated. With similar goals in mind, we decided to follow their lead and adopt their model for our own needs. [Further below](#why-did-you-choose-this-license) you can read why we chose this license.

The license allows you the free right to use, modify, create derivative works, and redistribute under the following conditions:

- You may use or modify PLANKA (a) for personal, hobby, or educational purposes, (b) internally within your own organization, (c) for private hosting for a typical number of friends, family, or personal projects, (d) to provide free access to non-profit organizations (as recognized by applicable tax authorities), or if you are a recognized non-profit organization yourself involving external users into your mission, and (e) public educational institutions for academic/research purposes only.

- Sharing accounts/credentials with third parties for business purposes or operating PLANKA as a hosted service for third parties for any commercial gain whatsoever is prohibited. Commercial gain includes any form of payment, advertising revenue, data monetization, or indirect commercial benefit or business advantage.

- You may not alter, remove, or obscure any licensing, copyright, or other notices from the software provided by the licensor. Any use of the licensor's trademarks is subject to applicable law.

### What is and is NOT allowed under the license in the context of PLANKA's products?

Our license allows use for "internal business purposes", as well as for personal, hobby, educational, and limited private hosting scenarios. However, it prohibits using PLANKA for any form of commercial gain, such as selling a product or service where the value derives in any form from PLANKA's functionality, operating it as a paid hosted service, monetizing access or user data, or deriving other indirect commercial benefits.

##### Here are some examples that would not be allowed:

- White-labeling PLANKA and offering it to your customers or affiliates for money.

- Hosting PLANKA and charging people money to access it.

- Use PLANKA's API to power services for which money is charged.

- Use of PLANKA to connect your own legal entity with legal entities or persons outside your organization in the context of a commercial relationship.

- Use PLANKA to conduct or support any kind of illegal or unlawful activity.

##### All of the following examples are allowed under our license:

- Using PLANKA to control your internal processes and manage your internal projects.

- Integrate PLANKA into other internally used products to enhance their capabilities.

- Providing consulting or public educational services related to PLANKA, for example, to build or integrate workflows for or in connection with PLANKA or develop custom modules to extend its functionalities.

- Supporting PLANKA, for example, by setting it up or maintaining it on an internal company server.

### Is it allowed to use PLANKA as a backend integration?

If you use PLANKA and its backend services beyond internal operations within your own organization by enabling third-party access for commercial gain, such as sharing accounts with clients or selling a product or service where the value derives in any form from PLANKA's functionality, operating it as a paid hosted service, monetizing access or user data, or deriving other indirect commercial benefits, you are required to purchase a "PLANKA Pro/Enterprise License".

This includes scenarios where PLANKA serves as core infrastructure for third-party solutions, is accessed by users outside your legal entity as part of a paid or commercial offering, or where you sublicense, repackage, or otherwise make PLANKA available to external parties. Any arrangement that involves integrating PLANKA into another product to serve as the primary operational engine for that product also requires a valid "PLANKA Pro/Enterprise License" or proprietary license available for our enterprise customers.

### Can you give me some quick examples to clarify free use vs. enterprise use?

---

##### Example 1: Use PLANKA's API to control or respond to fabrication machinery processes

Use our API to show and control the feedback coming from fabrication steps inside your company or to control production line events by moving cards.

**ALLOWED** under the "Fair Use License". You can integrate PLANKA into your other systems to use its API to control your internal processes.

##### Example 2: Offer commercial consulting or support services

You provide a service to your client to help them implement new workflows and board concepts into the PLANKA setup.

**ALLOWED** under the "Fair Use License". You are free to offer commercial consulting or integration and support services for PLANKA without the need for a separate license agreement with us.

##### Example 3: Bundle PLANKA in a free and public Docker image

A school or charity bundles PLANKA in a free, public Docker image for students who want to use PLANKA to organize their fields of study.

**ALLOWED** under the "Fair Use License". Since PLANKA is given away for free and without commercial revenues in mind, you are more than welcome to allow charitable entities and schools better access to PLANKA.

**HOWEVER** if you also plan to provide students access to an otherwise school's internal PLANKA instance, this would require our "PLANKA Educational License", which we will gladly offer on request.

##### Example 4: Provide PLANKA logins to clients and affiliates

To facilitate better project feedback, you offer your client and someone from a joint venture, both of whom you have a commercial relationship with, access to project boards inside your company. They can now comment on cards and also benefit from PLANKA's real-time update capabilities.

**NOT ALLOWED** under the "Fair Use License". Offering PLANKA as part of a paid service to third parties or providing PLANKA access to third-party users outside your own legal entity in the context of a commercial relationship requires you to register a "PLANKA Pro/Enterprise License".

##### Example 5: Offer PLANKA as a hosted product to other companies

You want to earn money by providing PLANKA to companies, freelancers, and other people. This way they have easy access to project management and control from everywhere.

**NOT ALLOWED** under the "Fair Use License". Selling PLANKA-based services requires you to agree to a "PLANKA Reseller License". We have a special hosting agreement for those who want to provide paid PLANKA service to customers or simply act as resellers for our own "PLANKA Corporate Hosting Services".

---

### Quick "Fair Use License" summary

- Personal, educational, hobby, or internal business use:

    Free to use. You can use, modify, and host PLANKA for yourself, your team, or your organization for personal, educational, or internal operational purposes.

- Use by non-profits and public educational institutions:

    Free to use for academic or research purposes, provided the organization qualifies as a non-profit under applicable tax laws. No application or special license is required.

- Providing access to third parties for any commercial gain:

    Not allowed. Sharing accounts or credentials with third parties for any commercial gain is prohibited under the license, even if you are not directly charging for access.

- Hosting PLANKA as a commercial service:

    Not allowed. You may not offer PLANKA as a hosted service or integrate it into a paid product or service where the value substantially comes from PLANKA. This includes advertising revenue, data monetization, or indirect commercial benefit or business advantage.

### What if I want to use PLANKA for something that's not permitted by the license?

To exceed the limits of the self-hosted Community version, you'll need a "PLANKA Pro/Enterprise License". Once you register with us, the "Fair Use License" restrictions are not only legally lifted, but you also gain access to the full enterprise feature set.

Alternatively, you can exceed these limits by subscribing to a hosted version - either Community or Pro/Enterprise. This allows, for example, providing accounts for your customers.

If you want to provide PLANKA as a service for other organizations or integrate PLANKA into your own software, you must sign a separate revenue agreement with us. We actively encourage software integrators and technical staff to integrate and connect PLANKA within their other products and use our extensive API to respond to, control, and master processes within their company; we just ask them to sign an agreement laying out the terms of use and the license fees required by PLANKA for using the product. Through PLANKA's API, it is capable of controlling and responding to external systems. You can learn more [here](https://docs.planka.cloud/docs/category/api-reference/) or contact us about it.

If you are unsure whether the use case you have in mind constitutes an internal business purpose or not, take a look at the [examples above](#can-you-give-me-some-quick-examples-to-clarify-free-use-vs-enterprise-use), and if you're still not sure, please contact us at [license@planka.group](mailto:license@planka.group).

### Why isn't there a commercial license for a self-hosted Community version which lifts the "Fair Use License" limits?

We support the self-hosted Community version through our GitHub and Discord community channels, but we can't efficiently provide direct phone or email support for multiple versions while meeting our obligations to paying customers. That's why direct support is reserved exclusively for Pro/Enterprise customers.

However, we do offer professional installation services and monthly-based service contracts for setting up, updating, and monitoring all versions to help organizations get the most out of their PLANKA deployment - please contact us at [license@planka.group](mailto:license@planka.group).

### Why doesn't PLANKA use a default open-source license?

We spend a lot of time creating an easy yet powerful tool that makes controlling and mastering projects a fun experience. Also, we wanted PLANKA to be as widely and freely available as possible while also ensuring that we can build a sustainable and viable business. By making our product free to use, easy to distribute, and source-available, we help everyone access the product. By operating as a business, we can develop and release new features, fix bugs, and provide reliable software at scale long-term.

### Why did you choose this license?

We believe that the "Fair Use License" is beneficial for the community as well as for the developers. Development is a costly enterprise, and giving away a Community version for free is a risk that many companies don't survive without selling software or the company. Therefore, many open-source companies live from donations or financial investors. Instead of selling our soul, we sell services and software licenses. This way we continue to grow, code, and support our community. So the short answer is "Live and let live" is how we feel about PLANKA.

Therefore, we are helping to promote [fair-code](https://faircode.io) software with the goal of making it a well-known umbrella term to describe software models like ours. To keep any friction around our proprietary license to an absolute minimum, we focus on two things:

1. Plain language, minimal length - the license is written in clear, concise English (a German version exists as well), with only the clauses absolutely needed.

2. Advocating fair-code - we actively promote the fair-code model so people recognize it as a straightforward, sustainable way to share and improve software like PLANKA.

### My company has a policy against using code that restricts commercial use - can I still use PLANKA?

Provided you are using PLANKA for internal business purposes and not making PLANKA available to your customers or affiliates, then of course you should be able to use PLANKA. If you are unsure whether the use case you have in mind constitutes an internal business purpose or not, take a look at the [examples above](#can-you-give-me-some-quick-examples-to-clarify-free-use-vs-enterprise-use), and if you're still unclear, email us at [license@planka.group](mailto:license@planka.group).

### What happens to code I contribute to PLANKA in regard to its "Fair Use License"?

Any code you contribute on GitHub is subject to GitHub's [terms of use](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#d_user_generated_content). In simple terms, this means you own and are responsible for anything you contribute, but that you grant other GitHub users certain rights to use this code. When you contribute code to a repository containing notice of a license, you license the code under the same terms.

PLANKA asks every contributor to sign our [Contributor License Agreement](https://github.com/plankanban/planka/blob/master/CONTRIBUTOR_LICENSE_AGREEMENT.md). In addition to the above, this gives PLANKA the ability to change its license without seeking additional permission. It also means you aren't liable for your contributions (e.g., in case they cause damage to someone else's business).

It's easy to get started contributing code to PLANKA on [GitHub](https://github.com/plankanban), and we've listed broader ways of participating in our community [here](https://github.com/plankanban/planka/blob/master/CONTRIBUTING.md).

### Is PLANKA open source?

PLANKA's source code is freely available under the "Fair Use License". While this doesn't align with the Open Source Initiative's strict definition (which doesn't allow any use limitations), PLANKA still offers nearly all the same benefits as traditionally open-source software to most users, including corporations.

We embrace what's often called the 'fair-code' model - our code is source-available and follows a simple "Live and let live" philosophy. This approach allows us to maintain a sustainable company while still providing transparency and flexibility to our community. Many companies are adopting this balanced licensing approach that preserves the spirit of openness while ensuring the project's long-term viability. We're proud to be part of this movement!

### What is fair-code, and how does the "Fair Use License" relate to it?

Fair-code is not a software license. It describes a software model where software:

- Is generally free to use and can be distributed by anyone.

- Has its source code openly available.

- Can be extended by anyone in public and private communities.

- Is commercially restricted by its authors.

The "Fair Use License" is a fair-code license. You can read more about it and see other examples of fair-code licenses [here](https://faircode.io). To get in touch with us about license questions, please email [license@planka.group](mailto:license@planka.group).

### Can I use the "Fair Use License" for my own project?

Yes! We ourselves made use of the "Fair Use License" by following others' footsteps who openly invite others on their website and in their license to follow the fair code path. Like them, we're excited to see more software use the "Fair Use License".


================================================
FILE: README.md
================================================
<div align="center">

  ![Logo](https://raw.githubusercontent.com/plankanban/planka/master/assets/logo.png)

  # PLANKA

  _Project mastering driven by fun_

  ![Version](https://img.shields.io/github/package-json/v/plankanban/planka?style=flat-square) [![Docker Pulls](https://img.shields.io/badge/docker_pulls-8M%2B-%23066da5?style=flat-square&color=red)](https://github.com/plankanban/planka/pkgs/container/planka) [![Contributors](https://img.shields.io/github/contributors/plankanban/planka?style=flat-square&color=blue)](https://github.com/plankanban/planka/graphs/contributors) [![Chat](https://img.shields.io/discord/1041440072953765979?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/WqqYNd7Jvt)

  [Install](https://docs.planka.cloud/docs/installation/docker/production-version/) ·  [Demo](https://planka.app) · [Docs](https://docs.planka.cloud/docs/welcome/) · [API](https://plankanban.github.io/planka/swagger-ui/) · [Cloud](https://planka.app/pricing) · [Pro version](https://planka.app/pro)

  ![Demo](https://raw.githubusercontent.com/plankanban/planka/master/assets/demo.gif)

</div>

## Key Features

- **Collaborative Kanban Boards:** Create projects, boards, lists, cards, and manage tasks with an intuitive drag-and-drop interface
- **Real-Time Updates:** Instant syncing across all users, no refresh needed
- **Rich Markdown Support:** Write beautifully formatted card descriptions with a powerful markdown editor
- **Flexible Notifications:** Get alerts through 100+ providers, fully customizable to your workflow
- **Seamless Authentication:** Single sign-on with OpenID Connect integration
- **Multilingual & Easy to Translate:** Full internationalization support for a global audience

## How to Deploy

PLANKA is easy to install using multiple methods - learn more in the [installation guide](https://docs.planka.cloud/docs/welcome/).

For configuration and environment settings, see the [configuration section](https://docs.planka.cloud/docs/category/configuration/).

Interested in a hosted or [Pro version](https://planka.app/pro) of PLANKA? Check out the pricing on our [website](https://planka.app/pricing).

## Notes App

A testing version of the Notes app is now available on multiple platforms:

- **iOS:** Join the [TestFlight](https://testflight.apple.com/join/5eJqTaJW) to try the app
- **Windows & Android:** Download the app [here](https://planka-notes.hillerdaniel.de)

## Contact

For any security issues, please do not create a public issue on GitHub - instead, report it privately by emailing [security@planka.group](mailto:security@planka.group).

**Note:** We do NOT offer any public support via email, please use GitHub.

**Join our community:** Get help, share ideas, or contribute on our [Discord server](https://discord.gg/WqqYNd7Jvt).

## License

PLANKA is [fair-code](https://faircode.io) distributed under the [Fair Use License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Community%20License%20EN.md) and [PLANKA Pro/Enterprise License](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20Commercial%20License%20EN.md).

- **Source Available:** The source code is always visible
- **Self-Hostable:** Deploy and host it anywhere
- **Extensible:** Customize with your own functionality
- **Enterprise Licenses:** Available for additional features and support

For more details, check the [License Guide](https://github.com/plankanban/planka/blob/master/LICENSES/PLANKA%20License%20Guide%20EN.md).

## Contributing

Found a bug or have a feature request? Check out our [Contributing Guide](https://github.com/plankanban/planka/blob/master/CONTRIBUTING.md) to get started.

For setting up the project locally, see the [development section](https://docs.planka.cloud/docs/category/development/).

**Thanks to all our contributors!**

[![Contributors](https://contrib.rocks/image?repo=plankanban/planka)](https://github.com/plankanban/planka/graphs/contributors)


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Most recent release.

## Reporting a Vulnerability

Please report any security issues you discovered to [security@planka.group](mailto:security@planka.group). If the issue is confirmed, we will release a patch as soon as possible depending on complexity.

**Do NOT create public issues on GitHub for security vulnerabilities.**

Thank you for your contribution!


================================================
FILE: charts/planka/.helmignore
================================================
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/


================================================
FILE: charts/planka/Chart.yaml
================================================
apiVersion: v2
name: planka
description: A Helm chart to deploy PLANKA and it's dependencies.

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 2.1.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "2.1.0"

dependencies:
  - alias: postgresql
    condition: postgresql.enabled
    name: postgresql
    repository: &bitnami-repo https://charts.bitnami.com/bitnami
    version: 16.6.6


================================================
FILE: charts/planka/README.md
================================================
# PLANKA Helm Chart

This Helm Chart simplifies the deployment of [PLANKA](https://github.com/plankanban/planka) on Kubernetes.

Shoutout to [this issue](https://github.com/plankanban/planka/issues/192) for requesting a Helm Chart!

## Issues

By using the Bitnami chart for PostgreSQL, there is an issue where once deployed, if trying to use a different password then it will be ignored as the Persistant Volume (PV) will already exist with the previous password. See warning from Bitnami below:

> **Warning!** Setting a password will be ignored on new installation in the case when previous Posgresql release was deleted through the helm command. In that case, old PVC will have an old password, and setting it through helm won't take effect. Deleting persistent volumes (PVs) will solve the issue. Refer to [issue 2061](https://github.com/bitnami/charts/issues/2061) for more details

If you want to fully uninstall this chart including the data, follow [these steps](https://github.com/bitnami/charts/blob/main/bitnami/postgresql/README.md#uninstalling-the-chart) from the Bitnami Chart's docs.

## Usage

If you just want to spin up an instance using help, please see [these docs](https://docs.planka.cloud/docs/installation/kubernetes/helm-chart/). If you want to make changes to the chart locally, and deploy them, see the below section.

## Local Building and Using the Chart

The basic usage of the chart can be found below:

```bash
git clone https://github.com/plankanban/planka.git
cd planka/charts/planka
helm dependency build
export SECRETKEY=$(openssl rand -hex 64)
helm install planka . --set secretkey=$SECRETKEY  \
--set admin_email="demo@demo.demo"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo"
```

> **Note:** The command `openssl rand -hex 64` is needed to create a random hexadecimal key for planka. On Windows you can use Git Bash to run that command.

To access PLANKA you can port forward using the following command:

```bash
kubectl port-forward $POD_NAME 3000:1337
```

### Accessing Externally

To access PLANKA externally you can use the following configuration

```bash
# HTTP only
helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="demo@demo.demo"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo" \
--set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \

# HTTPS
helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="demo@demo.demo"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo" \
--set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \
--set ingress.tls[0].secretName=planka-tls \
--set ingress.tls[0].hosts[0]=planka.example.dev \
```

or create a values.yaml file like:

````yaml
secretkey: "<InsertSecretKey>"
# The admin section needs to be present for new instances of PLANKA, after the first start you can remove the lines starting with admin_. If you want the admin user to be unchangeable admin_email: has to stay
# After changing the config you have to run ```helm upgrade  planka . -f values.yaml```

# Admin user
admin_email: "demo@demo.demo" # Do not remove if you want to prevent this user from being edited/deleted
admin_password: "demo"
admin_name: "Demo Demo"
admin_username: "demo"
# Admin user

# Ingress
ingress:
  enabled: true
  hosts:
    - host: planka.example.dev
      paths:
        - path: /
          pathType: ImplementationSpecific

  # Needed for HTTPS
  tls:
    - secretName: planka-tls # existing TLS secret in k8s
      hosts:
        - planka.example.dev
```

```bash
helm install planka . -f values.yaml
```

### Things to consider if production hosting

If you want to host PLANKA for more than just playing around with, you might want to do the following things:

- Create a `values.yaml` with your config, as this will make applying upgrades much easier in the future.
- Create your `secretkey` once and store it either in a secure vault, or in your `values.yaml` file so it will be the same for upgrading in the future.
- Specify a password for `postgresql.auth.password` as there have been issues with the postgresql chart generating new passwords locking you out of the data you've already stored. (see [this issue](https://github.com/bitnami/charts/issues/2061))

Any questions or concerns, [raise an issue](https://github.com/Chris-Greaves/planka-helm-chart/issues/new).

## Advanced Configuration

### Extra Volume Mounts

The Helm chart supports mounting arbitrary ConfigMaps, Secrets, and Volumes to the PLANKA deployment using the `extraMounts` configuration. This is especially useful for scenarios like:

- Mounting custom CA certificates for OIDC with self-hosted identity providers
- Adding custom configuration files
- Mounting TLS certificates from existing secrets
- Adding temporary or persistent storage volumes

**Note**: ConfigMaps and Secrets must be created separately before referencing them in `extraMounts`.

#### Basic Usage

Use the `extraMounts` section to mount any type of volume:

```yaml
extraMounts:
  # Mount CA certificate from existing ConfigMap
  - name: ca-certs
    mountPath: /etc/ssl/certs/custom-ca.crt
    subPath: ca.crt
    readOnly: true
    configMap:
      name: ca-certificates # Must exist

  # Mount TLS certificates from existing Secret
  - name: tls-certs
    mountPath: /etc/ssl/private
    readOnly: true
    secret:
      secretName: planka-tls-secret # Must exist
      items:
        - key: tls.crt
          path: server.crt
        - key: tls.key
          path: server.key

  # Temporary storage
  - name: temp-storage
    mountPath: /tmp/planka-temp
    readOnly: false
    emptyDir:
      sizeLimit: 1Gi

  # Host path mount
  - name: backup-storage
    mountPath: /var/lib/planka-backups
    readOnly: false
    hostPath:
      path: /var/lib/planka-backups
      type: DirectoryOrCreate

  # NFS mount
  - name: nfs-storage
    mountPath: /shared/data
    readOnly: false
    nfs:
      server: nfs.example.com
      path: /exports/planka
```

### OIDC with Self-Hosted Keycloak

A common use case is configuring OIDC with a self-hosted Keycloak instance that uses custom CA certificates.

First, create the CA certificate ConfigMap:

```bash
kubectl create configmap ca-certificates --from-file=ca.crt=/path/to/your/ca.crt
```

Then configure the chart:

```yaml
# Mount custom CA certificate from existing ConfigMap
extraMounts:
  - name: keycloak-ca
    mountPath: /etc/ssl/certs/keycloak-ca.crt
    subPath: ca.crt
    readOnly: true
    configMap:
      name: ca-certificates

# Configure Node.js to trust the custom CA
extraEnv:
  - name: NODE_EXTRA_CA_CERTS
    value: "/etc/ssl/certs/keycloak-ca.crt"

# Enable OIDC
oidc:
  enabled: true
  clientId: "planka-client"
  clientSecret: "your-client-secret"
  issuerUrl: "https://keycloak.example.com/realms/master"
  admin:
    roles:
      - "planka-admin"
```

### Environment Variables from Secrets

You can reference values from existing secrets in environment variables:

```yaml
extraEnv:
  - name: SMTP_PASSWORD
    valueFrom:
      secretName: smtp-credentials
      key: password
  - name: CUSTOM_API_KEY
    valueFrom:
      secretName: api-credentials
      key: api-key
```

### Custom Terms of Service

You can provide your own End User Terms of Service by passing the markdown files directly via `values.yaml` in the `terms` configuration block. This automates the creation of a corresponding ConfigMap and volume mount.

```yaml
terms:
  enabled: true
  customFiles:
    en-US.md: |
      # End User Terms of Service
      ...
      [confirmations]::
      ---
      ✔️ **I have read and accept these End User Terms of Service**
    de-DE.md: |
      # Nutzungsbedingungen
      ...
      [confirmations]::
      ---
      ✔️ **Ich habe diese Nutzungsbedingungen gelesen und akzeptiere sie**
```

### Image Digest Pinning

For enhanced security and reproducibility, you can pin the container image using its SHA256 digest instead of relying solely on tags. This ensures you always deploy the exact same image, preventing tag mutations or accidental updates.

#### Finding the Image Digest

You can find the digest of a specific image tag using:

```bash
docker inspect ghcr.io/plankanban/planka:latest --format='{{index .RepoDigests 0}}'
# Output: ghcr.io/plankanban/planka@sha256:abc123def456...

# Or with skopeo
skopeo inspect docker://ghcr.io/plankanban/planka:latest
```

#### Usage

You can use digest pinning in several ways:

**Option 1: Digest with tag (recommended)**

Includes the tag for reference while using the digest for verification:

```bash
helm install planka . --set secretkey=$SECRETKEY \
  --set image.tag=latest \
  --set image.digest=abc123def456... \
  --set admin_email="demo@demo.demo" \
  --set admin_password="demo" \
  --set admin_name="Demo Demo" \
  --set admin_username="demo"
```

Or in values.yaml:

```yaml
image:
  repository: ghcr.io/plankanban/planka
  tag: latest
  digest: "abc123def456ab89cd12ef34ab56cd78ef90ab12cd34ef56ab78cd90ef12ab34"
```

**Option 2: Digest only**

If you prefer to pin only by digest without specifying a tag:

```yaml
image:
  repository: ghcr.io/plankanban/planka
  tag: "" # Empty - digest alone identifies the image
  digest: "abc123def456ab89cd12ef34ab56cd78ef90ab12cd34ef56ab78cd90ef12ab34"
```

#### Security Benefits

- **Immutability**: Ensures you always deploy the exact same image
- **Supply Chain Security**: Protects against tag mutations or registry compromise
- **Reproducibility**: Makes deployments fully reproducible across environments
- **Audit Trail**: Provides clear image identity in deployment manifests

### Complete Example

See `values-example.yaml` for a comprehensive example that demonstrates all the advanced features including OIDC configuration with custom CA certificates.
````


================================================
FILE: charts/planka/templates/NOTES.txt
================================================
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
  {{- range .paths }}
  http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
  {{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
  export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "planka.fullname" . }})
  export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "planka.fullname" . }}'
  export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "planka.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
  echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
  export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "planka.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
  export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
  echo "Visit http://localhost:3000 to use your application"
  kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 3000:$CONTAINER_PORT
{{- end }}


================================================
FILE: charts/planka/templates/_helpers.tpl
================================================
{{/*
Expand the name of the chart.
*/}}
{{- define "planka.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "planka.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "planka.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "planka.labels" -}}
helm.sh/chart: {{ include "planka.chart" . }}
{{ include "planka.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "planka.selectorLabels" -}}
app.kubernetes.io/name: {{ include "planka.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "planka.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "planka.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/configmap-terms.yaml
================================================
{{- if .Values.terms.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "planka.fullname" . }}-terms
  labels:
    {{- include "planka.labels" . | nindent 4 }}
data:
  {{- range $key, $value := .Values.terms.customFiles }}
  {{ $key }}: |
    {{- $value | nindent 4 }}
  {{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/deployment.yaml
================================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "planka.fullname" . }}
  labels:
    {{- include "planka.labels" . | nindent 4 }}
  {{- with .Values.deploymentAnnotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if not .Values.autoscaling.enabled }}
  replicas: {{ .Values.replicaCount }}
  {{- end }}
  selector:
    matchLabels:
      {{- include "planka.selectorLabels" . | nindent 6 }}
  {{- if .Values.persistence.enabled }}
  strategy:
    type: Recreate
  {{- end }}
  template:
    metadata:
      {{- with .Values.podAnnotations }}
      annotations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      labels:
        {{- include "planka.selectorLabels" . | nindent 8 }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      serviceAccountName: {{ include "planka.serviceAccountName" . }}
      securityContext:
        {{- toYaml .Values.podSecurityContext | nindent 8 }}
      containers:
        - name: {{ .Chart.Name }}
          securityContext:
            {{- toYaml .Values.securityContext | nindent 12 }}
          {{- $imageTag := .Values.image.tag | default .Chart.AppVersion }}
          {{- if .Values.image.digest }}
            {{- if $imageTag }}
          image: "{{ .Values.image.repository }}:{{ $imageTag }}@sha256:{{ .Values.image.digest }}"
            {{- else }}
          image: "{{ .Values.image.repository }}@sha256:{{ .Values.image.digest }}"
            {{- end }}
          {{- else }}
          image: "{{ .Values.image.repository }}:{{ $imageTag }}"
          {{- end }}
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.containerPort | default 1337 }}
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /
              port: http
          readinessProbe:
            httpGet:
              path: /
              port: http
          volumeMounts:
            - mountPath: /app/data
              subPath: data
              name: planka
          {{- if .Values.securityContext.readOnlyRootFilesystem }}
            - mountPath: /app/logs
              subPath: app-logs
              name: emptydir
            - mountPath: /app/.tmp
              subPath: app-tmp
              name: emptydir
            - mountPath: /tmp
              subPath: tmp
              name: emptydir
          {{- end }}
          {{- if .Values.terms.enabled }}
            - mountPath: /app/terms/custom
              name: planka-terms
          {{- end }}
          {{- /* Extra volume mounts */}}
          {{- range .Values.extraMounts }}
            - name: {{ .name }}
              mountPath: {{ .mountPath }}
              {{- if .subPath }}
              subPath: {{ .subPath }}
              {{- end }}
              {{- if hasKey . "readOnly" }}
              readOnly: {{ .readOnly }}
              {{- else }}
              readOnly: true
              {{- end }}
          {{- end }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
          env:
          {{- if .Values.extraEnv }}
          {{- range .Values.extraEnv }}
            - name: {{ .name }}
          {{- if  .value }}
              value: {{ .value | quote}}
          {{- end }}
          {{- if .valueFrom }}
              valueFrom:
                secretKeyRef:
                  name: {{ .valueFrom.secretName }}
                  key: {{ .valueFrom.key }}
          {{- end }}
          {{- end }}
          {{- end }}
          {{- if not .Values.postgresql.enabled }}
          {{- if .Values.existingDburlSecret }}
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.existingDburlSecret }}
                  key: uri
          {{- else }}
            - name: DATABASE_URL
              value: {{ required "If the included postgresql deployment is disabled you need to provide an existing secret in .Values.existingDburlSecret or define a Database URL in 'dburl'" .Values.dburl }}
          {{- end }}
          {{- else }}
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: {{ include "planka.fullname" . }}-postgresql-svcbind-custom-user
                  key: uri
          {{- end }}
            - name: BASE_URL
              {{- if .Values.baseUrl }}
              value: {{ .Values.baseUrl }}
              {{- else if .Values.ingress.enabled }}
              value: {{ printf "https://%s" (first .Values.ingress.hosts).host }}
              {{- else }}
              value: http://localhost:3000
              {{- end }}
            - name: SECRET_KEY
              {{- if .Values.existingSecretkeySecret }}
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.existingSecretkeySecret }}
                  key: key
              {{- else }}
              value: {{ required "A secret key needs to be generated using 'openssl rand -hex 64' and assigned to secretkey." .Values.secretkey }}
              {{- end }}
            - name: TRUST_PROXY
              value: "true"
            - name: DEFAULT_ADMIN_EMAIL
              value: {{ .Values.admin_email }}
            - name: DEFAULT_ADMIN_NAME
              value: {{ .Values.admin_name }}
            {{- if .Values.existingAdminCredsSecret }}
            - name: DEFAULT_ADMIN_USERNAME
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.existingAdminCredsSecret }}
                  key: username
            - name: DEFAULT_ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: {{ .Values.existingAdminCredsSecret }}
                  key: password
            {{- else }}
            - name: DEFAULT_ADMIN_USERNAME
              value: {{ .Values.admin_username }}
            - name: DEFAULT_ADMIN_PASSWORD
              value: {{ .Values.admin_password }}
            {{- end }}
          {{ range $k, $v := .Values.env }}
            - name: {{ $k | quote }}
              value: {{ $v | quote }}
          {{- end }}
          {{- if .Values.oidc.enabled }}
          {{- $secretName := default (printf "%s-oidc" (include "planka.fullname" .)) .Values.oidc.existingSecret }}
            - name: OIDC_CLIENT_ID
              valueFrom:
                secretKeyRef:
                  key:  clientId
                  name: {{ $secretName }}
            - name: OIDC_CLIENT_SECRET
              valueFrom:
                secretKeyRef:
                  key:  clientSecret
                  name: {{ $secretName }}
            - name: OIDC_ISSUER
              value: {{ required "issuerUrl is required when configuring OIDC" .Values.oidc.issuerUrl | quote }}
            - name: OIDC_SCOPES
              value: {{ join " " .Values.oidc.scopes | default "openid profile email" | quote }}
          {{- if .Values.oidc.admin.roles }}
            - name: OIDC_ADMIN_ROLES
              value: {{ join "," .Values.oidc.admin.roles | quote }}
          {{- end }}
            - name: OIDC_ROLES_ATTRIBUTE
              value: {{ .Values.oidc.admin.rolesAttribute | default "groups" | quote }}
          {{- if .Values.oidc.admin.ignoreRoles }}
            - name: OIDC_IGNORE_ROLES
              value: {{ .Values.oidc.admin.ignoreRoles | quote }}
          {{- end }}
          {{- end }}
        {{- if .Values.extraContainers -}}
          {{ toYaml .Values.extraContainers | nindent 8 }}
        {{- end }}
      {{- with .Values.nodeSelector }}
      nodeSelector:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.affinity }}
      affinity:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      {{- with .Values.tolerations }}
      tolerations:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      volumes:
        - name: planka
    {{- if .Values.persistence.enabled }}
          persistentVolumeClaim:
            claimName: {{ .Values.persistence.existingClaim | default (include "planka.fullname" .) }}
    {{- else }}
          emptyDir: {}
    {{- end }}
    {{- if .Values.securityContext.readOnlyRootFilesystem }}
        - name: emptydir
          emptyDir: {}
    {{- end }}
    {{- if .Values.terms.enabled }}
        - name: planka-terms
          configMap:
            name: {{ include "planka.fullname" . }}-terms
    {{- end }}
        {{- /* Extra volumes */}}
        {{- range .Values.extraMounts }}
        - name: {{ .name }}
          {{- if .configMap }}
          configMap:
            {{- toYaml .configMap | nindent 12 }}
          {{- else if .secret }}
          secret:
            {{- toYaml .secret | nindent 12 }}
          {{- else if .emptyDir }}
          emptyDir:
            {{- toYaml .emptyDir | nindent 12 }}
          {{- else if .hostPath }}
          hostPath:
            {{- toYaml .hostPath | nindent 12 }}
          {{- else if .persistentVolumeClaim }}
          persistentVolumeClaim:
            {{- toYaml .persistentVolumeClaim | nindent 12 }}
          {{- else if .nfs }}
          nfs:
            {{- toYaml .nfs | nindent 12 }}
          {{- else }}
          {{- /* Support any other volume type by removing known mount-specific keys */}}
          {{- $volume := omit . "name" "mountPath" "subPath" "readOnly" }}
          {{- toYaml $volume | nindent 10 }}
          {{- end }}
        {{- end }}


================================================
FILE: charts/planka/templates/hpa.yaml
================================================
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: {{ include "planka.fullname" . }}
  labels:
    {{- include "planka.labels" . | nindent 4 }}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: {{ include "planka.fullname" . }}
  minReplicas: {{ .Values.autoscaling.minReplicas }}
  maxReplicas: {{ .Values.autoscaling.maxReplicas }}
  metrics:
    {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
    {{- end }}
    {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
    {{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/ingress.yaml
================================================
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "planka.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
  {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
  {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
  {{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
  name: {{ $fullName }}
  labels:
    {{- include "planka.labels" . | nindent 4 }}
    {{- with .Values.ingress.labels }}
    {{- toYaml . | nindent 4 }}
    {{- end }}
  {{- with .Values.ingress.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
  ingressClassName: {{ .Values.ingress.className }}
  {{- end }}
  {{- if .Values.ingress.tls }}
  tls:
    {{- range .Values.ingress.tls }}
    - hosts:
        {{- range .hosts }}
        - {{ . | quote }}
        {{- end }}
      secretName: {{ .secretName }}
    {{- end }}
  {{- end }}
  rules:
    {{- range .Values.ingress.hosts }}
    - host: {{ .host | quote }}
      http:
        paths:
          {{- range .paths }}
          - path: {{ .path }}
            {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
            pathType: {{ .pathType }}
            {{- end }}
            backend:
              {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
              service:
                name: {{ $fullName }}
                port:
                  number: {{ $svcPort }}
              {{- else }}
              serviceName: {{ $fullName }}
              servicePort: {{ $svcPort }}
              {{- end }}
          {{- end }}
    {{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/pvc.yaml
================================================
{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: {{ include "planka.fullname" . }}
  labels:
    app.kubernetes.io/name: {{ include "planka.name" . }}
    helm.sh/chart: {{ include "planka.chart" . }}
    app.kubernetes.io/instance: {{ .Release.Name }}
    app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
  accessModes:
    - {{ .Values.persistence.accessMode }}
  resources:
    requests:
      storage: {{ .Values.persistence.size | quote }}
{{- if .Values.persistence.storageClass }}
{{- if (eq "-" .Values.persistence.storageClass) }}
  storageClassName: ""
{{- else }}
  storageClassName: "{{ .Values.persistence.storageClass }}"
{{- end }}
{{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/secret-oidc.yaml
================================================
{{- if .Values.oidc.enabled }}
{{- if eq (and (not (empty .Values.oidc.clientId)) (not (empty .Values.oidc.clientSecret))) (not (empty .Values.oidc.existingSecret)) -}}
  {{- fail "Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`" -}}
{{- end }}
{{- if (and (and (not (empty .Values.oidc.clientId)) (not (empty .Values.oidc.clientSecret))) (empty .Values.oidc.existingSecret)) -}}
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "planka.fullname" . }}-oidc
  labels:
    {{- include "planka.labels" . | nindent 4 }}
type: Opaque
data:
  clientId: {{ .Values.oidc.clientId | b64enc | quote }}
  clientSecret: {{ .Values.oidc.clientSecret | b64enc | quote }}
{{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/service.yaml
================================================
apiVersion: v1
kind: Service
metadata:
  name: {{ include "planka.fullname" . }}
  labels:
    {{- include "planka.labels" . | nindent 4 }}
  {{- with .Values.service.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: http
      protocol: TCP
      name: http
  selector:
    {{- include "planka.selectorLabels" . | nindent 4 }}


================================================
FILE: charts/planka/templates/serviceaccount.yaml
================================================
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ include "planka.serviceAccountName" . }}
  labels:
    {{- include "planka.labels" . | nindent 4 }}
  {{- with .Values.serviceAccount.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
{{- end }}


================================================
FILE: charts/planka/templates/tests/test-connection.yaml
================================================
apiVersion: v1
kind: Pod
metadata:
  name: "{{ include "planka.fullname" . }}-test-connection"
  labels:
    {{- include "planka.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": test
spec:
  containers:
    - name: wget
      image: busybox
      command: ['wget']
      args: ['{{ include "planka.fullname" . }}:{{ .Values.service.port }}']
  restartPolicy: Never


================================================
FILE: charts/planka/values.yaml
================================================
# Default values for planka.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.

replicaCount: 1

image:
  repository: ghcr.io/plankanban/planka
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: ""
  # Optional: specify the image digest for pinning by SHA256
  # When set, the image reference will include the digest for enhanced security
  # Example: "abc123def456..." (without sha256: prefix)
  digest: ""

imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""

# Generate a secret using openssl rand -base64 45
secretkey: ""

## @param existingSecretkeySecret Name of an existing secret containing the session key string
## NOTE: Must contain key `key`
## NOTE: When it's set, the secretkey parameter is ignored
existingSecretkeySecret: ""

## @param existingAdminCredsSecret Name of an existing secret containing the admin username and password
## NOTE: Must contain keys `username` and `password`
## NOTE: When it's set, the `admin_username` and `admin_password` parameters are ignored
existingAdminCredsSecret: ""

admin_email: ""
admin_password: ""
admin_name: ""
admin_username: ""

# Base url for PLANKA. Will override `ingress.hosts[0].host`
# Defaults to `http://localhost:3000` if ingress is disabled.
baseUrl: ""

serviceAccount:
  # Specifies whether a service account should be created
  create: true
  # Annotations to add to the service account
  annotations: {}
  # The name of the service account to use.
  # If not set and create is true, a name is generated using the fullname template
  name: ""

podAnnotations: {}

podSecurityContext: {}
  # fsGroup: 2000

# Annotations to add to the deployment
deploymentAnnotations: {}

securityContext: {}
  # capabilities:
  #   drop:
  #   - ALL
  # readOnlyRootFilesystem: true
  # runAsNonRoot: true
  # runAsUser: 1000

service:
  annotations: {}
  type: ClusterIP
  port: 1337
  ## @param service.containerPort PLANKA HTTP container port
  ## If empty will default to 1337
  ##
  containerPort: 1337

ingress:
  enabled: false
  className: ""
  labels: {}
  annotations: {}
    # kubernetes.io/ingress.class: nginx
    # kubernetes.io/tls-acme: "true"
  hosts:
    # Used to set planka BASE_URL if no `baseurl` is provided.
    - host: planka.local
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls: []
  #  - secretName: planka-tls
  #    hosts:
  #      - planka.local

resources: {}
  # We usually recommend not to specify default resources and to leave this as a conscious
  # choice for the user. This also increases chances charts run on environments with little
  # resources, such as Minikube. If you do want to specify resources, uncomment the following
  # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
  # limits:
  #   cpu: 100m
  #   memory: 128Mi
  # requests:
  #   cpu: 100m
  #   memory: 128Mi

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80
  # targetMemoryUtilizationPercentage: 80

nodeSelector: {}

tolerations: []

affinity: {}

postgresql:
  global:
    security:
      allowInsecureImages: true
  image:
    repository: bitnamilegacy/postgresql
  enabled: true
  auth:
    database: planka
    username: planka
    password: ""
    postgresPassword: ""
    replicationPassword: ""
    # existingSecret: planka-postgresql
  serviceBindings:
    enabled: true

## Set this or existingDburlSecret if you disable the built-in postgresql deployment
dburl:

## @param existingDburlSecret Name of an existing secret containing a DBurl connection string
## NOTE: Must contain key `uri`
## NOTE: When it's set, the `dburl` parameter is ignored
##
existingDburlSecret: ""

## PVC-based data storage configuration
persistence:
  enabled: false
  # existingClaim: netbox-data
  # storageClass: "-"
  accessMode: ReadWriteOnce
  size: 10Gi

## OpenID Identity Management configuration
##
## Example:
## ---------------
## oidc:
##   enabled: true
##   clientId: sxxaAIAxVXlCxTmc1YLHBbQr8NL8MqLI2DUbt42d
##   clientSecret: om4RTMRVHRszU7bqxB7RZNkHIzA8e4sGYWxeCwIMYQXPwEBWe4SY5a0wwCe9ltB3zrq5f0dnFnp34cEHD7QSMHsKvV9AiV5Z7eqDraMnv0I8IFivmuV5wovAECAYreSI
##   issuerUrl: https://auth.local/application/o/planka/
##   admin:
##     roles:
##       - planka-admin
##
## ---------------
## NOTE: A minimal configuration requires setting `clientId`, `clientSecret` and `issuerUrl`. (plus `admin.roles` for administrators)
## ref: https://docs.planka.cloud/docs/configuration/oidc/
##
oidc:
  ## @param oidc.enabled Enable single sign-on (SSO) with OpenID Connect (OIDC)
  ##
  enabled: false

  ## OIDC credentials
  ## @param oidc.clientId A string unique to the provider that identifies your app.
  ## @param oidc.clientSecret A secret string that the provider uses to confirm ownership of a client ID.
  ##
  ## NOTE: Either specify inline `clientId` and `clientSecret` or refer to them via `existingSecret`
  ##
  clientId: ""
  clientSecret: ""

  ## @param oidc.existingSecret Name of an existing secret containing OIDC credentials
  ## NOTE: Must contain key `clientId` and `clientSecret`
  ## NOTE: When it's set, the `clientId` and `clientSecret` parameters are ignored
  ##
  existingSecret: ""

  ## @param oidc.issuerUrl The OpenID connect metadata document endpoint
  ##
  issuerUrl: ""

  ## @param oidc.scopes A list of scopes required for OIDC client.
  ## If empty will default to `openid`, `profile` and `email`
  ## NOTE: PLANKA needs the email and name claims
  ##
  scopes: []

  ## Admin permissions configuration
  admin:
    ## @param oidc.admin.ignoreRoles If set to true, the admin roles will be ignored.
    ## It is useful if you want to use OIDC for authentication but not for authorization.
    ## If empty will default to `false`
    ##
    ignoreRoles: false

    ## @param oidc.admin.rolesAttribute The name of a custom group claim that you have configured in your OIDC provider
    ## If empty will default to `groups`
    ##
    rolesAttribute: groups

    ## @param oidc.admin.roles The names of the admin groups
    ##
    roles: []
      # - planka-admin

## Extra environment variables for planka deployment
## Supports hard coded and getting values from a k8s secret
## - name: test
##   value: valuetest
## - name: another
##   value: another
## - name: test-secret
##   valueFrom:
##     secretName: k8s-secret-name
##     key: key-inside-the-secret
##
extraEnv: []

## Example extraEnv for configuring SMTP
## extraEnv:
##   - name: SMTP_HOST
##     value: "smtp.example.com"
##   - name: SMTP_PORT
##     value: "587"
##   - name: SMTP_NAME
##     value: "Your Name"
##   - name: SMTP_SECURE
##     value: "true"
##   - name: SMTP_TLS_REJECT_UNAUTHORIZED
##     value: "false"
##   - name: SMTP_USER
##     value: "your_email@example.com"
##   - name: SMTP_PASSWORD
##     value: "your_password"
##   - name: SMTP_FROM
##     value: "your_email@example.com"

## End User Terms of Service configuration
## Mount custom terms of service markdown files into the Planka deployment
##
terms:
  enabled: false
  # Provide individual language files as key-value pairs
  # e.g.,
  # customFiles:
  #   en-US.md: |
  #     # End User Terms of Service
  #     ...
  #   de-DE.md: |
  #     # Nutzungsbedingungen
  #     ...
  customFiles: {}

## Extra volume mounts configuration
## Mount ConfigMaps, Secrets, and arbitrary volumes to the PLANKA container
## This allows mounting any pre-existing ConfigMaps, Secrets, or other volume types
##
extraMounts: []
## Example extraMounts:
## extraMounts:
##   - name: ca-certs
##     mountPath: /etc/ssl/certs/ca-certificates.crt
##     subPath: ca-bundle.crt
##     readOnly: true
##     configMap:
##       name: ca-certificates
##   - name: tls-certs
##     mountPath: /etc/ssl/private
##     readOnly: true
##     secret:
##       secretName: planka-tls
##       items:
##         - key: tls.crt
##           path: server.crt
##         - key: tls.key
##           path: server.key
##   - name: temp-storage
##     mountPath: /tmp/planka-temp
##     readOnly: false
##     emptyDir:
##       sizeLimit: 1Gi

## Example configuration for OIDC with self-hosted Keycloak using custom CA
## (Requires pre-existing ConfigMap "ca-certificates")
## extraMounts:
##   - name: keycloak-ca
##     mountPath: /etc/ssl/certs/keycloak-ca.crt
##     subPath: ca.crt
##     readOnly: true
##     configMap:
##       name: ca-certificates
##
## extraEnv:
##   - name: NODE_EXTRA_CA_CERTS
##     value: "/etc/ssl/certs/keycloak-ca.crt"

extraContainers: []
## Extra sidecar containers
## Add additional containers to the PLANKA pod
##
## Example extraContainers:
## extraContainers:
##   - name: nginx-sidecar
##     image: nginx:latest
##     ports:
##       - containerPort: 8085
##         name: nginx-http
##   - name: log-collector
##     image: busybox:latest
##     command: ['sh', '-c', 'tail -f /var/log/app.log']
##     volumeMounts:
##       - name: planka
##         mountPath: /var/log
##         subPath: app-logs
##


================================================
FILE: client/.gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


================================================
FILE: client/index.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="PLANKA is the kanban-style project mastering tool for everyone" />
    <title>PLANKA</title>
    <link rel="icon" href="/favicon.ico" />
    <link rel="apple-touch-icon" href="/logo192.png" />
    <link rel="manifest" href="/manifest.json" />
  </head>
  <body id="app"></body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script type="module" src="/src/index.js"></script>
  </body>
</html>


================================================
FILE: client/package.json
================================================
{
  "name": "planka-client",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "vite build",
    "postinstall": "patch-package",
    "lint": "eslint --ext js,jsx src --report-unused-disable-directives",
    "start": "vite",
    "test": "jest",
    "test:acceptance": "cucumber-js --import tests/acceptance/cucumber.conf.js --import tests/acceptance/steps/**/*.js --format @cucumber/pretty-formatter tests"
  },
  "babel": {
    "presets": [
      "@babel/preset-env"
    ]
  },
  "eslintConfig": {
    "env": {
      "browser": true,
      "jest": true
    },
    "parser": "@babel/eslint-parser",
    "parserOptions": {
      "babelOptions": {
        "presets": [
          "airbnb"
        ]
      },
      "requireConfigFile": false
    },
    "plugins": [
      "prettier"
    ],
    "extends": [
      "airbnb",
      "airbnb/hooks",
      "plugin:prettier/recommended"
    ],
    "rules": {
      "import/no-unresolved": [
        "error",
        {
          "ignore": [
            "\\?url$",
            "\\.svg\\?react$"
          ]
        }
      ],
      "prettier/prettier": [
        "error",
        {
          "endOfLine": "auto",
          "printWidth": 100,
          "singleQuote": true,
          "trailingComma": "all"
        }
      ]
    },
    "overrides": [
      {
        "files": [
          "tests/acceptance/**/*.js"
        ],
        "rules": {
          "import/extensions": "off"
        },
        "globals": {
          "browser": "readonly",
          "context": "readonly",
          "page": "readonly"
        }
      }
    ]
  },
  "jest": {
    "transform": {
      "^.+\\.(js|jsx)$": "babel-jest"
    }
  },
  "overrides": {
    "react-mentions": {
      "@babel/runtime": "^7.28.6"
    }
  },
  "dependencies": {
    "@ballerina/highlightjs-ballerina": "^1.0.1",
    "@diplodoc/cut-extension": "^1.1.1",
    "@diplodoc/transform": "^4.70.2",
    "@gravity-ui/components": "^4.18.0",
    "@gravity-ui/markdown-editor": "^15.35.1",
    "@gravity-ui/uikit": "^7.34.0",
    "@juggle/resize-observer": "^3.4.0",
    "@vitejs/plugin-react": "^5.2.0",
    "browserslist-to-esbuild": "^2.1.1",
    "classnames": "^2.5.1",
    "date-fns": "^4.1.0",
    "dequal": "^2.0.3",
    "highlight.js": "^11.11.1",
    "highlightjs-4d": "^1.0.6",
    "highlightjs-alan": "^0.0.2",
    "highlightjs-apex": "^1.5.0",
    "highlightjs-blade": "^0.1.0",
    "highlightjs-cobol": "^0.3.3",
    "highlightjs-cshtml-razor": "^2.2.0",
    "highlightjs-gf": "^1.0.1",
    "highlightjs-jolie": "^0.1.8",
    "highlightjs-lean": "^1.2.0",
    "highlightjs-lookml": "^1.0.2",
    "highlightjs-macaulay2": "^0.5.0",
    "highlightjs-mlir": "^0.0.1",
    "highlightjs-qsharp": "^1.0.2",
    "highlightjs-redbol": "^2.1.2",
    "highlightjs-rpm-specfile": "^1.0.0",
    "highlightjs-sap-abap": "^0.3.0",
    "highlightjs-solidity": "^2.0.6",
    "highlightjs-supercollider": "^1.0.0",
    "highlightjs-svelte": "^1.0.6",
    "highlightjs-xsharp": "^1.0.0",
    "highlightjs-zenscript": "^2.0.0",
    "hightlightjs-papyrus": "^0.0.4",
    "history": "^5.3.0",
    "i18next": "^25.8.18",
    "i18next-browser-languagedetector": "^8.2.1",
    "initials": "^3.1.2",
    "javascript-time-ago": "^2.6.4",
    "js-cookie": "^3.0.5",
    "jwt-decode": "^4.0.0",
    "linkify-react": "^4.3.2",
    "linkifyjs": "^4.3.2",
    "lodash": "^4.17.23",
    "lowlight": "^3.3.0",
    "markdown-it": "^13.0.2",
    "nanoid": "^5.1.7",
    "papaparse": "^5.5.3",
    "patch-package": "^8.0.1",
    "photoswipe": "^5.4.4",
    "prop-types": "^15.8.1",
    "react": "18.2.0",
    "react-beautiful-dnd": "^13.1.1",
    "react-datepicker": "^9.1.0",
    "react-dom": "18.2.0",
    "react-dropzone": "^15.0.0",
    "react-frame-component": "^5.2.7",
    "react-hot-toast": "^2.6.0",
    "react-i18next": "^16.5.8",
    "react-input-mask": "^2.0.4",
    "react-intersection-observer": "^10.0.3",
    "react-mentions": "^4.4.10",
    "react-photoswipe-gallery": "^4.0.0",
    "react-redux": "^9.2.0",
    "react-router": "^7.13.1",
    "react-textarea-autosize": "^8.5.9",
    "react-time-ago": "^7.4.4",
    "redux": "^5.0.1",
    "redux-logger": "^3.0.6",
    "redux-orm": "^0.16.2",
    "redux-saga": "^1.4.2",
    "reselect": "^5.1.1",
    "sails.io.js": "^1.2.1",
    "sass-embedded": "^1.98.0",
    "semantic-ui-react": "^2.1.5",
    "socket.io-client": "^4.8.3",
    "validator": "^13.15.26",
    "vite": "^7.3.1",
    "vite-plugin-commonjs": "^0.10.4",
    "vite-plugin-node-polyfills": "^0.25.0",
    "vite-plugin-svgr": "^4.5.0",
    "zxcvbn": "^4.4.2"
  },
  "devDependencies": {
    "@babel/eslint-parser": "^7.28.6",
    "@babel/preset-env": "^7.29.0",
    "@cucumber/cucumber": "^12.7.0",
    "@cucumber/pretty-formatter": "^3.2.0",
    "@playwright/test": "^1.58.2",
    "babel-jest": "^30.3.0",
    "babel-preset-airbnb": "^5.0.0",
    "eslint": "^8.57.1",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-prettier": "^10.1.8",
    "eslint-plugin-import": "^2.32.0",
    "eslint-plugin-jsx-a11y": "^6.10.2",
    "eslint-plugin-prettier": "^5.5.5",
    "eslint-plugin-react": "^7.37.5",
    "eslint-plugin-react-hooks": "^4.6.2",
    "jest": "^30.3.0",
    "playwright": "^1.58.2",
    "prettier": "3.8.1"
  }
}


================================================
FILE: client/patches/@diplodoc+transform+4.70.2.patch
================================================
diff --git a/node_modules/@diplodoc/transform/lib/md.js b/node_modules/@diplodoc/transform/lib/md.js
index c9faa96..e4bef9b 100644
--- a/node_modules/@diplodoc/transform/lib/md.js
+++ b/node_modules/@diplodoc/transform/lib/md.js
@@ -107,8 +107,12 @@ function initPlugins(md, options, pluginOptions) {
     }
     md.use(ol_attr_conversion_1.olAttrConversion);
     plugins.forEach((plugin) => md.use(plugin, pluginOptions));
-    if (linkify && linkifyTlds) {
-        md.linkify.tlds(linkifyTlds, true);
+    if (linkify) {
+        if (linkifyTlds) {
+            md.linkify.tlds(linkifyTlds, true);
+        } else if (linkifyTlds === null) {
+            md.linkify.set({ fuzzyLink: false });
+        }
     }
 }
 function initParser(md, options, env, pluginOptions) {


================================================
FILE: client/patches/@gravity-ui+markdown-editor+15.35.1.patch
================================================
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
index c0d13c3..4c6e4e9 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js
@@ -107,7 +107,6 @@ export const BundlePreset = (builder, opts) => {
             enableNewImageSizeCalculation: opts.enableNewImageSizeCalculation,
             ...opts.imgSize,
         },
-        checkbox: { checkboxLabelPlaceholder: () => i18nPlaceholder('checkbox'), ...opts.checkbox },
         deflist: {
             deflistTermPlaceholder: () => i18nPlaceholder('deflist_term'),
             deflistDescPlaceholder: () => i18nPlaceholder('deflist_desc'),
@@ -128,11 +127,6 @@ export const BundlePreset = (builder, opts) => {
             ...opts.yfmTable,
             controls: opts.mobile ? false : opts.yfmTable?.controls,
         },
-        yfmFile: {
-            fileUploadHandler: opts.fileUploadHandler,
-            needToSetDimensionsForUploadedImages: opts.needToSetDimensionsForUploadedImages,
-            ...opts.yfmFile,
-        },
         yfmHeading: {
             h1Key: f.toPM(A.Heading1),
             h2Key: f.toPM(A.Heading2),
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/core/ExtensionsManager.js b/node_modules/@gravity-ui/markdown-editor/build/esm/core/ExtensionsManager.js
index 8aefe20..99e59e3 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/core/ExtensionsManager.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/core/ExtensionsManager.js
@@ -42,6 +42,9 @@ export class ExtensionsManager {
         if (options.linkifyTlds) {
             this.#mdForMarkup.linkify.tlds(options.linkifyTlds, true);
             this.#mdForText.linkify.tlds(options.linkifyTlds, true);
+        } else if (options.linkifyTlds === null) {
+            this.#mdForMarkup.linkify.set({ fuzzyLink: false });
+            this.#mdForText.linkify.set({ fuzzyLink: false });
         }
         if (options.pmTransformers) {
             this.#pmTransformers = options.pmTransformers;
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
index 5eec9bb..3abd31a 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/markdown/CodeBlock/CodeBlockHighlight/TooltipPlugin/index.js
@@ -75,12 +75,6 @@ export const codeLangSelectTooltipViewCreator = (view, langItems, mapping = {},
                                     dispatch: view.dispatch,
                                 }),
                             },
-                        {
-                            id: 'code-block-copy',
-                            type: ToolbarDataType.ReactNodeFn,
-                            width: 28,
-                            content: () => _jsx(ClipboardButton, { text: node.textContent }),
-                        },
                     ].filter(isTruthy),
                     [
                         {
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js
index 212c583..b709383 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/extensions/yfm/YfmNote/YfmNoteSpecs/index.js
@@ -10,7 +10,7 @@ export { noteType, noteTitleType } from "./utils.js";
 export const YfmNoteSpecs = (builder, opts) => {
     const schemaSpecs = getSchemaSpecs(opts, builder.context.get('placeholder'));
     builder
-        .configureMd((md) => md.use(yfmPlugin, { log, lang: getConfig().lang || 'en' }))
+        .configureMd((md) => md.use(yfmPlugin, { log, lang: getConfig().lang || 'en', notesAutotitle: false }))
         .addNode(NoteNode.Note, () => ({
         spec: schemaSpecs[NoteNode.Note],
         toMd: serializerTokens[NoteNode.Note],
diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js b/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
index ed2a9db..77f6d08 100644
--- a/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
+++ b/node_modules/@gravity-ui/markdown-editor/build/esm/presets/yfm.js
@@ -1,5 +1,5 @@
 import { Deflist, Subscript, Superscript, Underline, } from "../extensions/markdown/index.js";
-import { Checkbox, ImgSize, Monospace, Video, YfmConfigs, YfmCut, YfmFile, YfmHeading, YfmNote, YfmTable, YfmTabs, } from "../extensions/yfm/index.js";
+import { ImgSize, Monospace, Video, YfmConfigs, YfmCut, YfmHeading, YfmNote, YfmTable, } from "../extensions/yfm/index.js";
 import { DefaultPreset } from "./default.js";
 export const YfmPreset = (builder, opts) => {
     builder.use(DefaultPreset, { ...opts, image: false, heading: false });
@@ -8,16 +8,13 @@ export const YfmPreset = (builder, opts) => {
         .use(Subscript)
         .use(Superscript)
         .use(Underline, opts.underline ?? {})
-        .use(Checkbox, opts.checkbox ?? {})
         .use(ImgSize, opts.imgSize ?? {})
         .use(Monospace)
         .use(Video, opts.video ?? {})
         .use(YfmConfigs, opts.yfmConfigs ?? {})
         .use(YfmCut, opts.yfmCut ?? {})
         .use(YfmNote, opts.yfmNote ?? {})
-        .use(YfmFile, opts.yfmFile ?? {})
         .use(YfmHeading, opts.yfmHeading ?? {})
-        .use(YfmTable, opts.yfmTable ?? {})
-        .use(YfmTabs);
+        .use(YfmTable, opts.yfmTable ?? {});
 };
 //# sourceMappingURL=yfm.js.map


================================================
FILE: client/patches/react-mentions+4.4.10.patch
================================================
diff --git a/node_modules/react-mentions/dist/react-mentions.esm.js b/node_modules/react-mentions/dist/react-mentions.esm.js
index 2efebba..b244446 100644
--- a/node_modules/react-mentions/dist/react-mentions.esm.js
+++ b/node_modules/react-mentions/dist/react-mentions.esm.js
@@ -1426,7 +1426,7 @@ var MentionsInput = /*#__PURE__*/function (_React$Component) {
 
       var mentions = getMentions(newValue, config);
 
-      if (ev.nativeEvent.isComposing && selectionStart === selectionEnd) {
+      if ((ev.nativeEvent.isComposing || newValue.length < value.length) && selectionStart === selectionEnd) {
         _this.updateMentionsQueries(_this.inputElement.value, selectionStart);
       } // Propagate change
       // let handleChange = this.getOnChange(this.props) || emptyFunction;
@@ -1454,7 +1454,9 @@ var MentionsInput = /*#__PURE__*/function (_React$Component) {
       var el = _this.inputElement;
 
       if (ev.target.selectionStart === ev.target.selectionEnd) {
-        _this.updateMentionsQueries(el.value, ev.target.selectionStart);
+        requestAnimationFrame(function () {
+          _this.updateMentionsQueries(el.value, ev.target.selectionStart);
+        });
       } else {
         _this.clearSuggestions();
       } // sync highlighters scroll position


================================================
FILE: client/patches/redux-orm+0.16.2.patch
================================================
diff --git a/node_modules/redux-orm/dist/redux-orm.js b/node_modules/redux-orm/dist/redux-orm.js
index 9298fea..d53d03e 100644
--- a/node_modules/redux-orm/dist/redux-orm.js
+++ b/node_modules/redux-orm/dist/redux-orm.js
@@ -103,7 +103,7 @@ return /******/ (function(modules) { // webpackBootstrap
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-eval("function _arrayLikeToArray(arr, len) {\n  if (len == null || len > arr.length) len = arr.length;\n\n  for (var i = 0, arr2 = new Array(len); i < len; i++) {\n    arr2[i] = arr[i];\n  }\n\n  return arr2;\n}\n\nmodule.exports = _arrayLikeToArray;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5TGlrZVRvQXJyYXkuanM/NWE0MyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBOztBQUVBLHdDQUF3QyxTQUFTO0FBQ2pEO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5TGlrZVRvQXJyYXkuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBfYXJyYXlMaWtlVG9BcnJheShhcnIsIGxlbikge1xuICBpZiAobGVuID09IG51bGwgfHwgbGVuID4gYXJyLmxlbmd0aCkgbGVuID0gYXJyLmxlbmd0aDtcblxuICBmb3IgKHZhciBpID0gMCwgYXJyMiA9IG5ldyBBcnJheShsZW4pOyBpIDwgbGVuOyBpKyspIHtcbiAgICBhcnIyW2ldID0gYXJyW2ldO1xuICB9XG5cbiAgcmV0dXJuIGFycjI7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX2FycmF5TGlrZVRvQXJyYXk7Il0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\n");
+eval("function _arrayLikeToArray(arr, len) {\n  if (len == null || len > arr.length) len = arr.length;\n\n  for (var i = 0, arr2 = new Array(len); i < len; i++) {\n    arr2[i] = arr[i];\n  }\n\n  return arr2;\n}\n\nmodule.exports = _arrayLikeToArray;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5TGlrZVRvQXJyYXkuanM/NWE0MyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBOztBQUVBLHdDQUF3QyxTQUFTO0FBQ2pEO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvYXJyYXlMaWtlVG9BcnJheS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9hcnJheUxpa2VUb0FycmF5KGFyciwgbGVuKSB7XG4gIGlmIChsZW4gPT0gbnVsbCB8fCBsZW4gPiBhcnIubGVuZ3RoKSBsZW4gPSBhcnIubGVuZ3RoO1xuXG4gIGZvciAodmFyIGkgPSAwLCBhcnIyID0gbmV3IEFycmF5KGxlbik7IGkgPCBsZW47IGkrKykge1xuICAgIGFycjJbaV0gPSBhcnJbaV07XG4gIH1cblxuICByZXR1cm4gYXJyMjtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfYXJyYXlMaWtlVG9BcnJheTtcbm1vZHVsZS5leHBvcnRzW1wiZGVmYXVsdFwiXSA9IG1vZHVsZS5leHBvcnRzLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\n");
 
 /***/ }),
 
@@ -114,7 +114,7 @@ eval("function _arrayLikeToArray(arr, len) {\n  if (len == null || len > arr.len
 /*! no static exports found */
 /***/ (function(module, exports, __webpack_require__) {
 
-eval("var arrayLikeToArray = __webpack_require__(/*! ./arrayLikeToArray */ \"./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\");\n\nfunction _arrayWithoutHoles(arr) {\n  if (Array.isArray(arr)) return arrayLikeToArray(arr);\n}\n\nmodule.exports = _arrayWithoutHoles;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5V2l0aG91dEhvbGVzLmpzPzIyMzYiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsdUJBQXVCLG1CQUFPLENBQUMscUZBQW9COztBQUVuRDtBQUNBO0FBQ0E7O0FBRUEiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9hcnJheVdpdGhvdXRIb2xlcy5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbInZhciBhcnJheUxpa2VUb0FycmF5ID0gcmVxdWlyZShcIi4vYXJyYXlMaWtlVG9BcnJheVwiKTtcblxuZnVuY3Rpb24gX2FycmF5V2l0aG91dEhvbGVzKGFycikge1xuICBpZiAoQXJyYXkuaXNBcnJheShhcnIpKSByZXR1cm4gYXJyYXlMaWtlVG9BcnJheShhcnIpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF9hcnJheVdpdGhvdXRIb2xlczsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/arrayWithoutHoles.js\n");
+eval("var arrayLikeToArray = __webpack_require__(/*! ./arrayLikeToArray.js */ \"./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\");\n\nfunction _arrayWithoutHoles(arr) {\n  if (Array.isArray(arr)) return arrayLikeToArray(arr);\n}\n\nmodule.exports = _arrayWithoutHoles;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5V2l0aG91dEhvbGVzLmpzPzIyMzYiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsdUJBQXVCLG1CQUFPLENBQUMsd0ZBQXVCOztBQUV0RDtBQUNBO0FBQ0E7O0FBRUE7QUFDQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2FycmF5V2l0aG91dEhvbGVzLmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsidmFyIGFycmF5TGlrZVRvQXJyYXkgPSByZXF1aXJlKFwiLi9hcnJheUxpa2VUb0FycmF5LmpzXCIpO1xuXG5mdW5jdGlvbiBfYXJyYXlXaXRob3V0SG9sZXMoYXJyKSB7XG4gIGlmIChBcnJheS5pc0FycmF5KGFycikpIHJldHVybiBhcnJheUxpa2VUb0FycmF5KGFycik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX2FycmF5V2l0aG91dEhvbGVzO1xubW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlOyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/arrayWithoutHoles.js\n");
 
 /***/ }),
 
@@ -125,7 +125,7 @@ eval("var arrayLikeToArray = __webpack_require__(/*! ./arrayLikeToArray */ \"./n
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-eval("function _defineProperties(target, props) {\n  for (var i = 0; i < props.length; i++) {\n    var descriptor = props[i];\n    descriptor.enumerable = descriptor.enumerable || false;\n    descriptor.configurable = true;\n    if (\"value\" in descriptor) descriptor.writable = true;\n    Object.defineProperty(target, descriptor.key, descriptor);\n  }\n}\n\nfunction _createClass(Constructor, protoProps, staticProps) {\n  if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n  if (staticProps) _defineProperties(Constructor, staticProps);\n  return Constructor;\n}\n\nmodule.exports = _createClass;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2NyZWF0ZUNsYXNzLmpzPzViYzMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQSxpQkFBaUIsa0JBQWtCO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9jcmVhdGVDbGFzcy5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9kZWZpbmVQcm9wZXJ0aWVzKHRhcmdldCwgcHJvcHMpIHtcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBwcm9wcy5sZW5ndGg7IGkrKykge1xuICAgIHZhciBkZXNjcmlwdG9yID0gcHJvcHNbaV07XG4gICAgZGVzY3JpcHRvci5lbnVtZXJhYmxlID0gZGVzY3JpcHRvci5lbnVtZXJhYmxlIHx8IGZhbHNlO1xuICAgIGRlc2NyaXB0b3IuY29uZmlndXJhYmxlID0gdHJ1ZTtcbiAgICBpZiAoXCJ2YWx1ZVwiIGluIGRlc2NyaXB0b3IpIGRlc2NyaXB0b3Iud3JpdGFibGUgPSB0cnVlO1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGRlc2NyaXB0b3Iua2V5LCBkZXNjcmlwdG9yKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBfY3JlYXRlQ2xhc3MoQ29uc3RydWN0b3IsIHByb3RvUHJvcHMsIHN0YXRpY1Byb3BzKSB7XG4gIGlmIChwcm90b1Byb3BzKSBfZGVmaW5lUHJvcGVydGllcyhDb25zdHJ1Y3Rvci5wcm90b3R5cGUsIHByb3RvUHJvcHMpO1xuICBpZiAoc3RhdGljUHJvcHMpIF9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLCBzdGF0aWNQcm9wcyk7XG4gIHJldHVybiBDb25zdHJ1Y3Rvcjtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfY3JlYXRlQ2xhc3M7Il0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/createClass.js\n");
+eval("function _defineProperties(target, props) {\n  for (var i = 0; i < props.length; i++) {\n    var descriptor = props[i];\n    descriptor.enumerable = descriptor.enumerable || false;\n    descriptor.configurable = true;\n    if (\"value\" in descriptor) descriptor.writable = true;\n    Object.defineProperty(target, descriptor.key, descriptor);\n  }\n}\n\nfunction _createClass(Constructor, protoProps, staticProps) {\n  if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n  if (staticProps) _defineProperties(Constructor, staticProps);\n  return Constructor;\n}\n\nmodule.exports = _createClass;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2NyZWF0ZUNsYXNzLmpzPzViYzMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQSxpQkFBaUIsa0JBQWtCO0FBQ25DO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2NyZWF0ZUNsYXNzLmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX2RlZmluZVByb3BlcnRpZXModGFyZ2V0LCBwcm9wcykge1xuICBmb3IgKHZhciBpID0gMDsgaSA8IHByb3BzLmxlbmd0aDsgaSsrKSB7XG4gICAgdmFyIGRlc2NyaXB0b3IgPSBwcm9wc1tpXTtcbiAgICBkZXNjcmlwdG9yLmVudW1lcmFibGUgPSBkZXNjcmlwdG9yLmVudW1lcmFibGUgfHwgZmFsc2U7XG4gICAgZGVzY3JpcHRvci5jb25maWd1cmFibGUgPSB0cnVlO1xuICAgIGlmIChcInZhbHVlXCIgaW4gZGVzY3JpcHRvcikgZGVzY3JpcHRvci53cml0YWJsZSA9IHRydWU7XG4gICAgT2JqZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgZGVzY3JpcHRvci5rZXksIGRlc2NyaXB0b3IpO1xuICB9XG59XG5cbmZ1bmN0aW9uIF9jcmVhdGVDbGFzcyhDb25zdHJ1Y3RvciwgcHJvdG9Qcm9wcywgc3RhdGljUHJvcHMpIHtcbiAgaWYgKHByb3RvUHJvcHMpIF9kZWZpbmVQcm9wZXJ0aWVzKENvbnN0cnVjdG9yLnByb3RvdHlwZSwgcHJvdG9Qcm9wcyk7XG4gIGlmIChzdGF0aWNQcm9wcykgX2RlZmluZVByb3BlcnRpZXMoQ29uc3RydWN0b3IsIHN0YXRpY1Byb3BzKTtcbiAgcmV0dXJuIENvbnN0cnVjdG9yO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF9jcmVhdGVDbGFzcztcbm1vZHVsZS5leHBvcnRzW1wiZGVmYXVsdFwiXSA9IG1vZHVsZS5leHBvcnRzLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/createClass.js\n");
 
 /***/ }),
 
@@ -134,9 +134,9 @@ eval("function _defineProperties(target, props) {\n  for (var i = 0; i < props.l
   !*** ./node_modules/@babel/runtime/helpers/inheritsLoose.js ***!
   \**************************************************************/
 /*! no static exports found */
-/***/ (function(module, exports) {
+/***/ (function(module, exports, __webpack_require__) {
 
-eval("function _inheritsLoose(subClass, superClass) {\n  subClass.prototype = Object.create(superClass.prototype);\n  subClass.prototype.constructor = subClass;\n  subClass.__proto__ = superClass;\n}\n\nmodule.exports = _inheritsLoose;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2luaGVyaXRzTG9vc2UuanM/NTViNSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvaW5oZXJpdHNMb29zZS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9pbmhlcml0c0xvb3NlKHN1YkNsYXNzLCBzdXBlckNsYXNzKSB7XG4gIHN1YkNsYXNzLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoc3VwZXJDbGFzcy5wcm90b3R5cGUpO1xuICBzdWJDbGFzcy5wcm90b3R5cGUuY29uc3RydWN0b3IgPSBzdWJDbGFzcztcbiAgc3ViQ2xhc3MuX19wcm90b19fID0gc3VwZXJDbGFzcztcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfaW5oZXJpdHNMb29zZTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/inheritsLoose.js\n");
+eval("var setPrototypeOf = __webpack_require__(/*! ./setPrototypeOf.js */ \"./node_modules/@babel/runtime/helpers/setPrototypeOf.js\");\n\nfunction _inheritsLoose(subClass, superClass) {\n  subClass.prototype = Object.create(superClass.prototype);\n  subClass.prototype.constructor = subClass;\n  setPrototypeOf(subClass, superClass);\n}\n\nmodule.exports = _inheritsLoose;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2luaGVyaXRzTG9vc2UuanM/NTViNSJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxxQkFBcUIsbUJBQU8sQ0FBQyxvRkFBcUI7O0FBRWxEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2luaGVyaXRzTG9vc2UuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgc2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKFwiLi9zZXRQcm90b3R5cGVPZi5qc1wiKTtcblxuZnVuY3Rpb24gX2luaGVyaXRzTG9vc2Uoc3ViQ2xhc3MsIHN1cGVyQ2xhc3MpIHtcbiAgc3ViQ2xhc3MucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShzdXBlckNsYXNzLnByb3RvdHlwZSk7XG4gIHN1YkNsYXNzLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IHN1YkNsYXNzO1xuICBzZXRQcm90b3R5cGVPZihzdWJDbGFzcywgc3VwZXJDbGFzcyk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX2luaGVyaXRzTG9vc2U7XG5tb2R1bGUuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSBtb2R1bGUuZXhwb3J0cywgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWU7Il0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/inheritsLoose.js\n");
 
 /***/ }),
 
@@ -147,7 +147,7 @@ eval("function _inheritsLoose(subClass, superClass) {\n  subClass.prototype = Ob
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-eval("function _iterableToArray(iter) {\n  if (typeof Symbol !== \"undefined\" && Symbol.iterator in Object(iter)) return Array.from(iter);\n}\n\nmodule.exports = _iterableToArray;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2l0ZXJhYmxlVG9BcnJheS5qcz8xMWIwIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTs7QUFFQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2l0ZXJhYmxlVG9BcnJheS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9pdGVyYWJsZVRvQXJyYXkoaXRlcikge1xuICBpZiAodHlwZW9mIFN5bWJvbCAhPT0gXCJ1bmRlZmluZWRcIiAmJiBTeW1ib2wuaXRlcmF0b3IgaW4gT2JqZWN0KGl0ZXIpKSByZXR1cm4gQXJyYXkuZnJvbShpdGVyKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfaXRlcmFibGVUb0FycmF5OyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/iterableToArray.js\n");
+eval("function _iterableToArray(iter) {\n  if (typeof Symbol !== \"undefined\" && iter[Symbol.iterator] != null || iter[\"@@iterator\"] != null) return Array.from(iter);\n}\n\nmodule.exports = _iterableToArray;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL2l0ZXJhYmxlVG9BcnJheS5qcz8xMWIwIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvaXRlcmFibGVUb0FycmF5LmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX2l0ZXJhYmxlVG9BcnJheShpdGVyKSB7XG4gIGlmICh0eXBlb2YgU3ltYm9sICE9PSBcInVuZGVmaW5lZFwiICYmIGl0ZXJbU3ltYm9sLml0ZXJhdG9yXSAhPSBudWxsIHx8IGl0ZXJbXCJAQGl0ZXJhdG9yXCJdICE9IG51bGwpIHJldHVybiBBcnJheS5mcm9tKGl0ZXIpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF9pdGVyYWJsZVRvQXJyYXk7XG5tb2R1bGUuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSBtb2R1bGUuZXhwb3J0cywgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWU7Il0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/iterableToArray.js\n");
 
 /***/ }),
 
@@ -158,7 +158,18 @@ eval("function _iterableToArray(iter) {\n  if (typeof Symbol !== \"undefined\" &
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-eval("function _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\n\nmodule.exports = _nonIterableSpread;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL25vbkl0ZXJhYmxlU3ByZWFkLmpzPzA2NzYiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBOztBQUVBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvbm9uSXRlcmFibGVTcHJlYWQuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBfbm9uSXRlcmFibGVTcHJlYWQoKSB7XG4gIHRocm93IG5ldyBUeXBlRXJyb3IoXCJJbnZhbGlkIGF0dGVtcHQgdG8gc3ByZWFkIG5vbi1pdGVyYWJsZSBpbnN0YW5jZS5cXG5JbiBvcmRlciB0byBiZSBpdGVyYWJsZSwgbm9uLWFycmF5IG9iamVjdHMgbXVzdCBoYXZlIGEgW1N5bWJvbC5pdGVyYXRvcl0oKSBtZXRob2QuXCIpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF9ub25JdGVyYWJsZVNwcmVhZDsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/nonIterableSpread.js\n");
+eval("function _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\n\nmodule.exports = _nonIterableSpread;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL25vbkl0ZXJhYmxlU3ByZWFkLmpzPzA2NzYiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy9ub25JdGVyYWJsZVNwcmVhZC5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF9ub25JdGVyYWJsZVNwcmVhZCgpIHtcbiAgdGhyb3cgbmV3IFR5cGVFcnJvcihcIkludmFsaWQgYXR0ZW1wdCB0byBzcHJlYWQgbm9uLWl0ZXJhYmxlIGluc3RhbmNlLlxcbkluIG9yZGVyIHRvIGJlIGl0ZXJhYmxlLCBub24tYXJyYXkgb2JqZWN0cyBtdXN0IGhhdmUgYSBbU3ltYm9sLml0ZXJhdG9yXSgpIG1ldGhvZC5cIik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX25vbkl0ZXJhYmxlU3ByZWFkO1xubW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlOyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/nonIterableSpread.js\n");
+
+/***/ }),
+
+/***/ "./node_modules/@babel/runtime/helpers/setPrototypeOf.js":
+/*!***************************************************************!*\
+  !*** ./node_modules/@babel/runtime/helpers/setPrototypeOf.js ***!
+  \***************************************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+eval("function _setPrototypeOf(o, p) {\n  module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n    o.__proto__ = p;\n    return o;\n  };\n\n  module.exports[\"default\"] = module.exports, module.exports.__esModule = true;\n  return _setPrototypeOf(o, p);\n}\n\nmodule.exports = _setPrototypeOf;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3NldFByb3RvdHlwZU9mLmpzPzRhNGIiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3NldFByb3RvdHlwZU9mLmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX3NldFByb3RvdHlwZU9mKG8sIHApIHtcbiAgbW9kdWxlLmV4cG9ydHMgPSBfc2V0UHJvdG90eXBlT2YgPSBPYmplY3Quc2V0UHJvdG90eXBlT2YgfHwgZnVuY3Rpb24gX3NldFByb3RvdHlwZU9mKG8sIHApIHtcbiAgICBvLl9fcHJvdG9fXyA9IHA7XG4gICAgcmV0dXJuIG87XG4gIH07XG5cbiAgbW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlO1xuICByZXR1cm4gX3NldFByb3RvdHlwZU9mKG8sIHApO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF9zZXRQcm90b3R5cGVPZjtcbm1vZHVsZS5leHBvcnRzW1wiZGVmYXVsdFwiXSA9IG1vZHVsZS5leHBvcnRzLCBtb2R1bGUuZXhwb3J0cy5fX2VzTW9kdWxlID0gdHJ1ZTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/setPrototypeOf.js\n");
 
 /***/ }),
 
@@ -169,7 +180,7 @@ eval("function _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt t
 /*! no static exports found */
 /***/ (function(module, exports, __webpack_require__) {
 
-eval("var arrayWithoutHoles = __webpack_require__(/*! ./arrayWithoutHoles */ \"./node_modules/@babel/runtime/helpers/arrayWithoutHoles.js\");\n\nvar iterableToArray = __webpack_require__(/*! ./iterableToArray */ \"./node_modules/@babel/runtime/helpers/iterableToArray.js\");\n\nvar unsupportedIterableToArray = __webpack_require__(/*! ./unsupportedIterableToArray */ \"./node_modules/@babel/runtime/helpers/unsupportedIterableToArray.js\");\n\nvar nonIterableSpread = __webpack_require__(/*! ./nonIterableSpread */ \"./node_modules/@babel/runtime/helpers/nonIterableSpread.js\");\n\nfunction _toConsumableArray(arr) {\n  return arrayWithoutHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableSpread();\n}\n\nmodule.exports = _toConsumableArray;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3RvQ29uc3VtYWJsZUFycmF5LmpzPzQ0OGEiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCLG1CQUFPLENBQUMsdUZBQXFCOztBQUVyRCxzQkFBc0IsbUJBQU8sQ0FBQyxtRkFBbUI7O0FBRWpELGlDQUFpQyxtQkFBTyxDQUFDLHlHQUE4Qjs7QUFFdkUsd0JBQXdCLG1CQUFPLENBQUMsdUZBQXFCOztBQUVyRDtBQUNBO0FBQ0E7O0FBRUEiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy90b0NvbnN1bWFibGVBcnJheS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbInZhciBhcnJheVdpdGhvdXRIb2xlcyA9IHJlcXVpcmUoXCIuL2FycmF5V2l0aG91dEhvbGVzXCIpO1xuXG52YXIgaXRlcmFibGVUb0FycmF5ID0gcmVxdWlyZShcIi4vaXRlcmFibGVUb0FycmF5XCIpO1xuXG52YXIgdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkgPSByZXF1aXJlKFwiLi91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheVwiKTtcblxudmFyIG5vbkl0ZXJhYmxlU3ByZWFkID0gcmVxdWlyZShcIi4vbm9uSXRlcmFibGVTcHJlYWRcIik7XG5cbmZ1bmN0aW9uIF90b0NvbnN1bWFibGVBcnJheShhcnIpIHtcbiAgcmV0dXJuIGFycmF5V2l0aG91dEhvbGVzKGFycikgfHwgaXRlcmFibGVUb0FycmF5KGFycikgfHwgdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkoYXJyKSB8fCBub25JdGVyYWJsZVNwcmVhZCgpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IF90b0NvbnN1bWFibGVBcnJheTsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/toConsumableArray.js\n");
+eval("var arrayWithoutHoles = __webpack_require__(/*! ./arrayWithoutHoles.js */ \"./node_modules/@babel/runtime/helpers/arrayWithoutHoles.js\");\n\nvar iterableToArray = __webpack_require__(/*! ./iterableToArray.js */ \"./node_modules/@babel/runtime/helpers/iterableToArray.js\");\n\nvar unsupportedIterableToArray = __webpack_require__(/*! ./unsupportedIterableToArray.js */ \"./node_modules/@babel/runtime/helpers/unsupportedIterableToArray.js\");\n\nvar nonIterableSpread = __webpack_require__(/*! ./nonIterableSpread.js */ \"./node_modules/@babel/runtime/helpers/nonIterableSpread.js\");\n\nfunction _toConsumableArray(arr) {\n  return arrayWithoutHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableSpread();\n}\n\nmodule.exports = _toConsumableArray;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3RvQ29uc3VtYWJsZUFycmF5LmpzPzQ0OGEiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCLG1CQUFPLENBQUMsMEZBQXdCOztBQUV4RCxzQkFBc0IsbUJBQU8sQ0FBQyxzRkFBc0I7O0FBRXBELGlDQUFpQyxtQkFBTyxDQUFDLDRHQUFpQzs7QUFFMUUsd0JBQXdCLG1CQUFPLENBQUMsMEZBQXdCOztBQUV4RDtBQUNBO0FBQ0E7O0FBRUE7QUFDQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3RvQ29uc3VtYWJsZUFycmF5LmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsidmFyIGFycmF5V2l0aG91dEhvbGVzID0gcmVxdWlyZShcIi4vYXJyYXlXaXRob3V0SG9sZXMuanNcIik7XG5cbnZhciBpdGVyYWJsZVRvQXJyYXkgPSByZXF1aXJlKFwiLi9pdGVyYWJsZVRvQXJyYXkuanNcIik7XG5cbnZhciB1bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSA9IHJlcXVpcmUoXCIuL3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5LmpzXCIpO1xuXG52YXIgbm9uSXRlcmFibGVTcHJlYWQgPSByZXF1aXJlKFwiLi9ub25JdGVyYWJsZVNwcmVhZC5qc1wiKTtcblxuZnVuY3Rpb24gX3RvQ29uc3VtYWJsZUFycmF5KGFycikge1xuICByZXR1cm4gYXJyYXlXaXRob3V0SG9sZXMoYXJyKSB8fCBpdGVyYWJsZVRvQXJyYXkoYXJyKSB8fCB1bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShhcnIpIHx8IG5vbkl0ZXJhYmxlU3ByZWFkKCk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX3RvQ29uc3VtYWJsZUFycmF5O1xubW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlOyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/toConsumableArray.js\n");
 
 /***/ }),
 
@@ -180,7 +191,7 @@ eval("var arrayWithoutHoles = __webpack_require__(/*! ./arrayWithoutHoles */ \".
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-eval("function _typeof(obj) {\n  \"@babel/helpers - typeof\";\n\n  if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n    module.exports = _typeof = function _typeof(obj) {\n      return typeof obj;\n    };\n  } else {\n    module.exports = _typeof = function _typeof(obj) {\n      return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n    };\n  }\n\n  return _typeof(obj);\n}\n\nmodule.exports = _typeof;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3R5cGVvZi5qcz83MDM3Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3R5cGVvZi5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbImZ1bmN0aW9uIF90eXBlb2Yob2JqKSB7XG4gIFwiQGJhYmVsL2hlbHBlcnMgLSB0eXBlb2ZcIjtcblxuICBpZiAodHlwZW9mIFN5bWJvbCA9PT0gXCJmdW5jdGlvblwiICYmIHR5cGVvZiBTeW1ib2wuaXRlcmF0b3IgPT09IFwic3ltYm9sXCIpIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IF90eXBlb2YgPSBmdW5jdGlvbiBfdHlwZW9mKG9iaikge1xuICAgICAgcmV0dXJuIHR5cGVvZiBvYmo7XG4gICAgfTtcbiAgfSBlbHNlIHtcbiAgICBtb2R1bGUuZXhwb3J0cyA9IF90eXBlb2YgPSBmdW5jdGlvbiBfdHlwZW9mKG9iaikge1xuICAgICAgcmV0dXJuIG9iaiAmJiB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgb2JqLmNvbnN0cnVjdG9yID09PSBTeW1ib2wgJiYgb2JqICE9PSBTeW1ib2wucHJvdG90eXBlID8gXCJzeW1ib2xcIiA6IHR5cGVvZiBvYmo7XG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiBfdHlwZW9mKG9iaik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX3R5cGVvZjsiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/typeof.js\n");
+eval("function _typeof(obj) {\n  \"@babel/helpers - typeof\";\n\n  if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") {\n    module.exports = _typeof = function _typeof(obj) {\n      return typeof obj;\n    };\n\n    module.exports[\"default\"] = module.exports, module.exports.__esModule = true;\n  } else {\n    module.exports = _typeof = function _typeof(obj) {\n      return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj;\n    };\n\n    module.exports[\"default\"] = module.exports, module.exports.__esModule = true;\n  }\n\n  return _typeof(obj);\n}\n\nmodule.exports = _typeof;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3R5cGVvZi5qcz83MDM3Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvdHlwZW9mLmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiZnVuY3Rpb24gX3R5cGVvZihvYmopIHtcbiAgXCJAYmFiZWwvaGVscGVycyAtIHR5cGVvZlwiO1xuXG4gIGlmICh0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgJiYgdHlwZW9mIFN5bWJvbC5pdGVyYXRvciA9PT0gXCJzeW1ib2xcIikge1xuICAgIG1vZHVsZS5leHBvcnRzID0gX3R5cGVvZiA9IGZ1bmN0aW9uIF90eXBlb2Yob2JqKSB7XG4gICAgICByZXR1cm4gdHlwZW9mIG9iajtcbiAgICB9O1xuXG4gICAgbW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlO1xuICB9IGVsc2Uge1xuICAgIG1vZHVsZS5leHBvcnRzID0gX3R5cGVvZiA9IGZ1bmN0aW9uIF90eXBlb2Yob2JqKSB7XG4gICAgICByZXR1cm4gb2JqICYmIHR5cGVvZiBTeW1ib2wgPT09IFwiZnVuY3Rpb25cIiAmJiBvYmouY29uc3RydWN0b3IgPT09IFN5bWJvbCAmJiBvYmogIT09IFN5bWJvbC5wcm90b3R5cGUgPyBcInN5bWJvbFwiIDogdHlwZW9mIG9iajtcbiAgICB9O1xuXG4gICAgbW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlO1xuICB9XG5cbiAgcmV0dXJuIF90eXBlb2Yob2JqKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfdHlwZW9mO1xubW9kdWxlLmV4cG9ydHNbXCJkZWZhdWx0XCJdID0gbW9kdWxlLmV4cG9ydHMsIG1vZHVsZS5leHBvcnRzLl9fZXNNb2R1bGUgPSB0cnVlOyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/typeof.js\n");
 
 /***/ }),
 
@@ -191,7 +202,7 @@ eval("function _typeof(obj) {\n  \"@babel/helpers - typeof\";\n\n  if (typeof Sy
 /*! no static exports found */
 /***/ (function(module, exports, __webpack_require__) {
 
-eval("var arrayLikeToArray = __webpack_require__(/*! ./arrayLikeToArray */ \"./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\");\n\nfunction _unsupportedIterableToArray(o, minLen) {\n  if (!o) return;\n  if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n  var n = Object.prototype.toString.call(o).slice(8, -1);\n  if (n === \"Object\" && o.constructor) n = o.constructor.name;\n  if (n === \"Map\" || n === \"Set\") return Array.from(o);\n  if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}\n\nmodule.exports = _unsupportedIterableToArray;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5LmpzPzY2MTMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsdUJBQXVCLG1CQUFPLENBQUMscUZBQW9COztBQUVuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBIiwiZmlsZSI6Ii4vbm9kZV9tb2R1bGVzL0BiYWJlbC9ydW50aW1lL2hlbHBlcnMvdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXkuanMuanMiLCJzb3VyY2VzQ29udGVudCI6WyJ2YXIgYXJyYXlMaWtlVG9BcnJheSA9IHJlcXVpcmUoXCIuL2FycmF5TGlrZVRvQXJyYXlcIik7XG5cbmZ1bmN0aW9uIF91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheShvLCBtaW5MZW4pIHtcbiAgaWYgKCFvKSByZXR1cm47XG4gIGlmICh0eXBlb2YgbyA9PT0gXCJzdHJpbmdcIikgcmV0dXJuIGFycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTtcbiAgdmFyIG4gPSBPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwobykuc2xpY2UoOCwgLTEpO1xuICBpZiAobiA9PT0gXCJPYmplY3RcIiAmJiBvLmNvbnN0cnVjdG9yKSBuID0gby5jb25zdHJ1Y3Rvci5uYW1lO1xuICBpZiAobiA9PT0gXCJNYXBcIiB8fCBuID09PSBcIlNldFwiKSByZXR1cm4gQXJyYXkuZnJvbShvKTtcbiAgaWYgKG4gPT09IFwiQXJndW1lbnRzXCIgfHwgL14oPzpVaXxJKW50KD86OHwxNnwzMikoPzpDbGFtcGVkKT9BcnJheSQvLnRlc3QobikpIHJldHVybiBhcnJheUxpa2VUb0FycmF5KG8sIG1pbkxlbik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5OyJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/unsupportedIterableToArray.js\n");
+eval("var arrayLikeToArray = __webpack_require__(/*! ./arrayLikeToArray.js */ \"./node_modules/@babel/runtime/helpers/arrayLikeToArray.js\");\n\nfunction _unsupportedIterableToArray(o, minLen) {\n  if (!o) return;\n  if (typeof o === \"string\") return arrayLikeToArray(o, minLen);\n  var n = Object.prototype.toString.call(o).slice(8, -1);\n  if (n === \"Object\" && o.constructor) n = o.constructor.name;\n  if (n === \"Map\" || n === \"Set\") return Array.from(o);\n  if (n === \"Arguments\" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return arrayLikeToArray(o, minLen);\n}\n\nmodule.exports = _unsupportedIterableToArray;\nmodule.exports[\"default\"] = module.exports, module.exports.__esModule = true;//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9AYmFiZWwvcnVudGltZS9oZWxwZXJzL3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5LmpzPzY2MTMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsdUJBQXVCLG1CQUFPLENBQUMsd0ZBQXVCOztBQUV0RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvQGJhYmVsL3J1bnRpbWUvaGVscGVycy91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbInZhciBhcnJheUxpa2VUb0FycmF5ID0gcmVxdWlyZShcIi4vYXJyYXlMaWtlVG9BcnJheS5qc1wiKTtcblxuZnVuY3Rpb24gX3Vuc3VwcG9ydGVkSXRlcmFibGVUb0FycmF5KG8sIG1pbkxlbikge1xuICBpZiAoIW8pIHJldHVybjtcbiAgaWYgKHR5cGVvZiBvID09PSBcInN0cmluZ1wiKSByZXR1cm4gYXJyYXlMaWtlVG9BcnJheShvLCBtaW5MZW4pO1xuICB2YXIgbiA9IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChvKS5zbGljZSg4LCAtMSk7XG4gIGlmIChuID09PSBcIk9iamVjdFwiICYmIG8uY29uc3RydWN0b3IpIG4gPSBvLmNvbnN0cnVjdG9yLm5hbWU7XG4gIGlmIChuID09PSBcIk1hcFwiIHx8IG4gPT09IFwiU2V0XCIpIHJldHVybiBBcnJheS5mcm9tKG8pO1xuICBpZiAobiA9PT0gXCJBcmd1bWVudHNcIiB8fCAvXig/OlVpfEkpbnQoPzo4fDE2fDMyKSg/OkNsYW1wZWQpP0FycmF5JC8udGVzdChuKSkgcmV0dXJuIGFycmF5TGlrZVRvQXJyYXkobywgbWluTGVuKTtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSBfdW5zdXBwb3J0ZWRJdGVyYWJsZVRvQXJyYXk7XG5tb2R1bGUuZXhwb3J0c1tcImRlZmF1bHRcIl0gPSBtb2R1bGUuZXhwb3J0cywgbW9kdWxlLmV4cG9ydHMuX19lc01vZHVsZSA9IHRydWU7Il0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/@babel/runtime/helpers/unsupportedIterableToArray.js\n");
 
 /***/ }),
 
@@ -262,6 +273,17 @@ eval("/**\n * Gets the first element of `array`.\n *\n * @static\n * @memberOf _
 
 /***/ }),
 
+/***/ "./node_modules/lodash/_baseGet.js":
+/*!*****************************************!*\
+  !*** ./node_modules/lodash/_baseGet.js ***!
+  \*****************************************/
+/*! no static exports found */
+/***/ (function(module, exports) {
+
+eval("/**\n * Gets the value at `key` of `object`.\n *\n * @private\n * @param {Object} [object] The object to query.\n * @param {string} key The key of the property to get.\n * @returns {*} Returns the property value.\n */\nfunction getValue(object, key) {\n  return object == null ? undefined : object[key];\n}\n\nmodule.exports = getValue;\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9sb2Rhc2gvX2Jhc2VHZXQuanM/NjU2YiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsT0FBTztBQUNsQixXQUFXLE9BQU87QUFDbEIsYUFBYSxFQUFFO0FBQ2Y7QUFDQTtBQUNBO0FBQ0E7O0FBRUEiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvbG9kYXNoL19iYXNlR2V0LmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBHZXRzIHRoZSB2YWx1ZSBhdCBga2V5YCBvZiBgb2JqZWN0YC5cbiAqXG4gKiBAcHJpdmF0ZVxuICogQHBhcmFtIHtPYmplY3R9IFtvYmplY3RdIFRoZSBvYmplY3QgdG8gcXVlcnkuXG4gKiBAcGFyYW0ge3N0cmluZ30ga2V5IFRoZSBrZXkgb2YgdGhlIHByb3BlcnR5IHRvIGdldC5cbiAqIEByZXR1cm5zIHsqfSBSZXR1cm5zIHRoZSBwcm9wZXJ0eSB2YWx1ZS5cbiAqL1xuZnVuY3Rpb24gZ2V0VmFsdWUob2JqZWN0LCBrZXkpIHtcbiAgcmV0dXJuIG9iamVjdCA9PSBudWxsID8gdW5kZWZpbmVkIDogb2JqZWN0W2tleV07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gZ2V0VmFsdWU7XG4iXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./node_modules/lodash/_baseGet.js\n");
+
+/***/ }),
+
 /***/ "./node_modules/lodash/_baseIteratee.js":
 /*!**********************************************!*\
   !*** ./node_modules/lodash/_baseIteratee.js ***!
@@ -291,7 +313,7 @@ eval("/**\n * A specialized version of `_.map` for arrays without support for it
 /*! no static exports found */
 /***/ (function(module, exports, __webpack_require__) {
 
-eval("var arrayMap = __webpack_require__(/*! ./_arrayMap */ \"./node_modules/lodash/_arrayMap.js\"),\n    baseIteratee = __webpack_require__(/*! ./_baseIteratee */ \"./node_modules/lodash/_baseIteratee.js\"),\n    baseMap = __webpack_require__(/*! ./_baseMap */ \"./node_modules/lodash/_baseMap.js\"),\n    baseSortBy = __webpack_require__(/*! ./_baseSortBy */ \"./node_modules/lodash/_baseSortBy.js\"),\n    baseUnary = __webpack_require__(/*! ./_baseUnary */ \"./node_modules/lodash/_baseUnary.js\"),\n    compareMultiple = __webpack_require__(/*! ./_compareMultiple */ \"./node_modules/lodash/_compareMultiple.js\"),\n    identity = __webpack_require__(/*! ./identity */ \"./node_modules/lodash/identity.js\");\n\n/**\n * The base implementation of `_.orderBy` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n * @param {string[]} orders The sort orders of `iteratees`.\n * @returns {Array} Returns the new sorted array.\n */\nfunction baseOrderBy(collection, iteratees, orders) {\n  var index = -1;\n  iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(baseIteratee));\n\n  var result = baseMap(collection, function(value, key, collection) {\n    var criteria = arrayMap(iteratees, function(iteratee) {\n      return iteratee(value);\n    });\n    return { 'criteria': criteria, 'index': ++index, 'value': value };\n  });\n\n  return baseSortBy(result, function(object, other) {\n    return compareMultiple(object, other, orders);\n  });\n}\n\nmodule.exports = baseOrderBy;\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9sb2Rhc2gvX2Jhc2VPcmRlckJ5LmpzPzZhNWMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsZUFBZSxtQkFBTyxDQUFDLHVEQUFhO0FBQ3BDLG1CQUFtQixtQkFBTyxDQUFDLCtEQUFpQjtBQUM1QyxjQUFjLG1CQUFPLENBQUMscURBQVk7QUFDbEMsaUJBQWlCLG1CQUFPLENBQUMsMkRBQWU7QUFDeEMsZ0JBQWdCLG1CQUFPLENBQUMseURBQWM7QUFDdEMsc0JBQXNCLG1CQUFPLENBQUMscUVBQW9CO0FBQ2xELGVBQWUsbUJBQU8sQ0FBQyxxREFBWTs7QUFFbkM7QUFDQTtBQUNBO0FBQ0E7QUFDQSxXQUFXLGFBQWE7QUFDeEIsV0FBVyw2QkFBNkI7QUFDeEMsV0FBVyxTQUFTO0FBQ3BCLGFBQWEsTUFBTTtBQUNuQjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsWUFBWTtBQUNaLEdBQUc7O0FBRUg7QUFDQTtBQUNBLEdBQUc7QUFDSDs7QUFFQSIsImZpbGUiOiIuL25vZGVfbW9kdWxlcy9sb2Rhc2gvX2Jhc2VPcmRlckJ5LmpzLmpzIiwic291cmNlc0NvbnRlbnQiOlsidmFyIGFycmF5TWFwID0gcmVxdWlyZSgnLi9fYXJyYXlNYXAnKSxcbiAgICBiYXNlSXRlcmF0ZWUgPSByZXF1aXJlKCcuL19iYXNlSXRlcmF0ZWUnKSxcbiAgICBiYXNlTWFwID0gcmVxdWlyZSgnLi9fYmFzZU1hcCcpLFxuICAgIGJhc2VTb3J0QnkgPSByZXF1aXJlKCcuL19iYXNlU29ydEJ5JyksXG4gICAgYmFzZVVuYXJ5ID0gcmVxdWlyZSgnLi9fYmFzZVVuYXJ5JyksXG4gICAgY29tcGFyZU11bHRpcGxlID0gcmVxdWlyZSgnLi9fY29tcGFyZU11bHRpcGxlJyksXG4gICAgaWRlbnRpdHkgPSByZXF1aXJlKCcuL2lkZW50aXR5Jyk7XG5cbi8qKlxuICogVGhlIGJhc2UgaW1wbGVtZW50YXRpb24gb2YgYF8ub3JkZXJCeWAgd2l0aG91dCBwYXJhbSBndWFyZHMuXG4gKlxuICogQHByaXZhdGVcbiAqIEBwYXJhbSB7QXJyYXl8T2JqZWN0fSBjb2xsZWN0aW9uIFRoZSBjb2xsZWN0aW9uIHRvIGl0ZXJhdGUgb3Zlci5cbiAqIEBwYXJhbSB7RnVuY3Rpb25bXXxPYmplY3RbXXxzdHJpbmdbXX0gaXRlcmF0ZWVzIFRoZSBpdGVyYXRlZXMgdG8gc29ydCBieS5cbiAqIEBwYXJhbSB7c3RyaW5nW119IG9yZGVycyBUaGUgc29ydCBvcmRlcnMgb2YgYGl0ZXJhdGVlc2AuXG4gKiBAcmV0dXJucyB7QXJyYXl9IFJldHVybnMgdGhlIG5ldyBzb3J0ZWQgYXJyYXkuXG4gKi9cbmZ1bmN0aW9uIGJhc2VPcmRlckJ5KGNvbGxlY3Rpb24sIGl0ZXJhdGVlcywgb3JkZXJzKSB7XG4gIHZhciBpbmRleCA9IC0xO1xuICBpdGVyYXRlZXMgPSBhcnJheU1hcChpdGVyYXRlZXMubGVuZ3RoID8gaXRlcmF0ZWVzIDogW2lkZW50aXR5XSwgYmFzZVVuYXJ5KGJhc2VJdGVyYXRlZSkpO1xuXG4gIHZhciByZXN1bHQgPSBiYXNlTWFwKGNvbGxlY3Rpb24sIGZ1bmN0aW9uKHZhbHVlLCBrZXksIGNvbGxlY3Rpb24pIHtcbiAgICB2YXIgY3JpdGVyaWEgPSBhcnJheU1hcChpdGVyYXRlZXMsIGZ1bmN0aW9uKGl0ZXJhdGVlKSB7XG4gICAgICByZXR1cm4gaXRlcmF0ZWUodmFsdWUpO1xuICAgIH0pO1xuICAgIHJldHVybiB7ICdjcml0ZXJpYSc6IGNyaXRlcmlhLCAnaW5kZXgnOiArK2luZGV4LCAndmFsdWUnOiB2YWx1ZSB9O1xuICB9KTtcblxuICByZXR1cm4gYmFzZVNvcnRCeShyZXN1bHQsIGZ1bmN0aW9uKG9iamVjdCwgb3RoZXIpIHtcbiAgICByZXR1cm4gY29tcGFyZU11bHRpcGxlKG9iamVjdCwgb3RoZXIsIG9yZGVycyk7XG4gIH0pO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJhc2VPcmRlckJ5O1xuIl0sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./node_modules/lodash/_baseOrderBy.js\n");
+eval("var arrayMap = __webpack_require__(/*! ./_arrayMap */ \"./node_modules/lodash/_arrayMap.js\"),\n    baseGet = __webpack_require__(/*! ./_baseGet */ \"./node_modules/lodash/_baseGet.js\"),\n    baseIteratee = __webpack_require__(/*! ./_baseIteratee */ \"./node_modules/lodash/_baseIteratee.js\"),\n    baseMap = __webpack_require__(/*! ./_baseMap */ \"./node_modules/lodash/_baseMap.js\"),\n    baseSortBy = __webpack_require__(/*! ./_baseSortBy */ \"./node_modules/lodash/_baseSortBy.js\"),\n    baseUnary = __webpack_require__(/*! ./_baseUnary */ \"./node_modules/lodash/_baseUnary.js\"),\n    compareMultiple = __webpack_require__(/*! ./_compareMultiple */ \"./node_modules/lodash/_compareMultiple.js\"),\n    identity = __webpack_require__(/*! ./identity */ \"./node_modules/lodash/identity.js\"),\n    isArray = __webpack_require__(/*! ./isArray */ \"./node_modules/lodash/isArray.js\");\n\n/**\n * The base implementation of `_.orderBy` without param guards.\n *\n * @private\n * @param {Array|Object} collection The collection to iterate over.\n * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.\n * @param {string[]} orders The sort orders of `iteratees`.\n * @returns {Array} Returns the new sorted array.\n */\nfunction baseOrderBy(collection, iteratees, orders) {\n  if (iteratees.length) {\n    iteratees = arrayMap(iteratees, function(iteratee) {\n      if (isArray(iteratee)) {\n        return function(value) {\n          return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);\n        }\n      }\n      return iteratee;\n    });\n  } else {\n    iteratees = [identity];\n  }\n\n  var index = -1;\n  iteratees = arrayMap(iteratees, baseUnary(baseIteratee));\n\n  var result = baseMap(collection, function(value, key, collection) {\n    var criteria = arrayMap(iteratees, function(iteratee) {\n      return iteratee(value);\n    });\n    return { 'criteria': criteria, 'index': ++index, 'value': value };\n  });\n\n  return baseSortBy(result, function(object, other) {\n    return compareMultiple(object, other, orders);\n  });\n}\n\nmodule.exports = baseOrderBy;\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9SZWR1eE9ybS8uL25vZGVfbW9kdWxlcy9sb2Rhc2gvX2Jhc2VPcmRlckJ5LmpzPzZhNWMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsZUFBZSxtQkFBTyxDQUFDLHVEQUFhO0FBQ3BDLGNBQWMsbUJBQU8sQ0FBQyxxREFBWTtBQUNsQyxtQkFBbUIsbUJBQU8sQ0FBQywrREFBaUI7QUFDNUMsY0FBYyxtQkFBTyxDQUFDLHFEQUFZO0FBQ2xDLGlCQUFpQixtQkFBTyxDQUFDLDJEQUFlO0FBQ3hDLGdCQUFnQixtQkFBTyxDQUFDLHlEQUFjO0FBQ3RDLHNCQUFzQixtQkFBTyxDQUFDLHFFQUFvQjtBQUNsRCxlQUFlLG1CQUFPLENBQUMscURBQVk7QUFDbkMsY0FBYyxtQkFBTyxDQUFDLG1EQUFXOztBQUVqQztBQUNBO0FBQ0E7QUFDQTtBQUNBLFdBQVcsYUFBYTtBQUN4QixXQUFXLDZCQUE2QjtBQUN4QyxXQUFXLFNBQVM7QUFDcEIsYUFBYSxNQUFNO0FBQ25CO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLEdBQUc7QUFDSDtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMLFlBQVk7QUFDWixHQUFHOztBQUVIO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7O0FBRUEiLCJmaWxlIjoiLi9ub2RlX21vZHVsZXMvbG9kYXNoL19iYXNlT3JkZXJCeS5qcy5qcyIsInNvdXJjZXNDb250ZW50IjpbInZhciBhcnJheU1hcCA9IHJlcXVpcmUoJy4vX2FycmF5TWFwJyksXG4gICAgYmFzZUdldCA9IHJlcXVpcmUoJy4vX2Jhc2VHZXQnKSxcbiAgICBiYXNlSXRlcmF0ZWUgPSByZXF1aXJlKCcuL19iYXNlSXRlcmF0ZWUnKSxcbiAgICBiYXNlTWFwID0gcmVxdWlyZSgnLi9fYmFzZU1hcCcpLFxuICAgIGJhc2VTb3J0QnkgPSByZXF1aXJlKCcuL19iYXNlU29ydEJ5JyksXG4gICAgYmFzZVVuYXJ5ID0gcmVxdWlyZSgnLi9fYmFzZVVuYXJ5JyksXG4gICAgY29tcGFyZU11bHRpcGxlID0gcmVxdWlyZSgnLi9fY29tcGFyZU11bHRpcGxlJyksXG4gICAgaWRlbnRpdHkgPSByZXF1aXJlKCcuL2lkZW50aXR5JyksXG4gICAgaXNBcnJheSA9IHJlcXVpcmUoJy4vaXNBcnJheScpO1xuXG4vKipcbiAqIFRoZSBiYXNlIGltcGxlbWVudGF0aW9uIG9mIGBfLm9yZGVyQnlgIHdpdGhvdXQgcGFyYW0gZ3VhcmRzLlxuICpcbiAqIEBwcml2YXRlXG4gKiBAcGFyYW0ge0FycmF5fE9iamVjdH0gY29sbGVjdGlvbiBUaGUgY29sbGVjdGl
Download .txt
gitextract_7ra8i_oc/

├── .dockerignore
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-bug-report.yml
│   │   └── 2-feature-request.yml
│   └── workflows/
│       ├── build-and-publish-release-package.yml
│       ├── build-and-push-docker-image.yml
│       ├── build-and-push-docker-nightly-image.yml
│       ├── build-and-test.yml
│       ├── lint.yml
│       └── release-helm-chart.yml
├── .gitignore
├── .husky/
│   └── pre-commit
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CONTRIBUTOR_LICENSE_AGREEMENT.md
├── Dockerfile
├── Dockerfile.dev
├── LICENSE.md
├── LICENSES/
│   ├── PLANKA Commercial License DE.md
│   ├── PLANKA Commercial License EN.md
│   ├── PLANKA Community License DE.md
│   ├── PLANKA Community License EN.md
│   ├── PLANKA License Guide DE.md
│   └── PLANKA License Guide EN.md
├── README.md
├── SECURITY.md
├── charts/
│   └── planka/
│       ├── .helmignore
│       ├── Chart.yaml
│       ├── README.md
│       ├── templates/
│       │   ├── NOTES.txt
│       │   ├── _helpers.tpl
│       │   ├── configmap-terms.yaml
│       │   ├── deployment.yaml
│       │   ├── hpa.yaml
│       │   ├── ingress.yaml
│       │   ├── pvc.yaml
│       │   ├── secret-oidc.yaml
│       │   ├── service.yaml
│       │   ├── serviceaccount.yaml
│       │   └── tests/
│       │       └── test-connection.yaml
│       └── values.yaml
├── client/
│   ├── .gitignore
│   ├── index.html
│   ├── package.json
│   ├── patches/
│   │   ├── @diplodoc+transform+4.70.2.patch
│   │   ├── @gravity-ui+markdown-editor+15.35.1.patch
│   │   ├── react-mentions+4.4.10.patch
│   │   ├── redux-orm+0.16.2.patch
│   │   ├── sails.io.js+1.2.1.patch
│   │   └── semantic-ui-react+2.1.5.patch
│   ├── public/
│   │   ├── manifest.json
│   │   └── robots.txt
│   ├── src/
│   │   ├── actions/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── login.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── router.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── api/
│   │   │   ├── access-tokens.js
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── card-labels.js
│   │   │   ├── card-memberships.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── http.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── terms.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── assets/
│   │   │   └── docs/
│   │   │       └── whats-new.md
│   │   ├── components/
│   │   │   ├── activities/
│   │   │   │   ├── BoardActivitiesModal/
│   │   │   │   │   ├── BoardActivitiesModal.jsx
│   │   │   │   │   ├── BoardActivitiesModal.module.scss
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── CardActivities/
│   │   │   │       ├── CardActivities.jsx
│   │   │   │       ├── CardActivities.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── attachments/
│   │   │   │   ├── AddAttachmentStep/
│   │   │   │   │   ├── AddAttachmentStep.jsx
│   │   │   │   │   ├── AddAttachmentStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── Attachments/
│   │   │   │       ├── Attachments.jsx
│   │   │   │       ├── Attachments.module.scss
│   │   │   │       ├── ContentViewer.jsx
│   │   │   │       ├── ContentViewer.module.scss
│   │   │   │       ├── CsvViewer.jsx
│   │   │   │       ├── CsvViewer.module.scss
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── ItemContent.jsx
│   │   │   │       ├── ItemContent.module.scss
│   │   │   │       ├── PdfViewer.jsx
│   │   │   │       ├── PdfViewer.module.scss
│   │   │   │       └── index.js
│   │   │   ├── base-custom-field-groups/
│   │   │   │   ├── AddBaseCustomFieldGroupStep/
│   │   │   │   │   ├── AddBaseCustomFieldGroupStep.jsx
│   │   │   │   │   ├── AddBaseCustomFieldGroupStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── BaseCustomFieldGroupChip/
│   │   │   │   │   ├── BaseCustomFieldGroupChip.jsx
│   │   │   │   │   ├── BaseCustomFieldGroupChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── BaseCustomFieldGroupStep/
│   │   │   │       ├── BaseCustomFieldGroupStep.jsx
│   │   │   │       ├── BaseCustomFieldGroupStep.module.scss
│   │   │   │       ├── CustomField.jsx
│   │   │   │       ├── CustomField.module.scss
│   │   │   │       ├── CustomFieldAddStep.jsx
│   │   │   │       ├── CustomFieldAddStep.module.scss
│   │   │   │       ├── CustomFieldEditStep.jsx
│   │   │   │       ├── CustomFieldEditStep.module.scss
│   │   │   │       ├── CustomFieldEditor.jsx
│   │   │   │       ├── CustomFieldEditor.module.scss
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── board-memberships/
│   │   │   │   ├── BoardMemberships/
│   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   ├── AddStep/
│   │   │   │   │   │   ├── AddStep.jsx
│   │   │   │   │   │   ├── AddStep.module.scss
│   │   │   │   │   │   ├── User.jsx
│   │   │   │   │   │   ├── User.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── BoardMemberships.jsx
│   │   │   │   │   ├── BoardMemberships.module.scss
│   │   │   │   │   ├── Group.jsx
│   │   │   │   │   ├── Group.module.scss
│   │   │   │   │   ├── GroupItemsStep.jsx
│   │   │   │   │   ├── SelectPermissionsStep.jsx
│   │   │   │   │   ├── SelectPermissionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardMembershipsStep.jsx
│   │   │   │   └── PureBoardMembershipsStep/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── PureBoardMembershipsStep.jsx
│   │   │   │       ├── PureBoardMembershipsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── boards/
│   │   │   │   ├── AddBoardStep/
│   │   │   │   │   ├── AddBoardStep.jsx
│   │   │   │   │   ├── AddBoardStep.module.scss
│   │   │   │   │   ├── ImportStep.jsx
│   │   │   │   │   ├── ImportStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Board/
│   │   │   │   │   ├── Board.jsx
│   │   │   │   │   ├── EndlessContent.jsx
│   │   │   │   │   ├── FiniteContent.jsx
│   │   │   │   │   ├── GridView.jsx
│   │   │   │   │   ├── GridView.module.scss
│   │   │   │   │   ├── KanbanContent/
│   │   │   │   │   │   ├── AddList.jsx
│   │   │   │   │   │   ├── AddList.module.scss
│   │   │   │   │   │   ├── KanbanContent.jsx
│   │   │   │   │   │   ├── KanbanContent.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── ListView.jsx
│   │   │   │   │   ├── ListView.module.scss
│   │   │   │   │   ├── ShortcutsProvider.jsx
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardActions/
│   │   │   │   │   ├── BoardActions.jsx
│   │   │   │   │   ├── BoardActions.module.scss
│   │   │   │   │   ├── Filters.jsx
│   │   │   │   │   ├── Filters.module.scss
│   │   │   │   │   ├── RightSide/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── RightSide.jsx
│   │   │   │   │   │   ├── RightSide.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── BoardSettingsModal/
│   │   │   │   │   ├── BoardSettingsModal.jsx
│   │   │   │   │   ├── GeneralPane/
│   │   │   │   │   │   ├── EditInformation.jsx
│   │   │   │   │   │   ├── EditInformation.module.scss
│   │   │   │   │   │   ├── GeneralPane.jsx
│   │   │   │   │   │   ├── GeneralPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── NotificationsPane.jsx
│   │   │   │   │   ├── NotificationsPane.module.scss
│   │   │   │   │   ├── PreferencesPane/
│   │   │   │   │   │   ├── DefaultCardType.jsx
│   │   │   │   │   │   ├── DefaultCardType.module.scss
│   │   │   │   │   │   ├── DefaultView.jsx
│   │   │   │   │   │   ├── DefaultView.module.scss
│   │   │   │   │   │   ├── Others.jsx
│   │   │   │   │   │   ├── Others.module.scss
│   │   │   │   │   │   ├── PreferencesPane.jsx
│   │   │   │   │   │   ├── PreferencesPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   └── Boards/
│   │   │   │       ├── Boards.jsx
│   │   │   │       ├── Boards.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── cards/
│   │   │   │   ├── AddCard/
│   │   │   │   │   ├── AddCard.jsx
│   │   │   │   │   ├── AddCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ArchiveCardsStep.jsx
│   │   │   │   ├── Card/
│   │   │   │   │   ├── Card.jsx
│   │   │   │   │   ├── Card.module.scss
│   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   ├── InlineContent.jsx
│   │   │   │   │   ├── InlineContent.module.scss
│   │   │   │   │   ├── ProjectContent.jsx
│   │   │   │   │   ├── ProjectContent.module.scss
│   │   │   │   │   ├── StoryContent.jsx
│   │   │   │   │   ├── StoryContent.module.scss
│   │   │   │   │   ├── TaskList/
│   │   │   │   │   │   ├── Task.jsx
│   │   │   │   │   │   ├── Task.module.scss
│   │   │   │   │   │   ├── TaskList.jsx
│   │   │   │   │   │   ├── TaskList.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── CardActionsStep/
│   │   │   │   │   ├── CardActionsStep.jsx
│   │   │   │   │   ├── CardActionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CardModal/
│   │   │   │   │   ├── AddAttachmentZone/
│   │   │   │   │   │   ├── AddAttachmentZone.jsx
│   │   │   │   │   │   ├── AddAttachmentZone.module.scss
│   │   │   │   │   │   ├── AddTextFileModal.jsx
│   │   │   │   │   │   ├── AddTextFileModal.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── CardModal.jsx
│   │   │   │   │   ├── CardModal.module.scss
│   │   │   │   │   ├── Communication.jsx
│   │   │   │   │   ├── Communication.module.scss
│   │   │   │   │   ├── CreationDetailsStep.jsx
│   │   │   │   │   ├── CreationDetailsStep.module.scss
│   │   │   │   │   ├── CustomFieldGroups/
│   │   │   │   │   │   ├── CustomFieldGroups.jsx
│   │   │   │   │   │   ├── CustomFieldGroups.module.scss
│   │   │   │   │   │   ├── DraggableItem.jsx
│   │   │   │   │   │   ├── DraggableItem.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── MoreActionsStep.jsx
│   │   │   │   │   ├── MoreActionsStep.module.scss
│   │   │   │   │   ├── NameField.jsx
│   │   │   │   │   ├── NameField.module.scss
│   │   │   │   │   ├── ProjectContent.jsx
│   │   │   │   │   ├── ProjectContent.module.scss
│   │   │   │   │   ├── StoryContent.jsx
│   │   │   │   │   ├── StoryContent.module.scss
│   │   │   │   │   ├── TaskLists/
│   │   │   │   │   │   ├── EditStep.jsx
│   │   │   │   │   │   ├── EditStep.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   ├── TaskLists.jsx
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Thumbnail.jsx
│   │   │   │   │   ├── Thumbnail.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── DraggableCard/
│   │   │   │   │   ├── DraggableCard.jsx
│   │   │   │   │   ├── DraggableCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── DueDateChip/
│   │   │   │   │   ├── DueDateChip.jsx
│   │   │   │   │   ├── DueDateChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditDueDateStep/
│   │   │   │   │   ├── EditDueDateStep.jsx
│   │   │   │   │   ├── EditDueDateStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditStopwatchStep/
│   │   │   │   │   ├── EditStopwatchStep.jsx
│   │   │   │   │   ├── EditStopwatchStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── MoveCardStep/
│   │   │   │   │   ├── MoveCardStep.jsx
│   │   │   │   │   ├── MoveCardStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── SelectCardType/
│   │   │   │   │   ├── SelectCardType.jsx
│   │   │   │   │   ├── SelectCardType.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── SelectCardTypeStep.jsx
│   │   │   │   └── StopwatchChip/
│   │   │   │       ├── StopwatchChip.jsx
│   │   │   │       ├── StopwatchChip.module.scss
│   │   │   │       └── index.js
│   │   │   ├── comments/
│   │   │   │   └── Comments/
│   │   │   │       ├── Add.jsx
│   │   │   │       ├── Add.module.scss
│   │   │   │       ├── Comments.jsx
│   │   │   │       ├── Comments.module.scss
│   │   │   │       ├── Edit.jsx
│   │   │   │       ├── Edit.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       └── index.js
│   │   │   ├── common/
│   │   │   │   ├── AboutModal/
│   │   │   │   │   ├── AboutModal.jsx
│   │   │   │   │   ├── AboutModal.module.scss
│   │   │   │   │   ├── AboutPane.jsx
│   │   │   │   │   ├── AboutPane.module.scss
│   │   │   │   │   ├── TermsPane.jsx
│   │   │   │   │   ├── TermsPane.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── AdministrationModal/
│   │   │   │   │   ├── AdministrationModal.jsx
│   │   │   │   │   ├── AdministrationModal.module.scss
│   │   │   │   │   ├── SmtpPane.jsx
│   │   │   │   │   ├── SmtpPane.module.scss
│   │   │   │   │   ├── UsersPane/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── AddStep.jsx
│   │   │   │   │   │   ├── AddStep.module.scss
│   │   │   │   │   │   ├── ApiKeyStep.jsx
│   │   │   │   │   │   ├── ApiKeyStep.module.scss
│   │   │   │   │   │   ├── Item.jsx
│   │   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   │   ├── SelectRoleStep.jsx
│   │   │   │   │   │   ├── SelectRoleStep.module.scss
│   │   │   │   │   │   ├── UsersPane.jsx
│   │   │   │   │   │   ├── UsersPane.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── WebhooksPane.jsx
│   │   │   │   │   ├── WebhooksPane.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ConfirmationStep/
│   │   │   │   │   ├── ConfirmationStep.jsx
│   │   │   │   │   ├── ConfirmationStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Core/
│   │   │   │   │   ├── Core.jsx
│   │   │   │   │   ├── Message.jsx
│   │   │   │   │   ├── Message.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditMarkdown/
│   │   │   │   │   ├── EditMarkdown.jsx
│   │   │   │   │   ├── EditMarkdown.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ExpandableMarkdown/
│   │   │   │   │   ├── ExpandableMarkdown.jsx
│   │   │   │   │   ├── ExpandableMarkdown.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Favicon/
│   │   │   │   │   ├── Favicon.jsx
│   │   │   │   │   ├── Favicon.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Favorites/
│   │   │   │   │   ├── Favorites.jsx
│   │   │   │   │   ├── Favorites.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Fixed/
│   │   │   │   │   ├── Fixed.jsx
│   │   │   │   │   ├── Fixed.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── GhostError/
│   │   │   │   │   ├── GhostError.jsx
│   │   │   │   │   ├── GhostError.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Header/
│   │   │   │   │   ├── Header.jsx
│   │   │   │   │   ├── Header.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Home/
│   │   │   │   │   ├── GridProjectsView.jsx
│   │   │   │   │   ├── GroupedProjectsView.jsx
│   │   │   │   │   ├── Home.jsx
│   │   │   │   │   ├── Home.module.scss
│   │   │   │   │   ├── Projects.jsx
│   │   │   │   │   ├── Projects.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── HomeActions/
│   │   │   │   │   ├── Filters.jsx
│   │   │   │   │   ├── Filters.module.scss
│   │   │   │   │   ├── HomeActions.jsx
│   │   │   │   │   ├── HomeActions.module.scss
│   │   │   │   │   ├── RightSide/
│   │   │   │   │   │   ├── RightSide.jsx
│   │   │   │   │   │   ├── RightSide.module.scss
│   │   │   │   │   │   ├── SelectOrderStep.jsx
│   │   │   │   │   │   ├── SelectOrderStep.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── Linkify/
│   │   │   │   │   ├── Link.jsx
│   │   │   │   │   ├── Linkify.jsx
│   │   │   │   │   └── index.js
│   │   │   │   ├── Login/
│   │   │   │   │   ├── Content.jsx
│   │   │   │   │   ├── Content.module.scss
│   │   │   │   │   ├── Login.jsx
│   │   │   │   │   ├── TermsModal.jsx
│   │   │   │   │   ├── TermsModal.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Markdown.jsx
│   │   │   │   ├── MarkdownEditor/
│   │   │   │   │   ├── MarkdownEditor.jsx
│   │   │   │   │   ├── MarkdownEditor.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Root.jsx
│   │   │   │   ├── Static/
│   │   │   │   │   ├── Static.jsx
│   │   │   │   │   ├── Static.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── TimeAgo/
│   │   │   │   │   ├── ExpirableTime.jsx
│   │   │   │   │   ├── ExpirableTime.module.scss
│   │   │   │   │   ├── TimeAgo.jsx
│   │   │   │   │   └── index.js
│   │   │   │   └── Toaster/
│   │   │   │       ├── EmptyTrashToast.jsx
│   │   │   │       ├── EmptyTrashToast.module.scss
│   │   │   │       ├── FileIsTooBigToast.jsx
│   │   │   │       ├── NotEnoughStorageToast.jsx
│   │   │   │       ├── SourceCardNotCopyableToast.jsx
│   │   │   │       ├── SourceCardNotMovableToast.jsx
│   │   │   │       ├── Toaster.jsx
│   │   │   │       └── index.js
│   │   │   ├── custom-field-groups/
│   │   │   │   ├── AddCustomFieldGroupStep/
│   │   │   │   │   ├── AddCustomFieldGroupStep.jsx
│   │   │   │   │   ├── AddCustomFieldGroupStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroup/
│   │   │   │   │   ├── CustomFieldGroup.jsx
│   │   │   │   │   ├── CustomFieldGroup.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupEditor/
│   │   │   │   │   ├── CustomFieldGroupEditor.jsx
│   │   │   │   │   ├── CustomFieldGroupEditor.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupStep/
│   │   │   │   │   ├── CustomField.jsx
│   │   │   │   │   ├── CustomField.module.scss
│   │   │   │   │   ├── CustomFieldAddStep.jsx
│   │   │   │   │   ├── CustomFieldAddStep.module.scss
│   │   │   │   │   ├── CustomFieldEditStep.jsx
│   │   │   │   │   ├── CustomFieldEditStep.module.scss
│   │   │   │   │   ├── CustomFieldEditor.jsx
│   │   │   │   │   ├── CustomFieldEditor.module.scss
│   │   │   │   │   ├── CustomFieldGroupStep.jsx
│   │   │   │   │   ├── UnbasedContent.jsx
│   │   │   │   │   ├── UnbasedContent.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── CustomFieldGroupsStep/
│   │   │   │   │   ├── CustomFieldGroupsStep.jsx
│   │   │   │   │   ├── CustomFieldGroupsStep.module.scss
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── EditCustomFieldGroupStep/
│   │   │   │       ├── EditCustomFieldGroupStep.jsx
│   │   │   │       ├── EditCustomFieldGroupStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── custom-field-values/
│   │   │   │   └── CustomFieldValueChip/
│   │   │   │       ├── CustomFieldValueChip.jsx
│   │   │   │       ├── CustomFieldValueChip.module.scss
│   │   │   │       └── index.js
│   │   │   ├── custom-fields/
│   │   │   │   └── CustomField/
│   │   │   │       ├── CustomField.jsx
│   │   │   │       ├── CustomField.module.scss
│   │   │   │       ├── ValueField.jsx
│   │   │   │       ├── ValueField.module.scss
│   │   │   │       └── index.js
│   │   │   ├── labels/
│   │   │   │   ├── LabelChip/
│   │   │   │   │   ├── LabelChip.jsx
│   │   │   │   │   ├── LabelChip.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── LabelsStep/
│   │   │   │       ├── AddStep.jsx
│   │   │   │       ├── EditStep.jsx
│   │   │   │       ├── EditStep.module.scss
│   │   │   │       ├── Editor.jsx
│   │   │   │       ├── Editor.module.scss
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── LabelsStep.jsx
│   │   │   │       ├── LabelsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── lists/
│   │   │   │   ├── List/
│   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   ├── EditColorStep.jsx
│   │   │   │   │   ├── EditColorStep.module.scss
│   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   ├── List.jsx
│   │   │   │   │   ├── List.module.scss
│   │   │   │   │   ├── MoveStep.jsx
│   │   │   │   │   ├── MoveStep.module.scss
│   │   │   │   │   ├── SortStep.jsx
│   │   │   │   │   ├── SortStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ListsStep/
│   │   │   │   │   ├── Item.jsx
│   │   │   │   │   ├── Item.module.scss
│   │   │   │   │   ├── ListsStep.jsx
│   │   │   │   │   ├── ListsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── SelectListTypeStep/
│   │   │   │       ├── SelectListTypeStep.jsx
│   │   │   │       ├── SelectListTypeStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── notification-services/
│   │   │   │   └── NotificationServices/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── NotificationServices.jsx
│   │   │   │       ├── NotificationServices.module.scss
│   │   │   │       └── index.js
│   │   │   ├── notifications/
│   │   │   │   └── NotificationsStep/
│   │   │   │       ├── Item.jsx
│   │   │   │       ├── Item.module.scss
│   │   │   │       ├── NotificationsStep.jsx
│   │   │   │       ├── NotificationsStep.module.scss
│   │   │   │       └── index.js
│   │   │   ├── project-managers/
│   │   │   │   └── ProjectManagers/
│   │   │   │       ├── ActionsStep.jsx
│   │   │   │       ├── ActionsStep.module.scss
│   │   │   │       ├── AddStep/
│   │   │   │       │   ├── AddStep.jsx
│   │   │   │       │   ├── AddStep.module.scss
│   │   │   │       │   ├── User.jsx
│   │   │   │       │   ├── User.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── ProjectManagers.jsx
│   │   │   │       ├── ProjectManagers.module.scss
│   │   │   │       └── index.js
│   │   │   ├── projects/
│   │   │   │   ├── AddProjectModal/
│   │   │   │   │   ├── AddProjectModal.jsx
│   │   │   │   │   ├── AddProjectModal.module.scss
│   │   │   │   │   ├── SelectTypeStep.jsx
│   │   │   │   │   ├── SelectTypeStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── Project/
│   │   │   │   │   ├── Project.jsx
│   │   │   │   │   ├── Project.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ProjectBackground/
│   │   │   │   │   ├── ProjectBackground.jsx
│   │   │   │   │   ├── ProjectBackground.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── ProjectCard/
│   │   │   │   │   ├── ProjectCard.jsx
│   │   │   │   │   ├── ProjectCard.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── ProjectSettingsModal/
│   │   │   │       ├── BackgroundPane/
│   │   │   │       │   ├── AddImageZone.jsx
│   │   │   │       │   ├── AddImageZone.module.scss
│   │   │   │       │   ├── BackgroundPane.jsx
│   │   │   │       │   ├── BackgroundPane.module.scss
│   │   │   │       │   ├── Gradients/
│   │   │   │       │   │   ├── Gradients.jsx
│   │   │   │       │   │   ├── Gradients.module.scss
│   │   │   │       │   │   ├── Item.jsx
│   │   │   │       │   │   ├── Item.module.scss
│   │   │   │       │   │   └── index.js
│   │   │   │       │   ├── Image.jsx
│   │   │   │       │   ├── Image.module.scss
│   │   │   │       │   ├── Images/
│   │   │   │       │   │   ├── Images.jsx
│   │   │   │       │   │   ├── Images.module.scss
│   │   │   │       │   │   ├── Item.jsx
│   │   │   │       │   │   ├── Item.module.scss
│   │   │   │       │   │   └── index.js
│   │   │   │       │   └── index.js
│   │   │   │       ├── BaseCustomFieldGroupsPane.jsx
│   │   │   │       ├── BaseCustomFieldGroupsPane.module.scss
│   │   │   │       ├── GeneralPane/
│   │   │   │       │   ├── EditInformation.jsx
│   │   │   │       │   ├── EditInformation.module.scss
│   │   │   │       │   ├── GeneralPane.jsx
│   │   │   │       │   ├── GeneralPane.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── ManagersPane.jsx
│   │   │   │       ├── ManagersPane.module.scss
│   │   │   │       ├── ProjectSettingsModal.jsx
│   │   │   │       ├── ProjectSettingsModal.module.scss
│   │   │   │       └── index.js
│   │   │   ├── task-lists/
│   │   │   │   ├── AddTaskListStep.jsx
│   │   │   │   ├── TaskList/
│   │   │   │   │   ├── AddTask.jsx
│   │   │   │   │   ├── AddTask.module.scss
│   │   │   │   │   ├── Task/
│   │   │   │   │   │   ├── ActionsStep.jsx
│   │   │   │   │   │   ├── ActionsStep.module.scss
│   │   │   │   │   │   ├── EditName.jsx
│   │   │   │   │   │   ├── EditName.module.scss
│   │   │   │   │   │   ├── SelectAssigneeStep.jsx
│   │   │   │   │   │   ├── Task.jsx
│   │   │   │   │   │   ├── Task.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── TaskList.jsx
│   │   │   │   │   ├── TaskList.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── TaskListEditor/
│   │   │   │       ├── TaskListEditor.jsx
│   │   │   │       ├── TaskListEditor.module.scss
│   │   │   │       └── index.js
│   │   │   ├── users/
│   │   │   │   ├── EditUserEmailStep/
│   │   │   │   │   ├── EditUserEmailStep.jsx
│   │   │   │   │   ├── EditUserEmailStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserInformation/
│   │   │   │   │   ├── EditUserInformation.jsx
│   │   │   │   │   ├── EditUserInformation.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserInformationStep.jsx
│   │   │   │   ├── EditUserPasswordStep/
│   │   │   │   │   ├── EditUserPasswordStep.jsx
│   │   │   │   │   ├── EditUserPasswordStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── EditUserUsernameStep/
│   │   │   │   │   ├── EditUserUsernameStep.jsx
│   │   │   │   │   ├── EditUserUsernameStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── UserActionsStep/
│   │   │   │   │   ├── UserActionsStep.jsx
│   │   │   │   │   ├── UserActionsStep.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   ├── UserAvatar/
│   │   │   │   │   ├── UserAvatar.jsx
│   │   │   │   │   ├── UserAvatar.module.scss
│   │   │   │   │   └── index.js
│   │   │   │   └── UserSettingsModal/
│   │   │   │       ├── AccountPane/
│   │   │   │       │   ├── AccountPane.jsx
│   │   │   │       │   ├── AccountPane.module.scss
│   │   │   │       │   ├── EditAvatarStep.jsx
│   │   │   │       │   ├── EditAvatarStep.module.scss
│   │   │   │       │   └── index.js
│   │   │   │       ├── NotificationsPane.jsx
│   │   │   │       ├── NotificationsPane.module.scss
│   │   │   │       ├── PreferencesPane.jsx
│   │   │   │       ├── PreferencesPane.module.scss
│   │   │   │       ├── UserSettingsModal.jsx
│   │   │   │       └── index.js
│   │   │   └── webhooks/
│   │   │       └── Webhooks/
│   │   │           ├── Editor.jsx
│   │   │           ├── Editor.module.scss
│   │   │           ├── Item.jsx
│   │   │           ├── Item.module.scss
│   │   │           ├── Webhooks.jsx
│   │   │           └── index.js
│   │   ├── configs/
│   │   │   └── markdown-plugins/
│   │   │       ├── index.js
│   │   │       ├── link.js
│   │   │       └── mention.js
│   │   ├── constants/
│   │   │   ├── AccessTokenSteps.js
│   │   │   ├── ActionTypes.js
│   │   │   ├── BackgroundGradients.js
│   │   │   ├── ClipboardTypes.js
│   │   │   ├── Config.js
│   │   │   ├── DroppableTypes.js
│   │   │   ├── Encodings.js
│   │   │   ├── EntryActionTypes.js
│   │   │   ├── Enums.js
│   │   │   ├── ErrorCodes.js
│   │   │   ├── Icons.js
│   │   │   ├── LabelColors.js
│   │   │   ├── ListColors.js
│   │   │   ├── ListTypeStateByType.js
│   │   │   ├── ModalTypes.js
│   │   │   ├── Paths.js
│   │   │   ├── StaticUsers.js
│   │   │   ├── ToastTypes.js
│   │   │   └── WebhookEvents.js
│   │   ├── contexts/
│   │   │   ├── BoardShortcutsContext.js
│   │   │   ├── ClosableContext.js
│   │   │   └── index.js
│   │   ├── entry-actions/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── bootstrap.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── config.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── login.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── socket.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── history.js
│   │   ├── hooks/
│   │   │   ├── index.js
│   │   │   ├── use-closable-modal.jsx
│   │   │   ├── use-closable.js
│   │   │   ├── use-escape-interceptor.js
│   │   │   ├── use-field.js
│   │   │   ├── use-form.js
│   │   │   ├── use-modal.js
│   │   │   ├── use-nested-ref.js
│   │   │   ├── use-popup-in-closable-context.js
│   │   │   └── use-steps.js
│   │   ├── i18n.js
│   │   ├── index.js
│   │   ├── lib/
│   │   │   ├── custom-ui/
│   │   │   │   ├── assets/
│   │   │   │   │   └── fonts/
│   │   │   │   │       └── icons.otf
│   │   │   │   ├── components/
│   │   │   │   │   ├── FilePicker/
│   │   │   │   │   │   ├── FilePicker.jsx
│   │   │   │   │   │   ├── FilePicker.module.css
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Input/
│   │   │   │   │   │   ├── Input.jsx
│   │   │   │   │   │   ├── InputMask.jsx
│   │   │   │   │   │   ├── InputPassword.jsx
│   │   │   │   │   │   ├── InputPassword.module.css
│   │   │   │   │   │   ├── MaskedInput.jsx
│   │   │   │   │   │   └── index.js
│   │   │   │   │   ├── Masonry/
│   │   │   │   │   │   ├── Masonry.jsx
│   │   │   │   │   │   ├── Masonry.module.scss
│   │   │   │   │   │   └── index.js
│   │   │   │   │   └── Popup/
│   │   │   │   │       ├── Popup.jsx
│   │   │   │   │       ├── PopupHeader.jsx
│   │   │   │   │       ├── PopupHeader.module.css
│   │   │   │   │       └── index.js
│   │   │   │   ├── index.js
│   │   │   │   └── styles.css
│   │   │   ├── hooks/
│   │   │   │   ├── index.js
│   │   │   │   ├── use-click-away-listener.js
│   │   │   │   ├── use-did-update.js
│   │   │   │   ├── use-event-callback.js
│   │   │   │   ├── use-force-update.js
│   │   │   │   ├── use-previous.js
│   │   │   │   ├── use-toggle.js
│   │   │   │   ├── use-transitioning.js
│   │   │   │   └── use-window-width.js
│   │   │   ├── popup/
│   │   │   │   ├── Popup.module.css
│   │   │   │   ├── close-popup.js
│   │   │   │   ├── index.js
│   │   │   │   └── use-popup.jsx
│   │   │   ├── redux-router/
│   │   │   │   ├── ReduxRouter.jsx
│   │   │   │   ├── actions.js
│   │   │   │   ├── create-router-middleware.js
│   │   │   │   ├── create-router-reducer.js
│   │   │   │   └── index.js
│   │   │   └── syntax-highlighter/
│   │   │       ├── index.js
│   │   │       ├── language-definitions/
│   │   │       │   ├── chapel.js
│   │   │       │   ├── dafny.js
│   │   │       │   ├── gn.js
│   │   │       │   ├── godot.js
│   │   │       │   ├── hlsl.js
│   │   │       │   └── terraform.js
│   │   │       ├── languages-map.json
│   │   │       ├── languages.js
│   │   │       └── syntax-highlighter.js
│   │   ├── locales/
│   │   │   ├── ar-YE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── bg-BG/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ca-ES/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── cs-CZ/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── da-DK/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── de-DE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── el-GR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── en-GB/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── en-US/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── es-ES/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── et-EE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fa-IR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fi-FI/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── fr-FR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── hu-HU/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── id-ID/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── index.js
│   │   │   ├── it-IT/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ja-JP/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ko-KR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── nl-NL/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pl-PL/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pt-BR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── pt-PT/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ro-RO/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── ru-RU/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sk-SK/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sr-Cyrl-RS/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sr-Latn-RS/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── sv-SE/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── tr-TR/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── uk-UA/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── uz-UZ/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── vi-VN/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   ├── zh-CN/
│   │   │   │   ├── core.js
│   │   │   │   ├── index.js
│   │   │   │   ├── login.js
│   │   │   │   └── markdown-editor.json
│   │   │   └── zh-TW/
│   │   │       ├── core.js
│   │   │       ├── index.js
│   │   │       ├── login.js
│   │   │       └── markdown-editor.json
│   │   ├── models/
│   │   │   ├── Activity.js
│   │   │   ├── Attachment.js
│   │   │   ├── BackgroundImage.js
│   │   │   ├── BaseCustomFieldGroup.js
│   │   │   ├── BaseModel.js
│   │   │   ├── Board.js
│   │   │   ├── BoardMembership.js
│   │   │   ├── Card.js
│   │   │   ├── Comment.js
│   │   │   ├── CustomField.js
│   │   │   ├── CustomFieldGroup.js
│   │   │   ├── CustomFieldValue.js
│   │   │   ├── Label.js
│   │   │   ├── List.js
│   │   │   ├── Notification.js
│   │   │   ├── NotificationService.js
│   │   │   ├── Project.js
│   │   │   ├── ProjectManager.js
│   │   │   ├── Task.js
│   │   │   ├── TaskList.js
│   │   │   ├── User.js
│   │   │   ├── Webhook.js
│   │   │   └── index.js
│   │   ├── orm.js
│   │   ├── reducers/
│   │   │   ├── auth.js
│   │   │   ├── common.js
│   │   │   ├── core.js
│   │   │   ├── index.js
│   │   │   ├── orm.js
│   │   │   ├── router.js
│   │   │   ├── socket.js
│   │   │   └── ui/
│   │   │       ├── authenticate-form.js
│   │   │       ├── index.js
│   │   │       ├── project-create-form.js
│   │   │       ├── smtp-test-state.js
│   │   │       └── user-create-form.js
│   │   ├── sagas/
│   │   │   ├── core/
│   │   │   │   ├── index.js
│   │   │   │   ├── request.js
│   │   │   │   ├── requests/
│   │   │   │   │   ├── boards.js
│   │   │   │   │   ├── core.js
│   │   │   │   │   └── index.js
│   │   │   │   ├── services/
│   │   │   │   │   ├── activities.js
│   │   │   │   │   ├── attachments.js
│   │   │   │   │   ├── background-images.js
│   │   │   │   │   ├── base-custom-field-groups.js
│   │   │   │   │   ├── board-memberships.js
│   │   │   │   │   ├── boards.js
│   │   │   │   │   ├── bootstrap.js
│   │   │   │   │   ├── cards.js
│   │   │   │   │   ├── comments.js
│   │   │   │   │   ├── config.js
│   │   │   │   │   ├── core.js
│   │   │   │   │   ├── custom-field-groups.js
│   │   │   │   │   ├── custom-field-values.js
│   │   │   │   │   ├── custom-fields.js
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── labels.js
│   │   │   │   │   ├── lists.js
│   │   │   │   │   ├── modals.js
│   │   │   │   │   ├── notification-services.js
│   │   │   │   │   ├── notifications.js
│   │   │   │   │   ├── project-managers.js
│   │   │   │   │   ├── projects.js
│   │   │   │   │   ├── router.js
│   │   │   │   │   ├── socket.js
│   │   │   │   │   ├── task-lists.js
│   │   │   │   │   ├── tasks.js
│   │   │   │   │   ├── users.js
│   │   │   │   │   └── webhooks.js
│   │   │   │   └── watchers/
│   │   │   │       ├── activities.js
│   │   │   │       ├── attachments.js
│   │   │   │       ├── background-images.js
│   │   │   │       ├── base-custom-field-groups.js
│   │   │   │       ├── board-memberships.js
│   │   │   │       ├── boards.js
│   │   │   │       ├── bootstrap.js
│   │   │   │       ├── cards.js
│   │   │   │       ├── comments.js
│   │   │   │       ├── config.js
│   │   │   │       ├── core.js
│   │   │   │       ├── custom-field-groups.js
│   │   │   │       ├── custom-field-values.js
│   │   │   │       ├── custom-fields.js
│   │   │   │       ├── index.js
│   │   │   │       ├── labels.js
│   │   │   │       ├── lists.js
│   │   │   │       ├── modals.js
│   │   │   │       ├── notification-services.js
│   │   │   │       ├── notifications.js
│   │   │   │       ├── project-managers.js
│   │   │   │       ├── projects.js
│   │   │   │       ├── router.js
│   │   │   │       ├── socket.js
│   │   │   │       ├── task-lists.js
│   │   │   │       ├── tasks.js
│   │   │   │       ├── users.js
│   │   │   │       └── webhooks.js
│   │   │   ├── index.js
│   │   │   ├── login/
│   │   │   │   ├── index.js
│   │   │   │   ├── services/
│   │   │   │   │   ├── index.js
│   │   │   │   │   ├── login.js
│   │   │   │   │   └── router.js
│   │   │   │   └── watchers/
│   │   │   │       ├── index.js
│   │   │   │       ├── login.js
│   │   │   │       └── router.js
│   │   │   └── run-watchers.js
│   │   ├── selectors/
│   │   │   ├── activities.js
│   │   │   ├── attachments.js
│   │   │   ├── background-images.js
│   │   │   ├── base-custom-field-groups.js
│   │   │   ├── board-memberships.js
│   │   │   ├── boards.js
│   │   │   ├── cards.js
│   │   │   ├── comments.js
│   │   │   ├── common.js
│   │   │   ├── core.js
│   │   │   ├── custom-field-groups.js
│   │   │   ├── custom-field-values.js
│   │   │   ├── custom-fields.js
│   │   │   ├── index.js
│   │   │   ├── labels.js
│   │   │   ├── lists.js
│   │   │   ├── modals.js
│   │   │   ├── notification-services.js
│   │   │   ├── notifications.js
│   │   │   ├── positioning.js
│   │   │   ├── project-managers.js
│   │   │   ├── projects.js
│   │   │   ├── router.js
│   │   │   ├── task-lists.js
│   │   │   ├── tasks.js
│   │   │   ├── users.js
│   │   │   └── webhooks.js
│   │   ├── store.js
│   │   ├── styles.module.scss
│   │   ├── utils/
│   │   │   ├── access-token-storage.js
│   │   │   ├── build-search-parts.js
│   │   │   ├── element-helpers.js
│   │   │   ├── event-helpers.js
│   │   │   ├── get-date-format.js
│   │   │   ├── get-filename-and-extension.js
│   │   │   ├── local-id.js
│   │   │   ├── local-id.test.js
│   │   │   ├── markdown-to-text.js
│   │   │   ├── match-paths.js
│   │   │   ├── mentions.js
│   │   │   ├── merge-records.js
│   │   │   ├── parse-dnd-id.js
│   │   │   ├── parse-time.js
│   │   │   ├── record-helpers.js
│   │   │   ├── stopwatch.js
│   │   │   └── validator.js
│   │   └── version.js
│   ├── tests/
│   │   ├── acceptance/
│   │   │   ├── Config.js
│   │   │   ├── cucumber.conf.js
│   │   │   ├── features/
│   │   │   │   └── login.feature
│   │   │   ├── pages/
│   │   │   │   ├── HomePage.js
│   │   │   │   └── LoginPage.js
│   │   │   └── steps/
│   │   │       └── login.step.js
│   │   └── setup-symlinks.sh
│   ├── version-template.ejs
│   └── vite.config.js
├── docker-backup.sh
├── docker-compose-dev.yml
├── docker-compose.yml
├── docker-restore.sh
├── package.json
└── server/
    ├── .buildignore
    ├── .editorconfig
    ├── .eslintignore
    ├── .gitignore
    ├── .npmrc
    ├── .sailsrc
    ├── api/
    │   ├── controllers/
    │   │   ├── .gitkeep
    │   │   ├── _internal/
    │   │   │   └── update-config.js
    │   │   ├── access-tokens/
    │   │   │   ├── accept-terms.js
    │   │   │   ├── create.js
    │   │   │   ├── debug-oidc.js
    │   │   │   ├── delete.js
    │   │   │   ├── exchange-with-oidc.js
    │   │   │   └── revoke-pending-token.js
    │   │   ├── actions/
    │   │   │   ├── index-in-board.js
    │   │   │   └── index-in-card.js
    │   │   ├── attachments/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── background-images/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── base-custom-field-groups/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── board-memberships/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── boards/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── bootstrap/
    │   │   │   └── show.js
    │   │   ├── card-labels/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── card-memberships/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── cards/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── duplicate.js
    │   │   │   ├── index.js
    │   │   │   ├── read-notifications.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── comments/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   └── update.js
    │   │   ├── config/
    │   │   │   ├── show.js
    │   │   │   ├── test-smtp.js
    │   │   │   └── update.js
    │   │   ├── custom-field-groups/
    │   │   │   ├── create-in-board.js
    │   │   │   ├── create-in-card.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── custom-field-values/
    │   │   │   ├── create-or-update.js
    │   │   │   └── delete.js
    │   │   ├── custom-fields/
    │   │   │   ├── create-in-base-custom-field-group.js
    │   │   │   ├── create-in-custom-field-group.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── file-attachments/
    │   │   │   ├── download-thumbnail.js
    │   │   │   └── download.js
    │   │   ├── index.js
    │   │   ├── labels/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── lists/
    │   │   │   ├── clear.js
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── move-cards.js
    │   │   │   ├── show.js
    │   │   │   ├── sort.js
    │   │   │   └── update.js
    │   │   ├── notification-services/
    │   │   │   ├── create-in-board.js
    │   │   │   ├── create-in-user.js
    │   │   │   ├── delete.js
    │   │   │   ├── test.js
    │   │   │   └── update.js
    │   │   ├── notifications/
    │   │   │   ├── index.js
    │   │   │   ├── read-all.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── project-managers/
    │   │   │   ├── create.js
    │   │   │   └── delete.js
    │   │   ├── projects/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── swagger/
    │   │   │   └── show.js
    │   │   ├── task-lists/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── show.js
    │   │   │   └── update.js
    │   │   ├── tasks/
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   └── update.js
    │   │   ├── terms/
    │   │   │   └── show.js
    │   │   ├── users/
    │   │   │   ├── create-api-key.js
    │   │   │   ├── create.js
    │   │   │   ├── delete.js
    │   │   │   ├── index.js
    │   │   │   ├── show.js
    │   │   │   ├── update-avatar.js
    │   │   │   ├── update-email.js
    │   │   │   ├── update-password.js
    │   │   │   ├── update-username.js
    │   │   │   └── update.js
    │   │   └── webhooks/
    │   │       ├── create.js
    │   │       ├── delete.js
    │   │       ├── index.js
    │   │       └── update.js
    │   ├── helpers/
    │   │   ├── .gitkeep
    │   │   ├── access-tokens/
    │   │   │   └── handle-steps.js
    │   │   ├── actions/
    │   │   │   └── create-one.js
    │   │   ├── attachments/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   ├── process-link.js
    │   │   │   ├── process-uploaded-file.js
    │   │   │   └── update-one.js
    │   │   ├── background-images/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   └── process-uploaded-file.js
    │   │   ├── base-custom-field-groups/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── board-memberships/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── boards/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-card-ids.js
    │   │   │   ├── get-kanban-lists-by-id.js
    │   │   │   ├── get-member-user-ids.js
    │   │   │   ├── get-notification-services-total.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── get-subscription-user-ids.js
    │   │   │   ├── import-from-trello.js
    │   │   │   ├── process-uploaded-trello-import-file.js
    │   │   │   └── update-one.js
    │   │   ├── bootstrap/
    │   │   │   └── present-one.js
    │   │   ├── card-labels/
    │   │   │   ├── create-one.js
    │   │   │   └── delete-one.js
    │   │   ├── card-memberships/
    │   │   │   ├── create-one.js
    │   │   │   └── delete-one.js
    │   │   ├── cards/
    │   │   │   ├── copy-custom-fields.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── detach-custom-fields.js
    │   │   │   ├── duplicate-one.js
    │   │   │   ├── get-labels.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── get-subscription-user-ids.js
    │   │   │   ├── read-notifications-for-user.js
    │   │   │   └── update-one.js
    │   │   ├── comments/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── config/
    │   │   │   ├── present-one.js
    │   │   │   └── update-main.js
    │   │   ├── custom-field-groups/
    │   │   │   ├── create-one-in-board.js
    │   │   │   ├── create-one-in-card.js
    │   │   │   ├── delete-one-in-board.js
    │   │   │   ├── delete-one-in-card.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── update-one-in-board.js
    │   │   │   └── update-one-in-card.js
    │   │   ├── custom-field-values/
    │   │   │   ├── create-or-update-one.js
    │   │   │   └── delete-one.js
    │   │   ├── custom-fields/
    │   │   │   ├── create-one-in-base-custom-field-group.js
    │   │   │   ├── create-one-in-custom-field-group.js
    │   │   │   ├── delete-one-in-base-custom-field-group.js
    │   │   │   ├── delete-one-in-custom-field-group.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── update-one-in-base-custom-field-group.js
    │   │   │   └── update-one-in-custom-field-group.js
    │   │   ├── internal-config/
    │   │   │   └── update-main.js
    │   │   ├── labels/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── lists/
    │   │   │   ├── clear-one.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   ├── is-archive-or-trash.js
    │   │   │   ├── is-finite.js
    │   │   │   ├── is-kanban.js
    │   │   │   ├── move-cards.js
    │   │   │   ├── resolve-name.js
    │   │   │   ├── sort-one.js
    │   │   │   └── update-one.js
    │   │   ├── notification-services/
    │   │   │   ├── create-one-in-board.js
    │   │   │   ├── create-one-in-user.js
    │   │   │   ├── delete-one-in-board.js
    │   │   │   ├── delete-one-in-user.js
    │   │   │   ├── get-path-to-user-by-id.js
    │   │   │   ├── update-one-in-board.js
    │   │   │   └── update-one-in-user.js
    │   │   ├── notifications/
    │   │   │   ├── create-many.js
    │   │   │   ├── create-one.js
    │   │   │   ├── read-all-for-user.js
    │   │   │   └── update-one.js
    │   │   ├── project-managers/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   └── get-path-to-project-by-id.js
    │   │   ├── projects/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-board-ids-by-id.js
    │   │   │   ├── get-board-memberships-total-by-id-and-user-id.js
    │   │   │   ├── get-boards-total-by-id.js
    │   │   │   ├── get-lonely-by-ids.js
    │   │   │   ├── get-manager-user-ids.js
    │   │   │   ├── get-project-managers-total-by-id.js
    │   │   │   ├── make-scoper.js
    │   │   │   └── update-one.js
    │   │   ├── sessions/
    │   │   │   └── create-one.js
    │   │   ├── task-lists/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── tasks/
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── get-path-to-project-by-id.js
    │   │   │   └── update-one.js
    │   │   ├── users/
    │   │   │   ├── build-gravatar-url.js
    │   │   │   ├── create-one.js
    │   │   │   ├── delete-one.js
    │   │   │   ├── delete-related.js
    │   │   │   ├── get-all-active-ids.js
    │   │   │   ├── get-manager-project-ids.js
    │   │   │   ├── get-notification-services-total.js
    │   │   │   ├── get-or-create-one-with-oidc.js
    │   │   │   ├── get-project-managers-total-by-id.js
    │   │   │   ├── is-admin-or-project-owner.js
    │   │   │   ├── is-board-member.js
    │   │   │   ├── is-board-subscriber.js
    │   │   │   ├── is-card-subscriber.js
    │   │   │   ├── is-project-favorite.js
    │   │   │   ├── is-project-manager.js
    │   │   │   ├── make-scoper.js
    │   │   │   ├── present-many.js
    │   │   │   ├── present-one.js
    │   │   │   ├── process-uploaded-avatar-file.js
    │   │   │   └── update-one.js
    │   │   ├── utils/
    │   │   │   ├── clear-http-only-token-cookie.js
    │   │   │   ├── create-jwt-token.js
    │   │   │   ├── download-favicon.js
    │   │   │   ├── generate-api-key.js
    │   │   │   ├── generate-ids.js
    │   │   │   ├── generate-random-string.js
    │   │   │   ├── get-available-storage.js
    │   │   │   ├── hash.js
    │   │   │   ├── insert-to-positionables.js
    │   │   │   ├── is-preloaded-favicon-exists.js
    │   │   │   ├── make-smtp-transporter.js
    │   │   │   ├── make-translator.js
    │   │   │   ├── map-records.js
    │   │   │   ├── receive-file.js
    │   │   │   ├── remove-unreferenced-uploaded-files.js
    │   │   │   ├── send-email.js
    │   │   │   ├── send-notifications.js
    │   │   │   ├── send-webhooks.js
    │   │   │   ├── set-http-only-token-cookie.js
    │   │   │   └── verify-jwt-token.js
    │   │   └── webhooks/
    │   │       ├── create-one.js
    │   │       ├── delete-one.js
    │   │       └── update-one.js
    │   ├── hooks/
    │   │   ├── .gitkeep
    │   │   ├── current-user/
    │   │   │   └── index.js
    │   │   ├── file-manager/
    │   │   │   ├── LocalFileManager.js
    │   │   │   ├── S3FileManager.js
    │   │   │   └── index.js
    │   │   ├── oidc/
    │   │   │   └── index.js
    │   │   ├── query-methods/
    │   │   │   ├── helpers.js
    │   │   │   ├── index.js
    │   │   │   └── models/
    │   │   │       ├── Action.js
    │   │   │       ├── Attachment.js
    │   │   │       ├── BackgroundImage.js
    │   │   │       ├── BaseCustomFieldGroup.js
    │   │   │       ├── Board.js
    │   │   │       ├── BoardMembership.js
    │   │   │       ├── BoardSubscription.js
    │   │   │       ├── Card.js
    │   │   │       ├── CardLabel.js
    │   │   │       ├── CardMembership.js
    │   │   │       ├── CardSubscription.js
    │   │   │       ├── Comment.js
    │   │   │       ├── Config.js
    │   │   │       ├── CustomField.js
    │   │   │       ├── CustomFieldGroup.js
    │   │   │       ├── CustomFieldValue.js
    │   │   │       ├── IdentityProviderUser.js
    │   │   │       ├── InternalConfig.js
    │   │   │       ├── Label.js
    │   │   │       ├── List.js
    │   │   │       ├── Notification.js
    │   │   │       ├── NotificationService.js
    │   │   │       ├── Project.js
    │   │   │       ├── ProjectFavorite.js
    │   │   │       ├── ProjectManager.js
    │   │   │       ├── Session.js
    │   │   │       ├── StorageUsage.js
    │   │   │       ├── Task.js
    │   │   │       ├── TaskList.js
    │   │   │       ├── UploadedFile.js
    │   │   │       ├── User.js
    │   │   │       └── Webhook.js
    │   │   ├── s3/
    │   │   │   └── index.js
    │   │   ├── terms/
    │   │   │   └── index.js
    │   │   └── watcher/
    │   │       └── index.js
    │   ├── models/
    │   │   ├── .gitkeep
    │   │   ├── Action.js
    │   │   ├── Attachment.js
    │   │   ├── BackgroundImage.js
    │   │   ├── BaseCustomFieldGroup.js
    │   │   ├── Board.js
    │   │   ├── BoardMembership.js
    │   │   ├── BoardSubscription.js
    │   │   ├── Card.js
    │   │   ├── CardLabel.js
    │   │   ├── CardMembership.js
    │   │   ├── CardSubscription.js
    │   │   ├── Comment.js
    │   │   ├── Config.js
    │   │   ├── CustomField.js
    │   │   ├── CustomFieldGroup.js
    │   │   ├── CustomFieldValue.js
    │   │   ├── IdentityProviderUser.js
    │   │   ├── InternalConfig.js
    │   │   ├── Label.js
    │   │   ├── List.js
    │   │   ├── Notification.js
    │   │   ├── NotificationService.js
    │   │   ├── Project.js
    │   │   ├── ProjectFavorite.js
    │   │   ├── ProjectManager.js
    │   │   ├── Session.js
    │   │   ├── StorageUsage.js
    │   │   ├── Task.js
    │   │   ├── TaskList.js
    │   │   ├── UploadedFile.js
    │   │   ├── User.js
    │   │   └── Webhook.js
    │   ├── policies/
    │   │   ├── .gitkeep
    │   │   ├── is-admin-or-project-owner.js
    │   │   ├── is-admin.js
    │   │   ├── is-authenticated.js
    │   │   ├── is-external.js
    │   │   ├── is-internal.js
    │   │   └── is-session.js
    │   └── responses/
    │       ├── .gitkeep
    │       ├── conflict.js
    │       ├── forbidden.js
    │       ├── notFound.js
    │       ├── unauthorized.js
    │       ├── unprocessableEntity.js
    │       └── validationError.js
    ├── app.js
    ├── build.js
    ├── config/
    │   ├── blueprints.js
    │   ├── bootstrap.js
    │   ├── custom.js
    │   ├── datastores.js
    │   ├── env/
    │   │   ├── production.js
    │   │   └── test.js
    │   ├── globals.js
    │   ├── http.js
    │   ├── i18n.js
    │   ├── locales/
    │   │   ├── ar-YE.json
    │   │   ├── bg-BG.json
    │   │   ├── ca-ES.json
    │   │   ├── cs-CZ.json
    │   │   ├── da-DK.json
    │   │   ├── de-DE.json
    │   │   ├── el-GR.json
    │   │   ├── en-GB.json
    │   │   ├── en-US.json
    │   │   ├── es-ES.json
    │   │   ├── et-EE.json
    │   │   ├── fa-IR.json
    │   │   ├── fi-FI.json
    │   │   ├── fr-FR.json
    │   │   ├── hu-HU.json
    │   │   ├── id-ID.json
    │   │   ├── it-IT.json
    │   │   ├── ja-JP.json
    │   │   ├── ko-KR.json
    │   │   ├── nl-NL.json
    │   │   ├── pl-PL.json
    │   │   ├── pt-BR.json
    │   │   ├── pt-PT.json
    │   │   ├── ro-RO.json
    │   │   ├── ru-RU.json
    │   │   ├── sk-SK.json
    │   │   ├── sr-Cyrl-RS.json
    │   │   ├── sr-Latn-RS.json
    │   │   ├── sv-SE.json
    │   │   ├── tr-TR.json
    │   │   ├── uk-UA.json
    │   │   ├── uz-UZ.json
    │   │   ├── vi-VN.json
    │   │   ├── zh-CN.json
    │   │   └── zh-TW.json
    │   ├── log.js
    │   ├── models.js
    │   ├── policies.js
    │   ├── routes.js
    │   ├── security.js
    │   ├── session.js
    │   ├── sockets.js
    │   ├── swagger.js
    │   └── views.js
    ├── constants.js
    ├── data/
    │   └── .gitkeep
    ├── db/
    │   ├── create-admin-user.js
    │   ├── init.js
    │   ├── knexfile.js
    │   ├── migrations/
    │   │   ├── 20250228000022_version_2.js
    │   │   ├── 20250522151122_add_board_activity_log.js
    │   │   ├── 20250523131647_add_comments_counter.js
    │   │   ├── 20250603102521_canonicalize_locale_codes.js
    │   │   ├── 20250703122452_move_webhooks_configuration_from_environment_variable_to_ui.js
    │   │   ├── 20250708200908_persist_closed_state_per_card.js
    │   │   ├── 20250709160208_add_ability_to_link_tasks_to_cards.js
    │   │   ├── 20250721132312_add_ability_to_hide_completed_tasks.js
    │   │   ├── 20250728105713_add_legal_requirements.js
    │   │   ├── 20250820144730_track_storage_usage.js
    │   │   ├── 20250905101408_restore_toggleable_due_dates.js
    │   │   ├── 20250905205438_add_board_setting_to_expand_task_lists_by_default.js
    │   │   ├── 20250917123048_add_ability_to_configure_smtp_via_ui.js
    │   │   ├── 20251105104948_add_api_key_authentication.js
    │   │   ├── 20251121231641_rename_gin_indexes.js
    │   │   ├── 20260122093047_add_internal_runtime_configuration.js
    │   │   └── 20260312000000_add_ability_to_display_card_ages.js
    │   ├── seeds/
    │   │   ├── .gitkeep
    │   │   └── default.js
    │   └── upgrade.js
    ├── generate-swagger.js
    ├── healthcheck.js
    ├── nodemon.json
    ├── package.json
    ├── patches/
    │   ├── sails+1.5.17.patch
    │   ├── skipper-disk+0.5.12.patch
    │   └── waterline+0.15.2.patch
    ├── requirements.txt
    ├── setup-python.js
    ├── start.sh
    ├── terms/
    │   ├── _template/
    │   │   ├── de-DE.md
    │   │   └── en-US.md
    │   └── cloud/
    │       ├── de-DE.md
    │       └── en-US.md
    ├── test/
    │   ├── fixtures/
    │   │   └── .gitkeep
    │   ├── integration/
    │   │   ├── controllers/
    │   │   │   └── .gitkeep
    │   │   ├── helpers/
    │   │   │   └── .gitkeep
    │   │   └── models/
    │   │       └── User.test.js
    │   ├── lifecycle.test.js
    │   ├── mocha.opts
    │   └── utils/
    │       └── remote-address.test.js
    ├── utils/
    │   ├── build-query-parts.js
    │   ├── filenamify.js
    │   ├── inputs.js
    │   ├── logger.js
    │   ├── mentions.js
    │   ├── migrations.js
    │   ├── normalize-values.js
    │   ├── remote-address.js
    │   ├── send_notifications.py
    │   └── validators.js
    ├── version-template.ejs
    ├── version.js
    └── views/
        └── .gitkeep
Download .txt
SYMBOL INDEX (683 symbols across 398 files)

FILE: client/src/components/attachments/Attachments/Attachments.jsx
  constant INITIALLY_VISIBLE (line 20) | const INITIALLY_VISIBLE = 4;

FILE: client/src/components/attachments/Attachments/ContentViewer.jsx
  function fetchFile (line 45) | async function fetchFile() {

FILE: client/src/components/attachments/Attachments/CsvViewer.jsx
  constant ROWS_PER_PAGE (line 15) | const ROWS_PER_PAGE = 50;
  function fetchFile (line 41) | async function fetchFile() {

FILE: client/src/components/board-memberships/BoardMemberships/Group.jsx
  constant MAX_MEMBERS (line 18) | const MAX_MEMBERS = 6;

FILE: client/src/components/board-memberships/BoardMemberships/SelectPermissionsStep.jsx
  constant DESCRIPTION_BY_ROLE (line 20) | const DESCRIPTION_BY_ROLE = {

FILE: client/src/components/boards/Board/KanbanContent/AddList.jsx
  constant DEFAULT_DATA (line 23) | const DEFAULT_DATA = {

FILE: client/src/components/boards/BoardSettingsModal/PreferencesPane/DefaultView.jsx
  constant DESCRIPTION_BY_VIEW (line 18) | const DESCRIPTION_BY_VIEW = {

FILE: client/src/components/cards/AddCard/AddCard.jsx
  constant DEFAULT_DATA (line 24) | const DEFAULT_DATA = {

FILE: client/src/components/cards/CardModal/CardModal.jsx
  constant DIRECTION_BY_KEY (line 25) | const DIRECTION_BY_KEY = {

FILE: client/src/components/cards/DueDateChip/DueDateChip.jsx
  constant LONG_DATE_FORMAT_BY_SIZE (line 30) | const LONG_DATE_FORMAT_BY_SIZE = {
  constant FULL_DATE_FORMAT_BY_SIZE (line 36) | const FULL_DATE_FORMAT_BY_SIZE = {
  constant STATUS_ICON_PROPS_BY_STATUS (line 42) | const STATUS_ICON_PROPS_BY_STATUS = {

FILE: client/src/components/cards/SelectCardType/SelectCardType.jsx
  constant DESCRIPTION_BY_TYPE (line 16) | const DESCRIPTION_BY_TYPE = {

FILE: client/src/components/comments/Comments/Add.jsx
  constant DEFAULT_DATA (line 23) | const DEFAULT_DATA = {

FILE: client/src/components/common/AboutModal/AboutPane.jsx
  function fetchWhatsNew (line 23) | async function fetchWhatsNew() {

FILE: client/src/components/common/AboutModal/TermsPane.jsx
  function fetchTerms (line 20) | async function fetchTerms() {

FILE: client/src/components/common/AdministrationModal/UsersPane/SelectRoleStep.jsx
  constant DESCRIPTION_BY_ROLE (line 17) | const DESCRIPTION_BY_ROLE = {

FILE: client/src/components/common/EditMarkdown/EditMarkdown.jsx
  constant MAX_LENGTH (line 20) | const MAX_LENGTH = 1048576;

FILE: client/src/components/common/ExpandableMarkdown/ExpandableMarkdown.jsx
  constant MAX_VISIBLE_PART_HEIGHT (line 17) | const MAX_VISIBLE_PART_HEIGHT = 800;

FILE: client/src/components/common/Header/Header.jsx
  constant POPUP_PROPS (line 23) | const POPUP_PROPS = {

FILE: client/src/components/common/Home/GroupedProjectsView.jsx
  constant TITLE_BY_GROUP (line 16) | const TITLE_BY_GROUP = {
  constant DEFAULT_TYPE_BY_GROUP (line 23) | const DEFAULT_TYPE_BY_GROUP = {

FILE: client/src/components/common/Root.jsx
  function Root (line 27) | function Root({ store, history }) {

FILE: client/src/components/common/TimeAgo/ExpirableTime.jsx
  constant DAY (line 12) | const DAY = 1000 * 60 * 60 * 24;

FILE: client/src/components/common/Toaster/Toaster.jsx
  constant TOAST_BY_TYPE (line 16) | const TOAST_BY_TYPE = {

FILE: client/src/components/lists/List/List.jsx
  constant INDEX_BY_ADD_CARD_POSITION (line 39) | const INDEX_BY_ADD_CARD_POSITION = {

FILE: client/src/components/lists/List/SortStep.jsx
  constant DATA_BY_TYPE (line 25) | const DATA_BY_TYPE = {

FILE: client/src/components/lists/SelectListTypeStep/SelectListTypeStep.jsx
  constant DESCRIPTION_BY_TYPE (line 17) | const DESCRIPTION_BY_TYPE = {

FILE: client/src/components/notification-services/NotificationServices/NotificationServices.jsx
  constant DEFAULT_DATA (line 19) | const DEFAULT_DATA = {

FILE: client/src/components/projects/AddProjectModal/SelectTypeStep.jsx
  constant DESCRIPTION_BY_TYPE (line 17) | const DESCRIPTION_BY_TYPE = {

FILE: client/src/components/projects/ProjectSettingsModal/BackgroundPane/BackgroundPane.jsx
  constant TITLE_BY_TYPE (line 20) | const TITLE_BY_TYPE = {

FILE: client/src/components/task-lists/TaskList/AddTask.jsx
  constant DEFAULT_DATA (line 22) | const DEFAULT_DATA = {
  constant MULTIPLE_REGEX (line 27) | const MULTIPLE_REGEX = /\s*\r?\n\s*/;

FILE: client/src/components/users/UserAvatar/UserAvatar.jsx
  constant COLORS (line 28) | const COLORS = [

FILE: client/src/components/webhooks/Webhooks/Webhooks.jsx
  constant DEFAULT_DATA (line 17) | const DEFAULT_DATA = {

FILE: client/src/configs/markdown-plugins/link.js
  constant SAME_SITE_CLASS (line 9) | const SAME_SITE_CLASS = 'same-site';
  function process (line 20) | function process(token, nextToken) {

FILE: client/src/constants/ClipboardTypes.js
  constant COPY (line 6) | const COPY = 'COPY';
  constant CUT (line 7) | const CUT = 'CUT';

FILE: client/src/constants/Config.js
  constant BASE_PATH (line 6) | const BASE_PATH = window.BASE_PATH || '';
  constant ACCESS_TOKEN_KEY (line 8) | const ACCESS_TOKEN_KEY = 'accessToken';
  constant ACCESS_TOKEN_VERSION_KEY (line 9) | const ACCESS_TOKEN_VERSION_KEY = 'accessTokenVersion';
  constant ACCESS_TOKEN_VERSION (line 10) | const ACCESS_TOKEN_VERSION = '1';
  constant POSITION_GAP (line 12) | const POSITION_GAP = 65536;
  constant CARDS_LIMIT (line 13) | const CARDS_LIMIT = 50;
  constant COMMENTS_LIMIT (line 14) | const COMMENTS_LIMIT = 50;
  constant ACTIVITIES_LIMIT (line 15) | const ACTIVITIES_LIMIT = 50;
  constant MAX_SIZE_TO_DISPLAY_CONTENT (line 17) | const MAX_SIZE_TO_DISPLAY_CONTENT = 256 * 1024;
  constant IS_MAC (line 19) | const IS_MAC = navigator.platform.startsWith('Mac');

FILE: client/src/constants/DroppableTypes.js
  constant BOARD (line 6) | const BOARD = 'BOARD';
  constant LABEL (line 7) | const LABEL = 'LABEL';
  constant LIST (line 8) | const LIST = 'LIST';
  constant CARD (line 9) | const CARD = 'CARD';
  constant TASK_LIST (line 10) | const TASK_LIST = 'TASK_LIST';
  constant TASK (line 11) | const TASK = 'TASK';
  constant CUSTOM_FIELD_GROUP (line 12) | const CUSTOM_FIELD_GROUP = 'CUSTOM_FIELD_GROUP';
  constant CUSTOM_FIELD (line 13) | const CUSTOM_FIELD = 'CUSTOM_FIELD';

FILE: client/src/constants/Encodings.js
  constant BINARY (line 6) | const BINARY = 'binary';
  constant UTF8 (line 7) | const UTF8 = 'utf8';

FILE: client/src/constants/EntryActionTypes.js
  constant PREFIX (line 6) | const PREFIX = '@entry';

FILE: client/src/constants/ErrorCodes.js
  constant UNAUTHORIZED (line 6) | const UNAUTHORIZED = 'E_UNAUTHORIZED';
  constant NOT_FOUND (line 7) | const NOT_FOUND = 'E_NOT_FOUND';
  constant CONFLICT (line 8) | const CONFLICT = 'E_CONFLICT';

FILE: client/src/constants/ModalTypes.js
  constant ADMINISTRATION (line 6) | const ADMINISTRATION = 'ADMINISTRATION';
  constant ABOUT (line 7) | const ABOUT = 'ABOUT';
  constant USER_SETTINGS (line 8) | const USER_SETTINGS = 'USER_SETTINGS';
  constant ADD_PROJECT (line 9) | const ADD_PROJECT = 'ADD_PROJECT';
  constant PROJECT_SETTINGS (line 10) | const PROJECT_SETTINGS = 'PROJECT_SETTINGS';
  constant BOARD_SETTINGS (line 11) | const BOARD_SETTINGS = 'BOARD_SETTINGS';
  constant BOARD_ACTIVITIES (line 12) | const BOARD_ACTIVITIES = 'BOARD_ACTIVITIES';

FILE: client/src/constants/Paths.js
  constant ROOT (line 8) | const ROOT = `${Config.BASE_PATH}/`;
  constant LOGIN (line 9) | const LOGIN = `${Config.BASE_PATH}/login`;
  constant OIDC_CALLBACK (line 10) | const OIDC_CALLBACK = `${Config.BASE_PATH}/oidc-callback`;
  constant PROJECTS (line 11) | const PROJECTS = `${Config.BASE_PATH}/projects/:id`;
  constant BOARDS (line 12) | const BOARDS = `${Config.BASE_PATH}/boards/:id`;
  constant CARDS (line 13) | const CARDS = `${Config.BASE_PATH}/cards/:id`;

FILE: client/src/constants/StaticUsers.js
  constant DELETED (line 12) | const DELETED = {
  constant STATIC_USER_BY_ID (line 22) | const STATIC_USER_BY_ID = {

FILE: client/src/constants/ToastTypes.js
  constant FILE_IS_TOO_BIG (line 6) | const FILE_IS_TOO_BIG = 'FILE_IS_TOO_BIG';
  constant NOT_ENOUGH_STORAGE (line 7) | const NOT_ENOUGH_STORAGE = 'NOT_ENOUGH_STORAGE';
  constant EMPTY_TRASH (line 8) | const EMPTY_TRASH = 'EMPTY_TRASH';
  constant SOURCE_CARD_NOT_COPYABLE (line 9) | const SOURCE_CARD_NOT_COPYABLE = 'SOURCE_CARD_NOT_COPYABLE';
  constant SOURCE_CARD_NOT_MOVABLE (line 10) | const SOURCE_CARD_NOT_MOVABLE = 'SOURCE_CARD_NOT_MOVABLE';

FILE: client/src/hooks/use-form.js
  constant CHECKED_TYPES_SET (line 8) | const CHECKED_TYPES_SET = new Set(['checkbox', 'radio']);

FILE: client/src/i18n.js
  constant FALLBACK_LANGUAGE (line 23) | const FALLBACK_LANGUAGE = 'en-US';
  method init (line 27) | init() {}
  method addLocale (line 28) | addLocale(language, locale) {
  method setLanguage (line 32) | setLanguage(language) {
  method getLocale (line 35) | getLocale(language = i18n.resolvedLanguage) {
  method format (line 38) | format(date, format, { language, ...options } = {}) {
  method parse (line 44) | parse(dateString, format, backupDate, { language, ...options } = {}) {
  method init (line 53) | init() {
  method addLocale (line 56) | addLocale(_, locale) {
  method setLanguage (line 59) | setLanguage() {}
  method init (line 63) | init() {
  method addLocale (line 67) | addLocale(language, locale) {
  method setLanguage (line 72) | setLanguage(language) {
  method process (line 92) | process(value, _, options) {
  method process (line 100) | process(value, _, options) {
  method format (line 117) | format(value, format, language) {

FILE: client/src/lib/custom-ui/components/Input/Input.jsx
  class Input (line 11) | class Input extends SemanticUIInput {

FILE: client/src/lib/custom-ui/components/Input/InputPassword.jsx
  constant STRENGTH_SCORE_COLORS (line 14) | const STRENGTH_SCORE_COLORS = ['red', 'orange', 'yellow', 'olive', 'gree...

FILE: client/src/lib/custom-ui/components/Input/MaskedInput.jsx
  class MaskedInput (line 8) | class MaskedInput extends InputMask {
    method focus (line 9) | focus(options) {
    method select (line 13) | select() {

FILE: client/src/lib/custom-ui/components/Popup/Popup.jsx
  class Popup (line 10) | class Popup extends SemanticUIPopup {

FILE: client/src/lib/redux-router/ReduxRouter.jsx
  function ReduxRouter (line 13) | function ReduxRouter({ children, history, selector, basename }) {

FILE: client/src/lib/redux-router/actions.js
  constant LOCATION_CHANGE_HANDLE (line 6) | const LOCATION_CHANGE_HANDLE = '@@router/LOCATION_CHANGE_HANDLE';
  constant HISTORY_METHOD_CALL (line 17) | const HISTORY_METHOD_CALL = '@@router/HISTORY_METHOD_CALL';

FILE: client/src/lib/syntax-highlighter/language-definitions/hlsl.js
  constant HLSL_NUMBER_RE (line 34) | const HLSL_NUMBER_RE =
  constant HLSL_NUMBER_MODE (line 37) | const HLSL_NUMBER_MODE = {

FILE: client/src/lib/syntax-highlighter/languages.js
  constant LANGUAGES_BY_FILENAME (line 8) | const LANGUAGES_BY_FILENAME = {};
  constant LANGUAGES_BY_EXTENSION (line 9) | const LANGUAGES_BY_EXTENSION = {};

FILE: client/src/models/Activity.js
  method reducer (line 38) | static reducer({ type, payload }, Activity) {

FILE: client/src/models/Attachment.js
  method reducer (line 53) | static reducer({ type, payload }, Attachment) {
  method duplicate (line 129) | duplicate(id, data) {

FILE: client/src/models/BackgroundImage.js
  method reducer (line 25) | static reducer({ type, payload }, BackgroundImage) {
  method deleteRelated (line 85) | deleteRelated() {
  method deleteWithRelated (line 94) | deleteWithRelated() {

FILE: client/src/models/BaseCustomFieldGroup.js
  method reducer (line 24) | static reducer({ type, payload }, BaseCustomFieldGroup) {
  method getCustomFieldsQuerySet (line 92) | getCustomFieldsQuerySet() {
  method deleteRelated (line 96) | deleteRelated() {
  method deleteWithRelated (line 104) | deleteWithRelated() {

FILE: client/src/models/BaseModel.js
  class BaseModel (line 8) | class BaseModel extends Model {
    method _onDelete (line 10) | _onDelete() {}

FILE: client/src/models/Board.js
  method reducer (line 68) | static reducer({ type, payload }, Board) {
  method getMembershipsQuerySet (line 277) | getMembershipsQuerySet() {
  method getLabelsQuerySet (line 281) | getLabelsQuerySet() {
  method getListsQuerySet (line 285) | getListsQuerySet() {
  method getKanbanListsQuerySet (line 289) | getKanbanListsQuerySet() {
  method getCustomFieldGroupsQuerySet (line 293) | getCustomFieldGroupsQuerySet() {
  method getActivitiesQuerySet (line 297) | getActivitiesQuerySet() {
  method getUnreadNotificationsQuerySet (line 301) | getUnreadNotificationsQuerySet() {
  method getNotificationServicesQuerySet (line 307) | getNotificationServicesQuerySet() {
  method getMembershipModelByUserId (line 311) | getMembershipModelByUserId(userId) {
  method getCardsModelArray (line 319) | getCardsModelArray() {
  method getFilteredCardsModelArray (line 325) | getFilteredCardsModelArray() {
  method getActivitiesModelArray (line 395) | getActivitiesModelArray() {
  method hasMembershipWithUserId (line 419) | hasMembershipWithUserId(userId) {
  method isAvailableForUser (line 427) | isAvailableForUser(userModel) {
  method deleteListsWithRelated (line 438) | deleteListsWithRelated(soft) {
  method deleteClearable (line 444) | deleteClearable() {
  method deleteRelated (line 449) | deleteRelated(exceptMemberUserId, soft) {
  method deleteWithClearable (line 466) | deleteWithClearable() {
  method deleteWithRelated (line 471) | deleteWithRelated(soft) {

FILE: client/src/models/BoardMembership.js
  method reducer (line 30) | static reducer({ type, payload }, BoardMembership) {
  method deleteRelated (line 108) | deleteRelated(isCurrentUser = false) {
  method deleteWithRelated (line 144) | deleteWithRelated(isCurrentUser) {

FILE: client/src/models/Card.js
  method reducer (line 85) | static reducer({ type, payload }, Card) {
  method getTaskListsQuerySet (line 546) | getTaskListsQuerySet() {
  method getAttachmentsQuerySet (line 550) | getAttachmentsQuerySet() {
  method getCustomFieldGroupsQuerySet (line 554) | getCustomFieldGroupsQuerySet() {
  method getCommentsQuerySet (line 558) | getCommentsQuerySet() {
  method getActivitiesQuerySet (line 562) | getActivitiesQuerySet() {
  method getUnreadNotificationsQuerySet (line 566) | getUnreadNotificationsQuerySet() {
  method getShownOnFrontOfCardTaskListsModelArray (line 572) | getShownOnFrontOfCardTaskListsModelArray() {
  method getCommentsModelArray (line 578) | getCommentsModelArray() {
  method getActivitiesModelArray (line 602) | getActivitiesModelArray() {
  method hasUserWithId (line 626) | hasUserWithId(userId) {
  method isAvailableForUser (line 634) | isAvailableForUser(userModel) {
  method duplicate (line 638) | duplicate(id, data, rootId) {
  method syncAfterBoardChange (line 713) | syncAfterBoardChange() {
  method deleteClearable (line 754) | deleteClearable() {
  method deleteRelated (line 759) | deleteRelated(soft = false) {
  method deleteWithClearable (line 784) | deleteWithClearable() {
  method deleteWithRelated (line 789) | deleteWithRelated(soft) {

FILE: client/src/models/Comment.js
  method reducer (line 32) | static reducer({ type, payload }, Comment) {

FILE: client/src/models/CustomField.js
  method reducer (line 31) | static reducer({ type, payload }, CustomField) {
  method duplicate (line 108) | duplicate(id, data) {
  method deleteRelated (line 120) | deleteRelated() {
  method deleteWithRelated (line 124) | deleteWithRelated() {

FILE: client/src/models/CustomFieldGroup.js
  method reducer (line 35) | static reducer({ type, payload }, CustomFieldGroup) {
  method getCustomFieldsQuerySet (line 111) | getCustomFieldsQuerySet() {
  method getCustomFieldsModelArray (line 115) | getCustomFieldsModelArray() {
  method getShownOnFrontOfCardCustomFieldsModelArray (line 123) | getShownOnFrontOfCardCustomFieldsModelArray() {
  method duplicate (line 129) | duplicate(id, data, rootId) {
  method deleteRelated (line 157) | deleteRelated() {
  method deleteWithRelated (line 162) | deleteWithRelated() {

FILE: client/src/models/CustomFieldValue.js
  method reducer (line 46) | static reducer({ type, payload }, CustomFieldValue) {
  method duplicate (line 115) | duplicate(data) {

FILE: client/src/models/Label.js
  method reducer (line 26) | static reducer({ type, payload }, Label) {
  method deleteRelated (line 98) | deleteRelated() {
  method deleteWithRelated (line 114) | deleteWithRelated() {

FILE: client/src/models/List.js
  constant POSITION_BY_LIST_TYPE (line 16) | const POSITION_BY_LIST_TYPE = {
  method reducer (line 72) | static reducer({ type, payload }, List) {
  method getCardsQuerySet (line 278) | getCardsQuerySet() {
  method getCardsModelArray (line 289) | getCardsModelArray() {
  method getFilteredCardsModelArray (line 323) | getFilteredCardsModelArray() {
  method isAvailableForUser (line 393) | isAvailableForUser(userModel) {
  method sortCards (line 397) | sortCards(options) {
  method deleteRelated (line 438) | deleteRelated(soft) {
  method deleteWithRelated (line 444) | deleteWithRelated(soft) {

FILE: client/src/models/Notification.js
  method reducer (line 49) | static reducer({ type, payload }, Notification) {

FILE: client/src/models/NotificationService.js
  method reducer (line 33) | static reducer({ type, payload }, NotificationService) {

FILE: client/src/models/Project.js
  method reducer (line 42) | static reducer({ type, payload }, Project) {
  method getSharedQuerySet (line 169) | static getSharedQuerySet() {
  method getManagersQuerySet (line 175) | getManagersQuerySet() {
  method getBackgroundImagesQuerySet (line 179) | getBackgroundImagesQuerySet() {
  method getBaseCustomFieldGroupsQuerySet (line 183) | getBaseCustomFieldGroupsQuerySet() {
  method getBoardsQuerySet (line 187) | getBoardsQuerySet() {
  method getBoardsModelArrayForUserWithId (line 191) | getBoardsModelArrayForUserWithId(userId) {
  method getBoardsModelArrayAvailableForUser (line 197) | getBoardsModelArrayAvailableForUser(userModel) {
  method hasManagerWithUserId (line 205) | hasManagerWithUserId(userId) {
  method hasMembershipWithUserIdInAnyBoard (line 213) | hasMembershipWithUserIdInAnyBoard(userId) {
  method isExternalAccessibleForUser (line 219) | isExternalAccessibleForUser(userModel) {
  method isAvailableForUser (line 227) | isAvailableForUser(userModel) {
  method deleteRelated (line 234) | deleteRelated(soft) {
  method deleteWithRelated (line 250) | deleteWithRelated(soft) {

FILE: client/src/models/ProjectManager.js
  method reducer (line 28) | static reducer({ type, payload }, ProjectManager) {

FILE: client/src/models/Task.js
  method reducer (line 38) | static reducer({ type, payload }, Task) {
  method duplicate (line 114) | duplicate(id, data) {

FILE: client/src/models/TaskList.js
  method reducer (line 27) | static reducer({ type, payload }, TaskList) {
  method getTasksQuerySet (line 103) | getTasksQuerySet() {
  method duplicate (line 107) | duplicate(id, data, rootId) {
  method deleteRelated (line 131) | deleteRelated() {
  method deleteWithRelated (line 135) | deleteWithRelated() {

FILE: client/src/models/User.js
  constant DEFAULT_EMAIL_UPDATE_FORM (line 14) | const DEFAULT_EMAIL_UPDATE_FORM = {
  constant DEFAULT_PASSWORD_UPDATE_FORM (line 23) | const DEFAULT_PASSWORD_UPDATE_FORM = {
  constant DEFAULT_USERNAME_UPDATE_FORM (line 32) | const DEFAULT_USERNAME_UPDATE_FORM = {
  constant DEFAULT_API_KEY_STATE (line 41) | const DEFAULT_API_KEY_STATE = {
  method reducer (line 101) | static reducer({ type, payload }, User) {
  method getAllQuerySet (line 371) | static getAllQuerySet() {
  method getActiveQuerySet (line 375) | static getActiveQuerySet() {
  method getProjectManagersQuerySet (line 381) | getProjectManagersQuerySet() {
  method getBoardMembershipsQuerySet (line 385) | getBoardMembershipsQuerySet() {
  method getUnreadNotificationsQuerySet (line 389) | getUnreadNotificationsQuerySet() {
  method getNotificationServicesQuerySet (line 397) | getNotificationServicesQuerySet() {
  method getManagerProjectsModelArray (line 401) | getManagerProjectsModelArray() {
  method getMembershipProjectsModelArray (line 407) | getMembershipProjectsModelArray() {
  method getSeparatedProjectsModelArray (line 422) | getSeparatedProjectsModelArray() {
  method getProjectsModelArray (line 466) | getProjectsModelArray() {
  method getFavoriteProjectsModelArray (line 473) | getFavoriteProjectsModelArray(orderByArgs) {
  method getFilteredSeparatedProjectsModelArray (line 487) | getFilteredSeparatedProjectsModelArray(search, isHidden, orderByArgs) {
  method getFilteredProjectsModelArray (line 504) | getFilteredProjectsModelArray(search, isHidden, orderByArgs) {
  method deleteRelated (line 515) | deleteRelated() {
  method deleteWithRelated (line 567) | deleteWithRelated() {

FILE: client/src/models/Webhook.js
  method reducer (line 28) | static reducer({ type, payload }, Webhook) {
  method getAllQuerySet (line 87) | static getAllQuerySet() {

FILE: client/src/selectors/users.js
  constant ORDER_BY_ARGS_BY_PROJECTS_ORDER (line 20) | const ORDER_BY_ARGS_BY_PROJECTS_ORDER = {

FILE: client/src/utils/access-token-storage.js
  constant PATH (line 11) | const PATH = Config.BASE_PATH || '/';

FILE: client/src/utils/build-search-parts.js
  constant SEARCH_PARTS_REGEX (line 6) | const SEARCH_PARTS_REGEX = /[ ,;]+/;

FILE: client/src/utils/mentions.js
  constant USERNAME_CHAR_CLASS (line 6) | const USERNAME_CHAR_CLASS = 'a-zA-Z0-9._';
  constant USERNAME_CHAR_REGEX (line 7) | const USERNAME_CHAR_REGEX = new RegExp(`^[${USERNAME_CHAR_CLASS}]$`);
  constant MENTION_TEXT_REGEX (line 9) | const MENTION_TEXT_REGEX = new RegExp(
  constant MENTION_MARKUP_REGEX (line 14) | const MENTION_MARKUP_REGEX = /@\[(.*?)\]\((.*?)\)/g;

FILE: client/src/utils/parse-time.js
  constant TIME_REGEX (line 8) | const TIME_REGEX =
  constant ALTERNATIVE_AM_MERIDIEMS_SET (line 11) | const ALTERNATIVE_AM_MERIDIEMS_SET = new Set(['am', 'a.m.', 'midnight', ...
  constant ALTERNATIVE_PM_MERIDIEMS_SET (line 12) | const ALTERNATIVE_PM_MERIDIEMS_SET = new Set(['pm', 'p.m.', 'noon', 'n']);
  constant PATTERNS_GROUPS_BY_TIME_FORMAT (line 19) | const PATTERNS_GROUPS_BY_TIME_FORMAT = {
  constant INVALID_DATE (line 40) | const INVALID_DATE = new Date('invalid-date');

FILE: client/src/utils/validator.js
  constant USERNAME_REGEX (line 9) | const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;

FILE: client/tests/acceptance/Config.js
  constant BASE_URL (line 1) | const BASE_URL = process.env.BASE_URL || 'http://localhost:1337';
  constant TIMEOUT (line 3) | const TIMEOUT = parseInt(process.env.TIMEOUT, 10) || 6000;
  constant PLAYWRIGHT (line 5) | const PLAYWRIGHT = {

FILE: client/tests/acceptance/pages/HomePage.js
  class HomePage (line 3) | class HomePage {
    method constructor (line 4) | constructor() {
    method navigate (line 11) | async navigate() {
    method logout (line 15) | async logout() {

FILE: client/tests/acceptance/pages/LoginPage.js
  class LoginPage (line 3) | class LoginPage {
    method constructor (line 4) | constructor() {
    method navigate (line 13) | async navigate() {
    method login (line 17) | async login(emailOrUsername, password) {
    method getMessage (line 23) | async getMessage() {

FILE: client/vite.config.js
  method closeBundle (line 18) | closeBundle() {

FILE: server/api/controllers/_internal/update-config.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/controllers/access-tokens/accept-terms.js
  method fn (line 148) | async fn(inputs) {

FILE: server/api/controllers/access-tokens/create.js
  method fn (line 170) | async fn(inputs) {

FILE: server/api/controllers/access-tokens/debug-oidc.js
  method fn (line 32) | async fn(inputs) {

FILE: server/api/controllers/access-tokens/delete.js
  method fn (line 36) | async fn() {

FILE: server/api/controllers/access-tokens/exchange-with-oidc.js
  method fn (line 240) | async fn(inputs) {

FILE: server/api/controllers/access-tokens/revoke-pending-token.js
  method fn (line 70) | async fn(inputs) {

FILE: server/api/controllers/actions/index-in-board.js
  method fn (line 86) | async fn(inputs) {

FILE: server/api/controllers/actions/index-in-card.js
  method fn (line 86) | async fn(inputs) {

FILE: server/api/controllers/attachments/create.js
  method fn (line 165) | async fn(inputs, exits) {

FILE: server/api/controllers/attachments/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/attachments/update.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/background-images/create.js
  method fn (line 126) | async fn(inputs, exits) {

FILE: server/api/controllers/background-images/delete.js
  method fn (line 67) | async fn(inputs) {

FILE: server/api/controllers/base-custom-field-groups/create.js
  method fn (line 86) | async fn(inputs) {

FILE: server/api/controllers/base-custom-field-groups/delete.js
  method fn (line 67) | async fn(inputs) {

FILE: server/api/controllers/base-custom-field-groups/update.js
  method fn (line 84) | async fn(inputs) {

FILE: server/api/controllers/board-memberships/create.js
  method fn (line 124) | async fn(inputs) {

FILE: server/api/controllers/board-memberships/delete.js
  method fn (line 65) | async fn(inputs) {

FILE: server/api/controllers/board-memberships/update.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/boards/create.js
  method fn (line 164) | async fn(inputs, exits) {

FILE: server/api/controllers/boards/delete.js
  method fn (line 65) | async fn(inputs) {

FILE: server/api/controllers/boards/show.js
  method fn (line 178) | async fn(inputs) {

FILE: server/api/controllers/boards/update.js
  method fn (line 144) | async fn(inputs) {

FILE: server/api/controllers/bootstrap/show.js
  method fn (line 74) | async fn() {

FILE: server/api/controllers/card-labels/create.js
  method fn (line 104) | async fn(inputs) {

FILE: server/api/controllers/card-labels/delete.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/card-memberships/create.js
  method fn (line 104) | async fn(inputs) {

FILE: server/api/controllers/card-memberships/delete.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/cards/create.js
  method fn (line 174) | async fn(inputs) {

FILE: server/api/controllers/cards/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/cards/duplicate.js
  method fn (line 191) | async fn(inputs) {

FILE: server/api/controllers/cards/index.js
  method fn (line 200) | async fn(inputs) {

FILE: server/api/controllers/cards/read-notifications.js
  method fn (line 76) | async fn(inputs) {

FILE: server/api/controllers/cards/show.js
  method fn (line 131) | async fn(inputs) {

FILE: server/api/controllers/cards/update.js
  method fn (line 227) | async fn(inputs) {

FILE: server/api/controllers/comments/create.js
  method fn (line 92) | async fn(inputs) {

FILE: server/api/controllers/comments/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/comments/index.js
  method fn (line 86) | async fn(inputs) {

FILE: server/api/controllers/comments/update.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/config/show.js
  method fn (line 30) | async fn() {

FILE: server/api/controllers/config/test-smtp.js
  method fn (line 46) | async fn() {

FILE: server/api/controllers/config/update.js
  method fn (line 127) | async fn(inputs) {

FILE: server/api/controllers/custom-field-groups/create-in-board.js
  method fn (line 123) | async fn(inputs) {

FILE: server/api/controllers/custom-field-groups/create-in-card.js
  method fn (line 123) | async fn(inputs) {

FILE: server/api/controllers/custom-field-groups/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/custom-field-groups/show.js
  method fn (line 82) | async fn(inputs) {

FILE: server/api/controllers/custom-field-groups/update.js
  method fn (line 109) | async fn(inputs) {

FILE: server/api/controllers/custom-field-values/create-or-update.js
  method fn (line 126) | async fn(inputs) {

FILE: server/api/controllers/custom-field-values/delete.js
  method fn (line 107) | async fn(inputs) {

FILE: server/api/controllers/custom-fields/create-in-base-custom-field-group.js
  method fn (line 102) | async fn(inputs) {

FILE: server/api/controllers/custom-fields/create-in-custom-field-group.js
  method fn (line 110) | async fn(inputs) {

FILE: server/api/controllers/custom-fields/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/custom-fields/update.js
  method fn (line 106) | async fn(inputs) {

FILE: server/api/controllers/file-attachments/download-thumbnail.js
  constant FILE_NAMES (line 14) | const FILE_NAMES = ['outside-360', 'outside-720'];
  method fn (line 40) | async fn(inputs, exits) {

FILE: server/api/controllers/file-attachments/download.js
  constant INLINE_MIME_TYPES_SET (line 14) | const INLINE_MIME_TYPES_SET = new Set([
  method fn (line 41) | async fn(inputs, exits) {

FILE: server/api/controllers/index.js
  method fn (line 14) | fn() {

FILE: server/api/controllers/labels/create.js
  method fn (line 115) | async fn(inputs) {

FILE: server/api/controllers/labels/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/labels/update.js
  method fn (line 110) | async fn(inputs) {

FILE: server/api/controllers/lists/clear.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/lists/create.js
  method fn (line 114) | async fn(inputs) {

FILE: server/api/controllers/lists/delete.js
  method fn (line 84) | async fn(inputs) {

FILE: server/api/controllers/lists/move-cards.js
  method fn (line 107) | async fn(inputs) {

FILE: server/api/controllers/lists/show.js
  method fn (line 137) | async fn(inputs) {

FILE: server/api/controllers/lists/sort.js
  method fn (line 117) | async fn(inputs) {

FILE: server/api/controllers/lists/update.js
  method fn (line 130) | async fn(inputs) {

FILE: server/api/controllers/notification-services/create-in-board.js
  method fn (line 103) | async fn(inputs) {

FILE: server/api/controllers/notification-services/create-in-user.js
  method fn (line 103) | async fn(inputs) {

FILE: server/api/controllers/notification-services/delete.js
  method fn (line 65) | async fn(inputs) {

FILE: server/api/controllers/notification-services/test.js
  method fn (line 65) | async fn(inputs) {

FILE: server/api/controllers/notification-services/update.js
  method fn (line 91) | async fn(inputs) {

FILE: server/api/controllers/notifications/index.js
  method fn (line 47) | async fn() {

FILE: server/api/controllers/notifications/read-all.js
  method fn (line 36) | async fn() {

FILE: server/api/controllers/notifications/show.js
  method fn (line 76) | async fn(inputs) {

FILE: server/api/controllers/notifications/update.js
  method fn (line 79) | async fn(inputs) {

FILE: server/api/controllers/project-managers/create.js
  method fn (line 112) | async fn(inputs) {

FILE: server/api/controllers/project-managers/delete.js
  method fn (line 81) | async fn(inputs) {

FILE: server/api/controllers/projects/create.js
  method fn (line 90) | async fn(inputs) {

FILE: server/api/controllers/projects/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/projects/index.js
  method fn (line 96) | async fn() {

FILE: server/api/controllers/projects/show.js
  method fn (line 125) | async fn(inputs) {

FILE: server/api/controllers/projects/update.js
  method fn (line 196) | async fn(inputs) {

FILE: server/api/controllers/swagger/show.js
  constant SWAGGER_PATH (line 9) | const SWAGGER_PATH = path.join(sails.config.appPath, 'swagger.json');
  method fn (line 12) | async fn() {

FILE: server/api/controllers/task-lists/create.js
  method fn (line 117) | async fn(inputs) {

FILE: server/api/controllers/task-lists/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/task-lists/show.js
  method fn (line 76) | async fn(inputs) {

FILE: server/api/controllers/task-lists/update.js
  method fn (line 113) | async fn(inputs) {

FILE: server/api/controllers/tasks/create.js
  method fn (line 130) | async fn(inputs) {

FILE: server/api/controllers/tasks/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/tasks/update.js
  method fn (line 132) | async fn(inputs) {

FILE: server/api/controllers/terms/show.js
  method fn (line 69) | async fn(inputs) {

FILE: server/api/controllers/users/create-api-key.js
  method fn (line 75) | async fn(inputs) {

FILE: server/api/controllers/users/create.js
  method fn (line 200) | async fn(inputs) {

FILE: server/api/controllers/users/delete.js
  method fn (line 73) | async fn(inputs) {

FILE: server/api/controllers/users/index.js
  method fn (line 50) | async fn() {

FILE: server/api/controllers/users/show.js
  constant CURRENT_USER_ID (line 69) | const CURRENT_USER_ID = 'me';
  constant ID_OR_CURRENT_USER_ID_REGEX (line 71) | const ID_OR_CURRENT_USER_ID_REGEX = new RegExp(`${ID_REGEX.source}|^${CU...
  method fn (line 95) | async fn(inputs) {

FILE: server/api/controllers/users/update-avatar.js
  method fn (line 95) | async fn(inputs, exits) {

FILE: server/api/controllers/users/update-email.js
  method fn (line 120) | async fn(inputs) {

FILE: server/api/controllers/users/update-password.js
  method fn (line 122) | async fn(inputs) {

FILE: server/api/controllers/users/update-username.js
  method fn (line 122) | async fn(inputs) {

FILE: server/api/controllers/users/update.js
  method fn (line 229) | async fn(inputs) {

FILE: server/api/controllers/webhooks/create.js
  method fn (line 121) | async fn(inputs) {

FILE: server/api/controllers/webhooks/delete.js
  method fn (line 65) | async fn(inputs) {

FILE: server/api/controllers/webhooks/index.js
  method fn (line 36) | async fn() {

FILE: server/api/controllers/webhooks/update.js
  method fn (line 130) | async fn(inputs) {

FILE: server/api/helpers/access-tokens/handle-steps.js
  constant PENDING_TOKEN_EXPIRES_IN (line 14) | const PENDING_TOKEN_EXPIRES_IN = 10 * 60;
  method fn (line 44) | async fn(inputs) {

FILE: server/api/helpers/actions/create-one.js
  method fn (line 117) | async fn(inputs) {

FILE: server/api/helpers/attachments/create-one.js
  method fn (line 32) | async fn(inputs) {

FILE: server/api/helpers/attachments/delete-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/attachments/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/attachments/present-many.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/attachments/present-one.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/attachments/process-link.js
  method fn (line 16) | async fn(inputs) {

FILE: server/api/helpers/attachments/process-uploaded-file.js
  method fn (line 23) | async fn(inputs) {

FILE: server/api/helpers/attachments/update-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/background-images/create-one.js
  method fn (line 24) | async fn(inputs) {

FILE: server/api/helpers/background-images/delete-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/background-images/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/background-images/present-many.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/background-images/present-one.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/background-images/process-uploaded-file.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/base-custom-field-groups/create-one.js
  method fn (line 21) | async fn(inputs) {

FILE: server/api/helpers/base-custom-field-groups/delete-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/base-custom-field-groups/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/base-custom-field-groups/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/base-custom-field-groups/update-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/board-memberships/create-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/board-memberships/delete-one.js
  method fn (line 35) | async fn(inputs) {

FILE: server/api/helpers/board-memberships/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/board-memberships/update-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/boards/create-one.js
  method fn (line 27) | async fn(inputs) {

FILE: server/api/helpers/boards/delete-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/boards/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/boards/get-card-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/boards/get-kanban-lists-by-id.js
  method fn (line 17) | async fn(inputs) {

FILE: server/api/helpers/boards/get-member-user-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/boards/get-notification-services-total.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/boards/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/boards/get-subscription-user-ids.js
  method fn (line 17) | async fn(inputs) {

FILE: server/api/helpers/boards/import-from-trello.js
  method fn (line 24) | async fn(inputs) {

FILE: server/api/helpers/boards/process-uploaded-trello-import-file.js
  method fn (line 22) | async fn(inputs) {

FILE: server/api/helpers/boards/update-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/bootstrap/present-one.js
  method fn (line 22) | fn(inputs) {

FILE: server/api/helpers/card-labels/create-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/card-labels/delete-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/card-memberships/create-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/card-memberships/delete-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/cards/copy-custom-fields.js
  method fn (line 28) | async fn(inputs) {

FILE: server/api/helpers/cards/create-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/cards/delete-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/cards/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/cards/detach-custom-fields.js
  method fn (line 24) | async fn(inputs) {

FILE: server/api/helpers/cards/duplicate-one.js
  method fn (line 40) | async fn(inputs) {

FILE: server/api/helpers/cards/get-labels.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/cards/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/cards/get-subscription-user-ids.js
  method fn (line 17) | async fn(inputs) {

FILE: server/api/helpers/cards/read-notifications-for-user.js
  method fn (line 22) | async fn(inputs) {

FILE: server/api/helpers/cards/update-one.js
  method fn (line 49) | async fn(inputs) {

FILE: server/api/helpers/comments/create-one.js
  method fn (line 61) | async fn(inputs) {

FILE: server/api/helpers/comments/delete-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/comments/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/comments/update-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/config/present-one.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/config/update-main.js
  method fn (line 21) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/create-one-in-board.js
  method fn (line 30) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/create-one-in-card.js
  method fn (line 38) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/delete-one-in-board.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/delete-one-in-card.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/update-one-in-board.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/custom-field-groups/update-one-in-card.js
  method fn (line 45) | async fn(inputs) {

FILE: server/api/helpers/custom-field-values/create-or-update-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/custom-field-values/delete-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/create-one-in-base-custom-field-group.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/create-one-in-custom-field-group.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/delete-one-in-base-custom-field-group.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/delete-one-in-custom-field-group.js
  method fn (line 44) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/update-one-in-base-custom-field-group.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/custom-fields/update-one-in-custom-field-group.js
  method fn (line 48) | async fn(inputs) {

FILE: server/api/helpers/internal-config/update-main.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/labels/create-one.js
  method fn (line 28) | async fn(inputs) {

FILE: server/api/helpers/labels/delete-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/labels/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/labels/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/labels/update-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/lists/clear-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/lists/create-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/lists/delete-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/lists/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/lists/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/lists/is-archive-or-trash.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/lists/is-finite.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/lists/is-kanban.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/lists/move-cards.js
  method fn (line 38) | async fn(inputs) {

FILE: server/api/helpers/lists/resolve-name.js
  method fn (line 19) | fn(inputs) {

FILE: server/api/helpers/lists/sort-one.js
  method fn (line 40) | async fn(inputs) {

FILE: server/api/helpers/lists/update-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/notification-services/create-one-in-board.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/notification-services/create-one-in-user.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/notification-services/delete-one-in-board.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/notification-services/delete-one-in-user.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/notification-services/get-path-to-user-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/notification-services/update-one-in-board.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/notification-services/update-one-in-user.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/notifications/create-many.js
  method fn (line 236) | async fn(inputs) {

FILE: server/api/helpers/notifications/create-one.js
  method fn (line 234) | async fn(inputs) {

FILE: server/api/helpers/notifications/read-all-for-user.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/notifications/update-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/project-managers/create-one.js
  method fn (line 30) | async fn(inputs) {

FILE: server/api/helpers/project-managers/delete-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/project-managers/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/projects/create-one.js
  method fn (line 21) | async fn(inputs) {

FILE: server/api/helpers/projects/delete-one.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/projects/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/projects/get-board-ids-by-id.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/projects/get-board-memberships-total-by-id-and-user-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/projects/get-boards-total-by-id.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/projects/get-lonely-by-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/projects/get-manager-user-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/projects/get-project-managers-total-by-id.js
  method fn (line 17) | async fn(inputs) {

FILE: server/api/helpers/projects/make-scoper.js
  class Scoper (line 6) | class Scoper {
    method constructor (line 7) | constructor(project, board, { notificationService }) {
    method replaceProject (line 26) | replaceProject(project) {
    method replaceBoard (line 42) | replaceBoard(board) {
    method clone (line 54) | clone() {
    method cloneForProject (line 58) | cloneForProject(project) {
    method cloneForBoard (line 65) | cloneForBoard(board) {
    method getAdminUserIds (line 72) | async getAdminUserIds() {
    method getProjectManagerUserIds (line 80) | async getProjectManagerUserIds() {
    method getBoardMemberships (line 88) | async getBoardMemberships() {
    method getUserIdsWithFullProjectVisibility (line 96) | async getUserIdsWithFullProjectVisibility() {
    method getBoardMembershipsForWholeProject (line 112) | async getBoardMembershipsForWholeProject() {
    method getBoardMemberUserIdsForWholeProject (line 122) | async getBoardMemberUserIdsForWholeProject() {
    method getBoardMemberUserIds (line 136) | async getBoardMemberUserIds() {
    method getProjectRelatedUserIds (line 146) | async getProjectRelatedUserIds() {
    method getBoardRelatedUserIds (line 160) | async getBoardRelatedUserIds() {
    method getNotificationServiceRelatedUserIds (line 171) | async getNotificationServiceRelatedUserIds() {
  method fn (line 200) | fn(inputs) {

FILE: server/api/helpers/projects/update-one.js
  method fn (line 39) | async fn(inputs) {

FILE: server/api/helpers/sessions/create-one.js
  method fn (line 20) | async fn(inputs) {

FILE: server/api/helpers/task-lists/create-one.js
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/task-lists/delete-one.js
  method fn (line 37) | async fn(inputs) {

FILE: server/api/helpers/task-lists/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/task-lists/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/task-lists/update-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/tasks/create-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/tasks/delete-one.js
  method fn (line 41) | async fn(inputs) {

FILE: server/api/helpers/tasks/get-path-to-project-by-id.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/tasks/update-one.js
  method fn (line 50) | async fn(inputs) {

FILE: server/api/helpers/users/build-gravatar-url.js
  method fn (line 18) | fn(inputs) {

FILE: server/api/helpers/users/create-one.js
  method fn (line 29) | async fn(inputs) {

FILE: server/api/helpers/users/delete-one.js
  method fn (line 21) | async fn(inputs) {

FILE: server/api/helpers/users/delete-related.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/users/get-all-active-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/users/get-manager-project-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/users/get-notification-services-total.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/users/get-or-create-one-with-oidc.js
  method fn (line 28) | async fn(inputs) {

FILE: server/api/helpers/users/get-project-managers-total-by-id.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/users/is-admin-or-project-owner.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/users/is-board-member.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/users/is-board-subscriber.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/users/is-card-subscriber.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/users/is-project-favorite.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/users/is-project-manager.js
  method fn (line 18) | async fn(inputs) {

FILE: server/api/helpers/users/make-scoper.js
  class Scoper (line 6) | class Scoper {
    method constructor (line 7) | constructor(user) {
    method getProjectManagers (line 19) | async getProjectManagers() {
    method getSeparatedUserIds (line 27) | async getSeparatedUserIds() {
    method getUserRelatedProjectManagerAndBoardMemberUserIds (line 54) | async getUserRelatedProjectManagerAndBoardMemberUserIds() {
    method getPrivateUserRelatedUserIds (line 100) | async getPrivateUserRelatedUserIds() {
    method getPublicUserRelatedUserIds (line 110) | async getPublicUserRelatedUserIds(skipRelatedProjectManagerAndBoardMem...
  method fn (line 147) | fn(inputs) {

FILE: server/api/helpers/users/present-many.js
  method fn (line 20) | fn(inputs) {

FILE: server/api/helpers/users/present-one.js
  method fn (line 19) | fn(inputs) {

FILE: server/api/helpers/users/process-uploaded-avatar-file.js
  method fn (line 25) | async fn(inputs) {

FILE: server/api/helpers/users/update-one.js
  method fn (line 34) | async fn(inputs) {

FILE: server/api/helpers/utils/clear-http-only-token-cookie.js
  method fn (line 16) | fn(inputs) {

FILE: server/api/helpers/utils/create-jwt-token.js
  method fn (line 25) | fn(inputs) {

FILE: server/api/helpers/utils/download-favicon.js
  constant FETCH_TIMEOUT (line 11) | const FETCH_TIMEOUT = 4000;
  constant MAX_RESPONSE_LENGTH (line 12) | const MAX_RESPONSE_LENGTH = 1024 * 1024;
  constant FAVICON_TAGS_REGEX (line 14) | const FAVICON_TAGS_REGEX = /<link [^>]*rel="([^"]* )?icon( [^"]*)?"[^>]*...
  constant HREF_REGEX (line 15) | const HREF_REGEX = /href="(.*?)"/i;
  constant SIZES_REGEX (line 16) | const SIZES_REGEX = /sizes="(.*?)"/i;
  method fn (line 81) | async fn(inputs) {

FILE: server/api/helpers/utils/generate-api-key.js
  method fn (line 9) | fn() {

FILE: server/api/helpers/utils/generate-ids.js
  method fn (line 14) | async fn(inputs) {

FILE: server/api/helpers/utils/generate-random-string.js
  constant CHARS (line 8) | const CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu...
  method fn (line 20) | fn(inputs) {

FILE: server/api/helpers/utils/get-available-storage.js
  method fn (line 9) | async fn() {

FILE: server/api/helpers/utils/hash.js
  method fn (line 18) | fn(inputs) {

FILE: server/api/helpers/utils/insert-to-positionables.js
  constant GAP (line 6) | const GAP = 2 ** 14;
  constant MIN_GAP (line 7) | const MIN_GAP = 0.125;
  constant MAX_POSITION (line 8) | const MAX_POSITION = 2 ** 50;
  method fn (line 94) | fn(inputs) {

FILE: server/api/helpers/utils/is-preloaded-favicon-exists.js
  constant PRELOADED_FAVICON_FILENAMES (line 9) | const PRELOADED_FAVICON_FILENAMES = fs
  constant PRELOADED_FAVICON_HOSTNAMES_SET (line 13) | const PRELOADED_FAVICON_HOSTNAMES_SET = new Set(
  method fn (line 27) | fn(inputs) {

FILE: server/api/helpers/utils/make-smtp-transporter.js
  method fn (line 15) | async fn(inputs) {

FILE: server/api/helpers/utils/make-translator.js
  method fn (line 19) | fn(inputs) {

FILE: server/api/helpers/utils/map-records.js
  method fn (line 28) | fn(inputs) {

FILE: server/api/helpers/utils/receive-file.js
  method fn (line 20) | async fn(inputs, exits) {

FILE: server/api/helpers/utils/remove-unreferenced-uploaded-files.js
  constant PATH_SEGMENT_BY_TYPE (line 8) | const PATH_SEGMENT_BY_TYPE = {
  method fn (line 24) | fn(inputs) {

FILE: server/api/helpers/utils/send-email.js
  method fn (line 26) | async fn(inputs) {

FILE: server/api/helpers/utils/send-notifications.js
  constant PYTHON_PATH (line 10) | const PYTHON_PATH =
  method fn (line 33) | async fn(inputs) {

FILE: server/api/helpers/utils/send-webhooks.js
  function sendWebhook (line 44) | async function sendWebhook(webhook, event, data, prevData, user) {
  method fn (line 109) | fn(inputs) {

FILE: server/api/helpers/utils/set-http-only-token-cookie.js
  method fn (line 24) | fn(inputs) {

FILE: server/api/helpers/utils/verify-jwt-token.js
  method fn (line 22) | fn(inputs) {

FILE: server/api/helpers/webhooks/create-one.js
  method fn (line 26) | async fn(inputs) {

FILE: server/api/helpers/webhooks/delete-one.js
  method fn (line 21) | async fn(inputs) {

FILE: server/api/helpers/webhooks/update-one.js
  method fn (line 26) | async fn(inputs) {

FILE: server/api/hooks/current-user/index.js
  method initialize (line 65) | async initialize() {
  method fn (line 72) | async fn(req, res, next) {
  method fn (line 128) | async fn(req, res, next) {

FILE: server/api/hooks/file-manager/LocalFileManager.js
  class LocalFileManager (line 17) | class LocalFileManager {
    method move (line 19) | async move(sourceFilePath, filePathSegment) {
    method save (line 32) | async save(filePathSegment, stream) {
    method read (line 41) | async read(filePathSegment, { withHeaders = false } = {}) {
    method getSize (line 70) | async getSize(filePathSegment) {
    method rename (line 82) | async rename(filePathSegment, nextFilePathSegment) {
    method delete (line 91) | async delete(filePathSegment) {
    method listDir (line 100) | async listDir(dirPathSegment) {
    method renameDir (line 114) | async renameDir(dirPathSegment, nextDirPathSegment) {
    method deleteDir (line 123) | async deleteDir(dirPathSegment) {
    method isExists (line 128) | async isExists(pathSegment) {

FILE: server/api/hooks/file-manager/S3FileManager.js
  class S3FileManager (line 19) | class S3FileManager {
    method constructor (line 20) | constructor(client) {
    method move (line 24) | async move(sourceFilePath, filePathSegment, contentType) {
    method save (line 36) | async save(filePathSegment, stream, contentType) {
    method read (line 50) | async read(filePathSegment, { withHeaders = false } = {}) {
    method getSize (line 87) | async getSize(filePathSegment) {
    method rename (line 103) | async rename(filePathSegment, nextFilePathSegment) {
    method delete (line 119) | async delete(filePathSegment) {
    method listDir (line 128) | async listDir(dirPathSegment, { ContinuationToken } = {}) {
    method renameDir (line 159) | async renameDir(dirPathSegment, nextDirPathSegment, { ContinuationToke...
    method deleteDir (line 185) | async deleteDir(dirPathSegment, { ContinuationToken } = {}) {
    method isExists (line 216) | async isExists(pathSegment) {

FILE: server/api/hooks/file-manager/index.js
  method initialize (line 31) | async initialize() {
  method getInstance (line 42) | getInstance() {

FILE: server/api/hooks/oidc/index.js
  method initialize (line 24) | async initialize() {
  method getClient (line 32) | async getClient() {
  method getBootstrap (line 83) | async getBootstrap() {
  method isEnabled (line 110) | isEnabled() {

FILE: server/api/hooks/query-methods/index.js
  method initialize (line 44) | async initialize() {

FILE: server/api/hooks/query-methods/models/Action.js
  constant LIMIT (line 6) | const LIMIT = 50;

FILE: server/api/hooks/query-methods/models/Card.js
  constant LIMIT (line 9) | const LIMIT = 50;

FILE: server/api/hooks/query-methods/models/Comment.js
  constant LIMIT (line 6) | const LIMIT = 50;

FILE: server/api/hooks/query-methods/models/Notification.js
  constant LIMIT (line 6) | const LIMIT = 100;

FILE: server/api/hooks/query-methods/models/UploadedFile.js
  constant COLUMN_NAME_BY_TYPE (line 8) | const COLUMN_NAME_BY_TYPE = {

FILE: server/api/hooks/s3/index.js
  method initialize (line 25) | async initialize() {
  method getClient (line 43) | getClient() {
  method getBaseUrl (line 47) | getBaseUrl() {
  method isEnabled (line 65) | isEnabled() {

FILE: server/api/hooks/terms/index.js
  constant PATH (line 18) | const PATH = path.join(sails.config.appPath, 'terms');
  constant TEMPLATE_TYPE (line 19) | const TEMPLATE_TYPE = '_template';
  method initialize (line 50) | async initialize() {
  method getPayload (line 76) | async getPayload(language) {
  method getLanguages (line 90) | getLanguages() {
  method isSignatureValid (line 94) | isSignatureValid(value) {

FILE: server/api/hooks/watcher/index.js
  method initialize (line 37) | async initialize() {

FILE: server/api/models/Action.js
  constant INTERNAL_NOTIFIABLE_TYPES (line 79) | const INTERNAL_NOTIFIABLE_TYPES = [Types.MOVE_CARD, Types.ADD_MEMBER_TO_...
  constant EXTERNAL_NOTIFIABLE_TYPES (line 80) | const EXTERNAL_NOTIFIABLE_TYPES = [Types.CREATE_CARD, Types.MOVE_CARD];
  constant PERSONAL_NOTIFIABLE_TYPES (line 81) | const PERSONAL_NOTIFIABLE_TYPES = [Types.ADD_MEMBER_TO_CARD];

FILE: server/api/models/BoardMembership.js
  constant SHARED_RULES (line 74) | const SHARED_RULES = {
  constant RULES_BY_ROLE (line 79) | const RULES_BY_ROLE = {

FILE: server/api/models/Config.js
  constant MAIN_ID (line 78) | const MAIN_ID = '1';
  constant SMTP_FIELD_NAMES (line 80) | const SMTP_FIELD_NAMES = [

FILE: server/api/models/InternalConfig.js
  constant MAIN_ID (line 13) | const MAIN_ID = '1';

FILE: server/api/models/Label.js
  constant COLORS (line 64) | const COLORS = [

FILE: server/api/models/List.js
  constant FINITE_TYPES (line 96) | const FINITE_TYPES = [Types.ACTIVE, Types.CLOSED];
  constant KANBAN_TYPES (line 98) | const KANBAN_TYPES = [Types.ACTIVE, Types.CLOSED];
  constant TYPE_STATE_BY_TYPE (line 100) | const TYPE_STATE_BY_TYPE = {
  constant COLORS (line 105) | const COLORS = [

FILE: server/api/models/Project.js
  constant BACKGROUND_GRADIENTS (line 95) | const BACKGROUND_GRADIENTS = [

FILE: server/api/models/StorageUsage.js
  constant MAIN_ID (line 13) | const MAIN_ID = '1';

FILE: server/api/models/User.js
  constant LANGUAGES (line 202) | const LANGUAGES = [
  constant PRIVATE_FIELD_NAMES (line 241) | const PRIVATE_FIELD_NAMES = ['email', 'apiKeyPrefix', 'apiKeyHash', 'isS...
  constant PERSONAL_FIELD_NAMES (line 243) | const PERSONAL_FIELD_NAMES = [
  constant INTERNAL (line 254) | const INTERNAL = {
  constant OIDC (line 259) | const OIDC = {

FILE: server/build.js
  constant OUT_DIR (line 10) | const OUT_DIR = 'dist';

FILE: server/config/http.js
  method www (line 57) | www(req, res, next) {

FILE: server/config/models.js
  method beforeCreate (line 82) | beforeCreate(valuesToSet, proceed) {
  method beforeUpdate (line 88) | beforeUpdate(valuesToSet, proceed) {

FILE: server/constants.js
  constant POSITION_GAP (line 5) | const POSITION_GAP = 65536;
  constant MAX_SIZE_TO_GET_ENCODING (line 7) | const MAX_SIZE_TO_GET_ENCODING = 8 * 1024 * 1024;
  constant MAX_SIZE_TO_PROCESS_AS_IMAGE (line 8) | const MAX_SIZE_TO_PROCESS_AS_IMAGE = 64 * 1024 * 1024;

FILE: server/db/knexfile.js
  function buildSSLConfig (line 15) | function buildSSLConfig() {

FILE: server/setup-python.js
  constant VENV_PATH (line 4) | const VENV_PATH = path.join(__dirname, '.venv');
  constant REQUIREMENTS_PATH (line 5) | const REQUIREMENTS_PATH = path.join(__dirname, 'requirements.txt');
  constant PYTHON_PATH (line 7) | const PYTHON_PATH =

FILE: server/test/utils/remote-address.test.js
  constant MOCK_REQUEST (line 8) | const MOCK_REQUEST = {

FILE: server/utils/build-query-parts.js
  constant QUERY_PARTS_REGEX (line 6) | const QUERY_PARTS_REGEX = /[ ,;]+/;

FILE: server/utils/filenamify.js
  function filenameReservedRegex (line 14) | function filenameReservedRegex() {
  function windowsReservedNameRegex (line 18) | function windowsReservedNameRegex() {
  constant MAX_FILENAME_LENGTH (line 36) | const MAX_FILENAME_LENGTH = 100;
  function filenamify (line 41) | function filenamify(string, options = {}) {

FILE: server/utils/mentions.js
  constant MENTION_ID_REGEX (line 6) | const MENTION_ID_REGEX = /@\[.*?\]\((.*?)\)/g;
  constant MENTION_USERNAME_REGEX (line 7) | const MENTION_USERNAME_REGEX = /@\[(.*?)\]\(.*?\)/g;

FILE: server/utils/send_notifications.py
  class CaptureWarningHandler (line 23) | class CaptureWarningHandler(logging.Handler):
    method emit (line 24) | def emit(self, record):
  function send_notification (line 38) | def send_notification(url, title, body, body_format):

FILE: server/utils/validators.js
  constant MAX_STRING_ID (line 10) | const MAX_STRING_ID = '9223372036854775807';
  constant ID_REGEX (line 12) | const ID_REGEX = /^[1-9][0-9]*$/;
  constant IDS_WITH_COMMA_REGEX (line 13) | const IDS_WITH_COMMA_REGEX = /^[1-9][0-9]*(,[1-9][0-9]*)*$/;
  constant USERNAME_REGEX (line 14) | const USERNAME_REGEX = /^[a-zA-Z0-9]+((_|\.)?[a-zA-Z0-9])*$/;
Condensed preview — 1523 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (7,260K chars).
[
  {
    "path": ".dockerignore",
    "chars": 258,
    "preview": "**/.DS_Store\n\n*/node_modules\n\nserver/swagger.json\nserver/.env\n\nserver/dist\nserver/logs\nserver/test\nserver/.tmp\nserver/.v"
  },
  {
    "path": ".gitattributes",
    "chars": 54,
    "preview": "client/src/lib/custom-ui/styles.css linguist-vendored\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-bug-report.yml",
    "chars": 1811,
    "preview": "name: \"🐛 Bug Report\"\ndescription: Report a bug found while using PLANKA\ntitle: \"[Bug]: \"\nlabels: [\"Type: Bug\", \"Status: "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2-feature-request.yml",
    "chars": 931,
    "preview": "name: \"✨ Feature Request\"\ndescription: Suggest a feature or enhancement to improve PLANKA.\nlabels: [\"Type: Idea\"]\nbody:\n"
  },
  {
    "path": ".github/workflows/build-and-publish-release-package.yml",
    "chars": 1624,
    "preview": "name: Build and Publish Release Package\n\non:\n  release:\n    types: [created]\n\njobs:\n  build-and-publish-release-package:"
  },
  {
    "path": ".github/workflows/build-and-push-docker-image.yml",
    "chars": 2384,
    "preview": "name: Build and Push Docker Image\n\non:\n  release:\n    types: [created]\n\njobs:\n  build-and-push-docker-image:\n    runs-on"
  },
  {
    "path": ".github/workflows/build-and-push-docker-nightly-image.yml",
    "chars": 2653,
    "preview": "name: Build and Push Docker Nightly Image\n\non:\n  push:\n    paths-ignore:\n      - '.github/**'\n      - 'charts/**'\n      "
  },
  {
    "path": ".github/workflows/build-and-test.yml",
    "chars": 2325,
    "preview": "name: Build and Test\n\non:\n  pull_request:\n    branches:\n      - master\n  push:\n    branches:\n      - master\n\njobs:\n  bui"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 688,
    "preview": "name: Lint\n\non:\n  pull_request:\n    branches:\n      - master\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    steps:\n     "
  },
  {
    "path": ".github/workflows/release-helm-chart.yml",
    "chars": 1310,
    "preview": "name: Release Charts\n\non:\n  push:\n    paths:\n      - \"charts/**\"\n    branches:\n      - master\n\njobs:\n  release-helm-char"
  },
  {
    "path": ".gitignore",
    "chars": 349,
    "preview": "docker-compose.override.yml\n.idea\n.DS_Store\n\nnode_modules\n\n# Prevent another lockfile than package-lock.json (npm) from "
  },
  {
    "path": ".husky/pre-commit",
    "chars": 26,
    "preview": "#!/bin/sh\nnpx lint-staged\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 60,
    "preview": "{\n  \"recommendations\": [\n    \"dbaeumer.vscode-eslint\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 539,
    "preview": "{\n  \"editor.tabSize\": 2,\n  \"editor.formatOnSave\": true,\n  \"files.insertFinalNewline\": true,\n  \"files.trimFinalNewlines\":"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3381,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2280,
    "preview": "# Contributing to PLANKA\n\nFirst off, thanks for taking the time to contribute!\n\n## Code of Conduct\n\nThis project and eve"
  },
  {
    "path": "CONTRIBUTOR_LICENSE_AGREEMENT.md",
    "chars": 461,
    "preview": "# PLANKA Contributor License Agreement\n\nI give PLANKA Software GmbH permission to license my contributions on any terms "
  },
  {
    "path": "Dockerfile",
    "chars": 1289,
    "preview": "# Stage 1: Server build\nFROM node:22-alpine AS server\n\nRUN apk -U upgrade \\\n  && apk add build-base python3 --no-cache\n\n"
  },
  {
    "path": "Dockerfile.dev",
    "chars": 146,
    "preview": "FROM node:22-alpine\n\nRUN apk -U upgrade \\\n  && apk add bash build-base python3 xdg-utils --no-cache \\\n  && npm install n"
  },
  {
    "path": "LICENSE.md",
    "chars": 6920,
    "preview": "**PLANKA Community License**\n\nVersion 1.1 - Last updated: May 20, 2025\n\nRelated files in English:\n\n- PLANKA Community Li"
  },
  {
    "path": "LICENSES/PLANKA Commercial License DE.md",
    "chars": 3771,
    "preview": "**PLANKA Commercial License**\n\nVersion 1.2 - Zuletzt aktualisiert: 28. Nov 2025\n\nZugehörige Dateien in Englisch:\n\n- [PLA"
  },
  {
    "path": "LICENSES/PLANKA Commercial License EN.md",
    "chars": 3416,
    "preview": "**PLANKA Commercial License**\n\nVersion 1.2 - Last updated: Nov 28, 2025\n\nRelated files in English:\n\n- [PLANKA Community "
  },
  {
    "path": "LICENSES/PLANKA Community License DE.md",
    "chars": 8003,
    "preview": "**PLANKA Community License**\n\nVersion 1.1 - Zuletzt aktualisiert: 20. Mai 2025\n\nZugehörige Dateien in Englisch:\n\n- [PLAN"
  },
  {
    "path": "LICENSES/PLANKA Community License EN.md",
    "chars": 6920,
    "preview": "**PLANKA Community License**\n\nVersion 1.1 - Last updated: May 20, 2025\n\nRelated files in English:\n\n- PLANKA Community Li"
  },
  {
    "path": "LICENSES/PLANKA License Guide DE.md",
    "chars": 21342,
    "preview": "**PLANKA License Guide**\n\nVersion 1.1 - Zuletzt aktualisiert: 20. Mai 2025\n\nZugehörige Dateien in Englisch:\n\n- [PLANKA C"
  },
  {
    "path": "LICENSES/PLANKA License Guide EN.md",
    "chars": 18318,
    "preview": "**PLANKA License Guide**\n\nVersion 1.1 - Last updated: May 20, 2025\n\nRelated files in English:\n\n- [PLANKA Community Licen"
  },
  {
    "path": "README.md",
    "chars": 3969,
    "preview": "<div align=\"center\">\n\n  ![Logo](https://raw.githubusercontent.com/plankanban/planka/master/assets/logo.png)\n\n  # PLANKA\n"
  },
  {
    "path": "SECURITY.md",
    "chars": 404,
    "preview": "# Security Policy\n\n## Supported Versions\n\nMost recent release.\n\n## Reporting a Vulnerability\n\nPlease report any security"
  },
  {
    "path": "charts/planka/.helmignore",
    "chars": 349,
    "preview": "# Patterns to ignore when building packages.\n# This supports shell glob matching, relative path matching, and\n# negation"
  },
  {
    "path": "charts/planka/Chart.yaml",
    "chars": 1343,
    "preview": "apiVersion: v2\nname: planka\ndescription: A Helm chart to deploy PLANKA and it's dependencies.\n\n# A chart can be either a"
  },
  {
    "path": "charts/planka/README.md",
    "chars": 9906,
    "preview": "# PLANKA Helm Chart\n\nThis Helm Chart simplifies the deployment of [PLANKA](https://github.com/plankanban/planka) on Kube"
  },
  {
    "path": "charts/planka/templates/NOTES.txt",
    "chars": 1743,
    "preview": "1. Get the application URL by running these commands:\n{{- if .Values.ingress.enabled }}\n{{- range $host := .Values.ingre"
  },
  {
    "path": "charts/planka/templates/_helpers.tpl",
    "chars": 1772,
    "preview": "{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"planka.name\" -}}\n{{- default .Chart.Name .Values.nameOverride | trun"
  },
  {
    "path": "charts/planka/templates/configmap-terms.yaml",
    "chars": 313,
    "preview": "{{- if .Values.terms.enabled }}\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {{ include \"planka.fullname\" . }}-terms"
  },
  {
    "path": "charts/planka/templates/deployment.yaml",
    "chars": 9547,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: {{ include \"planka.fullname\" . }}\n  labels:\n    {{- include \"plan"
  },
  {
    "path": "charts/planka/templates/hpa.yaml",
    "chars": 988,
    "preview": "{{- if .Values.autoscaling.enabled }}\napiVersion: autoscaling/v2\nkind: HorizontalPodAutoscaler\nmetadata:\n  name: {{ incl"
  },
  {
    "path": "charts/planka/templates/ingress.yaml",
    "chars": 2163,
    "preview": "{{- if .Values.ingress.enabled -}}\n{{- $fullName := include \"planka.fullname\" . -}}\n{{- $svcPort := .Values.service.port"
  },
  {
    "path": "charts/planka/templates/pvc.yaml",
    "chars": 777,
    "preview": "{{- if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }}\n---\nkind: PersistentVolumeClaim\napiVer"
  },
  {
    "path": "charts/planka/templates/secret-oidc.yaml",
    "chars": 729,
    "preview": "{{- if .Values.oidc.enabled }}\n{{- if eq (and (not (empty .Values.oidc.clientId)) (not (empty .Values.oidc.clientSecret)"
  },
  {
    "path": "charts/planka/templates/service.yaml",
    "chars": 460,
    "preview": "apiVersion: v1\nkind: Service\nmetadata:\n  name: {{ include \"planka.fullname\" . }}\n  labels:\n    {{- include \"planka.label"
  },
  {
    "path": "charts/planka/templates/serviceaccount.yaml",
    "chars": 318,
    "preview": "{{- if .Values.serviceAccount.create -}}\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: {{ include \"planka.servic"
  },
  {
    "path": "charts/planka/templates/tests/test-connection.yaml",
    "chars": 376,
    "preview": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: \"{{ include \"planka.fullname\" . }}-test-connection\"\n  labels:\n    {{- include"
  },
  {
    "path": "charts/planka/values.yaml",
    "chars": 9079,
    "preview": "# Default values for planka.\n# This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n\nrep"
  },
  {
    "path": "client/.gitignore",
    "chars": 253,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndis"
  },
  {
    "path": "client/index.html",
    "chars": 702,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-w"
  },
  {
    "path": "client/package.json",
    "chars": 5241,
    "preview": "{\n  \"name\": \"planka-client\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"build\": \"vite build\",\n    \"posti"
  },
  {
    "path": "client/patches/@diplodoc+transform+4.70.2.patch",
    "chars": 774,
    "preview": "diff --git a/node_modules/@diplodoc/transform/lib/md.js b/node_modules/@diplodoc/transform/lib/md.js\nindex c9faa96..e4be"
  },
  {
    "path": "client/patches/@gravity-ui+markdown-editor+15.35.1.patch",
    "chars": 5954,
    "preview": "diff --git a/node_modules/@gravity-ui/markdown-editor/build/esm/bundle/wysiwyg-preset.js b/node_modules/@gravity-ui/mark"
  },
  {
    "path": "client/patches/react-mentions+4.4.10.patch",
    "chars": 1286,
    "preview": "diff --git a/node_modules/react-mentions/dist/react-mentions.esm.js b/node_modules/react-mentions/dist/react-mentions.es"
  },
  {
    "path": "client/patches/redux-orm+0.16.2.patch",
    "chars": 1972734,
    "preview": "diff --git a/node_modules/redux-orm/dist/redux-orm.js b/node_modules/redux-orm/dist/redux-orm.js\nindex 9298fea..d53d03e "
  },
  {
    "path": "client/patches/sails.io.js+1.2.1.patch",
    "chars": 2712,
    "preview": "diff --git a/node_modules/sails.io.js/sails.io.js b/node_modules/sails.io.js/sails.io.js\nindex 11694b5..bb5e594 100644\n-"
  },
  {
    "path": "client/patches/semantic-ui-react+2.1.5.patch",
    "chars": 6388,
    "preview": "diff --git a/node_modules/semantic-ui-react/dist/es/lib/doesNodeContainClick.js b/node_modules/semantic-ui-react/dist/es"
  },
  {
    "path": "client/public/manifest.json",
    "chars": 446,
    "preview": "{\n  \"name\": \"PLANKA\",\n  \"icons\": [\n    {\n      \"src\": \"favicon.ico\",\n      \"sizes\": \"64x64 32x32 24x24 16x16\",\n      \"ty"
  },
  {
    "path": "client/public/robots.txt",
    "chars": 67,
    "preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\nDisallow:\n"
  },
  {
    "path": "client/src/actions/activities.js",
    "chars": 1367,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/attachments.js",
    "chars": 1954,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/background-images.js",
    "chars": 1541,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/base-custom-field-groups.js",
    "chars": 2430,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/board-memberships.js",
    "chars": 3037,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/boards.js",
    "chars": 2904,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/bootstrap.js",
    "chars": 381,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/cards.js",
    "chars": 5164,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/comments.js",
    "chars": 2263,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/config.js",
    "chars": 1062,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/core.js",
    "chars": 1814,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/custom-field-groups.js",
    "chars": 2242,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/custom-field-values.js",
    "chars": 1579,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/custom-fields.js",
    "chars": 2010,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/index.js",
    "chars": 1739,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/labels.js",
    "chars": 3872,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/lists.js",
    "chars": 3388,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/login.js",
    "chars": 2485,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/modals.js",
    "chars": 454,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/notification-services.js",
    "chars": 2826,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/notifications.js",
    "chars": 1417,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/project-managers.js",
    "chars": 2322,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/projects.js",
    "chars": 3275,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/router.js",
    "chars": 1250,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/socket.js",
    "chars": 1459,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/task-lists.js",
    "chars": 1878,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/tasks.js",
    "chars": 1690,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/users.js",
    "chars": 7490,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/actions/webhooks.js",
    "chars": 1822,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/access-tokens.js",
    "chars": 1032,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/activities.js",
    "chars": 1002,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/attachments.js",
    "chars": 1766,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/background-images.js",
    "chars": 636,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/base-custom-field-groups.js",
    "chars": 706,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/board-memberships.js",
    "chars": 649,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/boards.js",
    "chars": 1238,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/bootstrap.js",
    "chars": 310,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/card-labels.js",
    "chars": 514,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/card-memberships.js",
    "chars": 541,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/cards.js",
    "chars": 3664,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/comments.js",
    "chars": 1512,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/config.js",
    "chars": 513,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/custom-field-groups.js",
    "chars": 965,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/custom-field-values.js",
    "chars": 779,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/custom-fields.js",
    "chars": 874,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/http.js",
    "chars": 962,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/index.js",
    "chars": 1845,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/labels.js",
    "chars": 552,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/lists.js",
    "chars": 2074,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/notification-services.js",
    "chars": 1015,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/notifications.js",
    "chars": 1772,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/project-managers.js",
    "chars": 511,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/projects.js",
    "chars": 737,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/socket.js",
    "chars": 1131,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/task-lists.js",
    "chars": 685,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/tasks.js",
    "chars": 553,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/terms.js",
    "chars": 352,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/users.js",
    "chars": 1577,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/api/webhooks.js",
    "chars": 635,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/assets/docs/whats-new.md",
    "chars": 5397,
    "preview": "# [2.1.0] - 2026-03-19\n\n### Added\n\n* Support running under subpath\n* Add ability to display card ages\n* Allow exposing S"
  },
  {
    "path": "client/src/components/activities/BoardActivitiesModal/BoardActivitiesModal.jsx",
    "chars": 2274,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/BoardActivitiesModal/BoardActivitiesModal.module.scss",
    "chars": 297,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/BoardActivitiesModal/Item.jsx",
    "chars": 6443,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/BoardActivitiesModal/Item.module.scss",
    "chars": 594,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/BoardActivitiesModal/index.js",
    "chars": 249,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/CardActivities/CardActivities.jsx",
    "chars": 1630,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/CardActivities/CardActivities.module.scss",
    "chars": 321,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/CardActivities/Item.jsx",
    "chars": 5311,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/CardActivities/Item.module.scss",
    "chars": 594,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/activities/CardActivities/index.js",
    "chars": 231,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/AddAttachmentStep/AddAttachmentStep.jsx",
    "chars": 1938,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/AddAttachmentStep/AddAttachmentStep.module.scss",
    "chars": 491,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/AddAttachmentStep/index.js",
    "chars": 240,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/Attachments.jsx",
    "chars": 3209,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/Attachments.module.scss",
    "chars": 492,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/ContentViewer.jsx",
    "chars": 2750,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/ContentViewer.module.scss",
    "chars": 224,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/CsvViewer.jsx",
    "chars": 3225,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/CsvViewer.module.scss",
    "chars": 289,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/EditStep.jsx",
    "chars": 3445,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/EditStep.module.scss",
    "chars": 427,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/Item.jsx",
    "chars": 4374,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/Item.module.scss",
    "chars": 695,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/ItemContent.jsx",
    "chars": 5872,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/ItemContent.module.scss",
    "chars": 2126,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/PdfViewer.jsx",
    "chars": 688,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/PdfViewer.module.scss",
    "chars": 202,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/attachments/Attachments/index.js",
    "chars": 222,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/AddBaseCustomFieldGroupStep/AddBaseCustomFieldGroupStep.jsx",
    "chars": 2124,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/AddBaseCustomFieldGroupStep/AddBaseCustomFieldGroupStep.module.scss",
    "chars": 313,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/AddBaseCustomFieldGroupStep/index.js",
    "chars": 270,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupChip/BaseCustomFieldGroupChip.jsx",
    "chars": 1629,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupChip/BaseCustomFieldGroupChip.module.scss",
    "chars": 996,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupChip/index.js",
    "chars": 261,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/BaseCustomFieldGroupStep.jsx",
    "chars": 7171,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/BaseCustomFieldGroupStep.module.scss",
    "chars": 1043,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomField.jsx",
    "chars": 2065,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomField.module.scss",
    "chars": 857,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldAddStep.jsx",
    "chars": 2135,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldAddStep.module.scss",
    "chars": 214,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldEditStep.jsx",
    "chars": 3358,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldEditStep.module.scss",
    "chars": 328,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldEditor.jsx",
    "chars": 1784,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/CustomFieldEditor.module.scss",
    "chars": 396,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/EditStep.jsx",
    "chars": 2763,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/EditStep.module.scss",
    "chars": 313,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/base-custom-field-groups/BaseCustomFieldGroupStep/index.js",
    "chars": 261,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/ActionsStep.jsx",
    "chars": 5697,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/ActionsStep.module.scss",
    "chars": 1216,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/AddStep/AddStep.jsx",
    "chars": 3623,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/AddStep/AddStep.module.scss",
    "chars": 676,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/AddStep/User.jsx",
    "chars": 1248,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/AddStep/User.module.scss",
    "chars": 1017,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/AddStep/index.js",
    "chars": 210,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/BoardMemberships.jsx",
    "chars": 2058,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/BoardMemberships.module.scss",
    "chars": 574,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/Group.jsx",
    "chars": 1806,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/Group.module.scss",
    "chars": 851,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/GroupItemsStep.jsx",
    "chars": 1330,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/SelectPermissionsStep.jsx",
    "chars": 3990,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/SelectPermissionsStep.module.scss",
    "chars": 598,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMemberships/index.js",
    "chars": 237,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/BoardMembershipsStep.jsx",
    "chars": 1483,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/PureBoardMembershipsStep/Item.jsx",
    "chars": 1940,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/PureBoardMembershipsStep/Item.module.scss",
    "chars": 859,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/PureBoardMembershipsStep/PureBoardMembershipsStep.jsx",
    "chars": 3387,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/PureBoardMembershipsStep/PureBoardMembershipsStep.module.scss",
    "chars": 997,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/board-memberships/PureBoardMembershipsStep/index.js",
    "chars": 261,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/AddBoardStep/AddBoardStep.jsx",
    "chars": 3513,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/AddBoardStep/AddBoardStep.module.scss",
    "chars": 919,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/AddBoardStep/ImportStep.jsx",
    "chars": 1254,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/AddBoardStep/ImportStep.module.scss",
    "chars": 454,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/AddBoardStep/index.js",
    "chars": 225,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/Board.jsx",
    "chars": 1703,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/EndlessContent.jsx",
    "chars": 1676,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/FiniteContent.jsx",
    "chars": 1474,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/GridView.jsx",
    "chars": 4531,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/GridView.module.scss",
    "chars": 1353,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/KanbanContent/AddList.jsx",
    "chars": 4476,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/KanbanContent/AddList.module.scss",
    "chars": 1117,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/KanbanContent/KanbanContent.jsx",
    "chars": 6342,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/KanbanContent/KanbanContent.module.scss",
    "chars": 1079,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/KanbanContent/index.js",
    "chars": 228,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/ListView.jsx",
    "chars": 4273,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/ListView.module.scss",
    "chars": 1551,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  },
  {
    "path": "client/src/components/boards/Board/ShortcutsProvider.jsx",
    "chars": 11258,
    "preview": "/*!\n * Copyright (c) 2024 PLANKA Software GmbH\n * Licensed under the Fair Use License: https://github.com/plankanban/pla"
  }
]

// ... and 1323 more files (download for full content)

About this extraction

This page contains the full source code of the plankanban/planka GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1523 files (6.6 MB), approximately 1.8M tokens, and a symbol index with 683 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!