Repository: OneArmyWorld/onearmy Branch: master Commit: ecddc5150337 Files: 1080 Total size: 2.8 MB Directory structure: gitextract_7op3tyix/ ├── .all-contributorsrc ├── .circleci/ │ └── config.yml ├── .dockerignore ├── .github/ │ ├── CODEOWNERS │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ ├── build-new-component.md │ │ └── feature-request-or-suggestion.md │ ├── actions/ │ │ └── destroy-fly-preview-app/ │ │ ├── Dockerfile │ │ ├── action.yml │ │ └── entrypoint.sh │ ├── labels.yml │ ├── pull_request_template.md │ └── workflows/ │ ├── codeql-analysis.yml │ ├── pr-preview-fly-deploy.yml │ ├── pr-preview-fly-destroy.yml │ ├── pr-preview-remove-label.yml │ ├── pr-stale.yml │ └── storybook-deploy.yml ├── .gitignore ├── .husky/ │ └── pre-commit ├── .node-version ├── .releaserc.json ├── .snaplet/ │ ├── config.json │ ├── dataModel.json │ ├── library.json │ └── questions.json ├── .vscode/ │ └── launch.json ├── .yarnrc.yml ├── CNAME ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.preview ├── FUNDING.yml ├── LICENSE ├── README.md ├── SECURITY.md ├── biome.json ├── codecov.yml ├── docs/ │ ├── circle-ci.md │ ├── db-seeding.md │ ├── maintainers.md │ ├── pwa-setup.md │ ├── react-router-7.md │ ├── supabase.md │ ├── team-principles.md │ └── technical-decisions.md ├── fly-ff.toml ├── fly-pk.toml ├── fly-pp.toml ├── fly-preview.toml ├── index.html ├── package.json ├── packages/ │ ├── components/ │ │ ├── .gitignore │ │ ├── .storybook/ │ │ │ ├── main.ts │ │ │ ├── manager.js │ │ │ └── preview.tsx │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── Accordion/ │ │ │ │ ├── Accordion.stories.tsx │ │ │ │ ├── Accordion.test.tsx │ │ │ │ └── Accordion.tsx │ │ │ ├── ActionSet/ │ │ │ │ └── ActionSet.tsx │ │ │ ├── Alert/ │ │ │ │ └── Alert.stories.tsx │ │ │ ├── ArrowIcon/ │ │ │ │ ├── ArrowIcon.stories.tsx │ │ │ │ ├── ArrowIcon.tsx │ │ │ │ └── styles.css │ │ │ ├── ArticleCallToActionSupabase/ │ │ │ │ ├── ArticleCallToActionSupabase.stories.tsx │ │ │ │ └── ArticleCallToActionSupabase.tsx │ │ │ ├── AuthorDisplay/ │ │ │ │ └── AuthorDisplay.tsx │ │ │ ├── Banner/ │ │ │ │ ├── Banner.stories.tsx │ │ │ │ ├── Banner.test.tsx │ │ │ │ └── Banner.tsx │ │ │ ├── BlockedRoute/ │ │ │ │ ├── BlockedRoute.stories.tsx │ │ │ │ └── BlockedRoute.tsx │ │ │ ├── Breadcrumbs/ │ │ │ │ ├── Breadcrumbs.stories.tsx │ │ │ │ ├── Breadcrumbs.test.tsx │ │ │ │ ├── Breadcrumbs.tsx │ │ │ │ └── BreadcrumbsItem.tsx │ │ │ ├── Button/ │ │ │ │ ├── Button.stories.tsx │ │ │ │ └── Button.tsx │ │ │ ├── ButtonIcon/ │ │ │ │ ├── ButtonIcon.stories.tsx │ │ │ │ └── ButtonIcon.tsx │ │ │ ├── ButtonShowReplies/ │ │ │ │ ├── ButtonShowReplies.stories.tsx │ │ │ │ ├── ButtonShowReplies.test.tsx │ │ │ │ └── ButtonShowReplies.tsx │ │ │ ├── CardButton/ │ │ │ │ ├── CardButton.stories.tsx │ │ │ │ └── CardButton.tsx │ │ │ ├── CardListItem/ │ │ │ │ ├── CardListItem.stories.tsx │ │ │ │ └── CardListItem.tsx │ │ │ ├── CardProfile/ │ │ │ │ ├── CardDetailsMemberProfile.tsx │ │ │ │ ├── CardDetailsSpaceProfile.tsx │ │ │ │ ├── CardProfile.stories.tsx │ │ │ │ ├── CardProfile.test.tsx │ │ │ │ └── CardProfile.tsx │ │ │ ├── Category/ │ │ │ │ ├── Category.stories.tsx │ │ │ │ └── Category.tsx │ │ │ ├── CategoryHorizonalList/ │ │ │ │ ├── CategoryHorizonalList.stories.tsx │ │ │ │ ├── CategoryHorizonalList.test.tsx │ │ │ │ └── CategoryHorizonalList.tsx │ │ │ ├── CharacterCount/ │ │ │ │ ├── CharacterCount.stories.tsx │ │ │ │ └── CharacterCount.tsx │ │ │ ├── CommentAvatar/ │ │ │ │ └── CommentAvatar.tsx │ │ │ ├── CommentBody/ │ │ │ │ └── CommentBody.tsx │ │ │ ├── CommentDisplay/ │ │ │ │ ├── CommentDisplay.stories.tsx │ │ │ │ ├── CommentDisplay.test.tsx │ │ │ │ └── CommentDisplay.tsx │ │ │ ├── CommentsTitle/ │ │ │ │ ├── CommentsTitle.stories.tsx │ │ │ │ ├── CommentsTitle.test.tsx │ │ │ │ └── CommentsTitle.tsx │ │ │ ├── ConfirmModal/ │ │ │ │ ├── ConfirmModal.stories.tsx │ │ │ │ └── ConfirmModal.tsx │ │ │ ├── ContentStatistics/ │ │ │ │ ├── ContentStatistics.stories.tsx │ │ │ │ ├── ContentStatistics.tsx │ │ │ │ ├── ContentStatisticsList.tsx │ │ │ │ └── types.ts │ │ │ ├── CreateComment/ │ │ │ │ ├── CreateComment.css │ │ │ │ ├── CreateComment.stories.tsx │ │ │ │ ├── CreateComment.test.tsx │ │ │ │ └── CreateComment.tsx │ │ │ ├── CreateReply/ │ │ │ │ ├── CreateReply.stories.tsx │ │ │ │ ├── CreateReply.test.tsx │ │ │ │ └── CreateReply.tsx │ │ │ ├── DisplayDate/ │ │ │ │ ├── DisplayDate.stories.tsx │ │ │ │ ├── DisplayDate.test.tsx │ │ │ │ ├── DisplayDate.tsx │ │ │ │ └── display-date.css │ │ │ ├── DonationRequestModal/ │ │ │ │ ├── DonationRequestModal.stories.tsx │ │ │ │ ├── DonationRequestModal.test.tsx │ │ │ │ └── DonationRequestModal.tsx │ │ │ ├── DownloadButton/ │ │ │ │ ├── DownloadButton.stories.tsx │ │ │ │ ├── DownloadButton.test.tsx │ │ │ │ └── DownloadButton.tsx │ │ │ ├── DownloadCounter/ │ │ │ │ ├── DownloadCounter.stories.tsx │ │ │ │ ├── DownloadCounter.test.tsx │ │ │ │ └── DownloadCounter.tsx │ │ │ ├── DownloadStaticFile/ │ │ │ │ ├── DownloadStaticFile.stories.tsx │ │ │ │ └── DownloadStaticFile.tsx │ │ │ ├── EditComment/ │ │ │ │ ├── EditComment.stories.tsx │ │ │ │ ├── EditComment.test.tsx │ │ │ │ └── EditComment.tsx │ │ │ ├── ElWithBeforeIcon/ │ │ │ │ ├── ElWithBeforeIcon.stories.tsx │ │ │ │ └── ElWithBeforeIcon.tsx │ │ │ ├── ExternalLink/ │ │ │ │ ├── ExternalLink.stories.tsx │ │ │ │ └── ExternalLink.tsx │ │ │ ├── FieldCheckbox/ │ │ │ │ └── FieldCheckbox.tsx │ │ │ ├── FieldInput/ │ │ │ │ ├── FieldInput.stories.tsx │ │ │ │ └── FieldInput.tsx │ │ │ ├── FieldMarkdown/ │ │ │ │ ├── AddImage.tsx │ │ │ │ ├── FieldMarkdown.stories.tsx │ │ │ │ ├── FieldMarkdown.tsx │ │ │ │ └── style.css │ │ │ ├── FieldTextarea/ │ │ │ │ ├── FieldTextarea.stories.tsx │ │ │ │ └── FieldTextarea.tsx │ │ │ ├── FlagIcon/ │ │ │ │ └── FlagIcon.tsx │ │ │ ├── FollowButton/ │ │ │ │ ├── FollowButton.stories.tsx │ │ │ │ └── FollowButton.tsx │ │ │ ├── FollowIcon/ │ │ │ │ ├── FollowIcon.stories.tsx │ │ │ │ └── FollowIcon.tsx │ │ │ ├── GlobalStyles/ │ │ │ │ └── GlobalStyles.tsx │ │ │ ├── GridForm/ │ │ │ │ ├── GridForm.stories.tsx │ │ │ │ └── GridForm.tsx │ │ │ ├── Guidelines/ │ │ │ │ ├── Guidelines.stories.tsx │ │ │ │ ├── Guidelines.test.tsx │ │ │ │ └── Guidelines.tsx │ │ │ ├── Heading/ │ │ │ │ └── Heading.stories.tsx │ │ │ ├── HeroBanner/ │ │ │ │ ├── HeroBanner.stories.tsx │ │ │ │ └── HeroBanner.tsx │ │ │ ├── Icon/ │ │ │ │ ├── DonateIcon.tsx │ │ │ │ ├── DownloadIcon.tsx │ │ │ │ ├── ExternalUrl.tsx │ │ │ │ ├── Icon.stories.tsx │ │ │ │ ├── Icon.tsx │ │ │ │ ├── svgs.tsx │ │ │ │ └── types.ts │ │ │ ├── IconCountWithTooltip/ │ │ │ │ ├── IconCountWithTooltip.stories.tsx │ │ │ │ ├── IconCountWithTooltip.test.tsx │ │ │ │ └── IconCountWithTooltip.tsx │ │ │ ├── ImageGallery/ │ │ │ │ ├── ImageGallery.stories.tsx │ │ │ │ ├── ImageGallery.test.tsx │ │ │ │ └── ImageGallery.tsx │ │ │ ├── ImageGalleryThumbnail/ │ │ │ │ ├── ImageGalleryThumbnail.stories.tsx │ │ │ │ ├── ImageGalleryThumbnail.test.tsx │ │ │ │ └── ImageGalleryThumbnail.tsx │ │ │ ├── ImageInput/ │ │ │ │ ├── ImageInputDeleteOverlay.tsx │ │ │ │ ├── ImageInputV2.tsx │ │ │ │ ├── ImageInputWrapper.tsx │ │ │ │ └── isImageValid.ts │ │ │ ├── InformationTooltip/ │ │ │ │ ├── InformationTooltip.stories.tsx │ │ │ │ └── InformationTooltip.tsx │ │ │ ├── Input/ │ │ │ │ └── Input.stories.tsx │ │ │ ├── InternalLink/ │ │ │ │ ├── InternalLink.stories.tsx │ │ │ │ └── InternalLink.tsx │ │ │ ├── LinkifyText/ │ │ │ │ ├── LinkifyText.stories.tsx │ │ │ │ └── LinkifyText.tsx │ │ │ ├── Loader/ │ │ │ │ ├── Loader.stories.tsx │ │ │ │ └── Loader.tsx │ │ │ ├── Map/ │ │ │ │ ├── Map.client.tsx │ │ │ │ ├── Map.stories.tsx │ │ │ │ └── index.css │ │ │ ├── MapCardList/ │ │ │ │ ├── MapCardList.stories.tsx │ │ │ │ ├── MapCardList.test.tsx │ │ │ │ └── MapCardList.tsx │ │ │ ├── MapFilterListItem/ │ │ │ │ └── MapFilterListItem.tsx │ │ │ ├── MapWithPin/ │ │ │ │ ├── MapPin.client.tsx │ │ │ │ ├── MapPin.stories.tsx │ │ │ │ ├── MapWithPin.client.tsx │ │ │ │ └── MapWithPin.stories.tsx │ │ │ ├── MemberBadge/ │ │ │ │ ├── MemberBadge.stories.tsx │ │ │ │ └── MemberBadge.tsx │ │ │ ├── MemberHistory/ │ │ │ │ ├── MemberHistory.test.tsx │ │ │ │ └── MemberHistory.tsx │ │ │ ├── Modal/ │ │ │ │ ├── Modal.stories.tsx │ │ │ │ └── Modal.tsx │ │ │ ├── ModerationStatus/ │ │ │ │ ├── ModerationStatus.stories.tsx │ │ │ │ └── ModerationStatus.tsx │ │ │ ├── MoreContainer/ │ │ │ │ ├── MoreContainer.stories.tsx │ │ │ │ └── MoreContainer.tsx │ │ │ ├── NotificationItemSupabase/ │ │ │ │ ├── NotificationItemSupabase.stories.tsx │ │ │ │ └── NotificationItemSupabase.tsx │ │ │ ├── NotificationListSupabase/ │ │ │ │ ├── NotificationListSupabase.stories.tsx │ │ │ │ ├── NotificationListSupabase.test.tsx │ │ │ │ └── NotificationListSupabase.tsx │ │ │ ├── NotificationsModal/ │ │ │ │ └── NotificationsModal.tsx │ │ │ ├── OsmGeocoding/ │ │ │ │ ├── OsmGeocoding.stories.tsx │ │ │ │ ├── OsmGeocoding.tsx │ │ │ │ ├── OsmGeocodingLoader.tsx │ │ │ │ ├── OsmGeocodingResultsList.tsx │ │ │ │ └── types.tsx │ │ │ ├── Pagination/ │ │ │ │ ├── Pagination.test.tsx │ │ │ │ └── Pagination.tsx │ │ │ ├── PaginationIcons/ │ │ │ │ ├── PaginationIcons.stories.tsx │ │ │ │ ├── PaginationIcons.test.tsx │ │ │ │ └── PaginationIcons.tsx │ │ │ ├── PinProfile/ │ │ │ │ ├── PinProfile.stories.tsx │ │ │ │ └── PinProfile.tsx │ │ │ ├── ProfileBadgeContentLabel/ │ │ │ │ ├── ProfileBadgeContentLabel.stories.tsx │ │ │ │ └── ProfileBadgeContentLabel.tsx │ │ │ ├── ProfileLink/ │ │ │ │ ├── ProfileLink.stories.tsx │ │ │ │ └── ProfileLink.tsx │ │ │ ├── ProfileList/ │ │ │ │ ├── ProfileList.stories.tsx │ │ │ │ ├── ProfileList.test.tsx │ │ │ │ └── ProfileList.tsx │ │ │ ├── ProfileTagsList/ │ │ │ │ ├── ProfileTagsList.stories.tsx │ │ │ │ ├── ProfileTagsList.test.tsx │ │ │ │ └── ProfileTagsList.tsx │ │ │ ├── ResearchEditorOverview/ │ │ │ │ ├── ResearchEditorOverview.stories.tsx │ │ │ │ ├── ResearchEditorOverview.test.tsx │ │ │ │ ├── ResearchEditorOverview.tsx │ │ │ │ └── __snapshots__/ │ │ │ │ └── ResearchEditorOverview.test.tsx.snap │ │ │ ├── ReturnPathLink/ │ │ │ │ └── ReturnPathLink.tsx │ │ │ ├── SearchField/ │ │ │ │ ├── SearchField.stories.tsx │ │ │ │ └── SearchField.tsx │ │ │ ├── Select/ │ │ │ │ ├── DropdownIndicator.tsx │ │ │ │ ├── Option.tsx │ │ │ │ ├── Select.stories.tsx │ │ │ │ └── Select.tsx │ │ │ ├── SiteFooter/ │ │ │ │ ├── SiteFooter.stories.tsx │ │ │ │ └── SiteFooter.tsx │ │ │ ├── TabbedContent/ │ │ │ │ ├── TabbedContent.stories.tsx │ │ │ │ ├── TabbedContent.test.tsx │ │ │ │ └── TabbedContent.tsx │ │ │ ├── Tag/ │ │ │ │ ├── Tag.stories.tsx │ │ │ │ └── Tag.tsx │ │ │ ├── TagList/ │ │ │ │ ├── TagList.stories.tsx │ │ │ │ ├── TagList.test.tsx │ │ │ │ └── TagList.tsx │ │ │ ├── Text/ │ │ │ │ └── Text.stories.tsx │ │ │ ├── TextNotification/ │ │ │ │ ├── TextNotification.stories.tsx │ │ │ │ └── TextNotification.tsx │ │ │ ├── Textarea/ │ │ │ │ └── Textarea.stories.tsx │ │ │ ├── Tooltip/ │ │ │ │ ├── Tooltip.stories.tsx │ │ │ │ └── Tooltip.tsx │ │ │ ├── UsefulStatsButton/ │ │ │ │ ├── UsefulButtonLite.test.tsx │ │ │ │ ├── UsefulButtonLite.tsx │ │ │ │ ├── UsefulStatsButton.stories.tsx │ │ │ │ └── UsefulStatsButton.tsx │ │ │ ├── UserEngagementWrapper/ │ │ │ │ ├── UserEngagementWrapper.stories.tsx │ │ │ │ ├── UserEngagementWrapper.test.tsx │ │ │ │ └── UserEngagementWrapper.tsx │ │ │ ├── UserStatistics/ │ │ │ │ ├── UserStatistics.stories.tsx │ │ │ │ ├── UserStatistics.test.tsx │ │ │ │ └── UserStatistics.tsx │ │ │ ├── Username/ │ │ │ │ ├── DisplayName.stories.tsx │ │ │ │ ├── DisplayName.tsx │ │ │ │ ├── UserBadge.tsx │ │ │ │ ├── Username.stories.tsx │ │ │ │ ├── Username.test.tsx │ │ │ │ └── Username.tsx │ │ │ ├── VerticalList/ │ │ │ │ ├── VerticalList.client.tsx │ │ │ │ └── VerticalList.stories.tsx │ │ │ ├── VideoPlayer/ │ │ │ │ ├── VideoPlayer.stories.tsx │ │ │ │ ├── VideoPlayer.test.tsx │ │ │ │ └── VideoPlayer.tsx │ │ │ ├── VisitorModal/ │ │ │ │ ├── VisitorModal.tsx │ │ │ │ ├── VisitorModalFooter.test.tsx │ │ │ │ ├── VisitorModalFooter.tsx │ │ │ │ ├── VisitorModalHeader.test.tsx │ │ │ │ ├── VisitorModalHeader.tsx │ │ │ │ └── props.ts │ │ │ ├── __mocks__/ │ │ │ │ └── AuthWrapper.mock.tsx │ │ │ ├── hooks/ │ │ │ │ ├── useImageLightbox.tsx │ │ │ │ └── usePhotoSwipeLightbox.ts │ │ │ ├── index.ts │ │ │ ├── providers/ │ │ │ │ └── AuthorsContext.ts │ │ │ ├── test/ │ │ │ │ ├── setup.ts │ │ │ │ └── utils.tsx │ │ │ ├── types/ │ │ │ │ └── common.ts │ │ │ └── utils.ts │ │ ├── tsconfig.json │ │ ├── types/ │ │ │ ├── emotion.d.ts │ │ │ ├── images.d.ts │ │ │ └── photoswipe.d.ts │ │ └── vite.config.ts │ ├── cypress/ │ │ ├── .gitignore │ │ ├── .npmrc │ │ ├── README.md │ │ ├── cypress.config.ts │ │ ├── package.json │ │ ├── scripts/ │ │ │ └── start.mts │ │ ├── src/ │ │ │ ├── data/ │ │ │ │ └── index.ts │ │ │ ├── fixtures/ │ │ │ │ ├── images/ │ │ │ │ │ └── file.random │ │ │ │ └── searchResults.ts │ │ │ ├── integration/ │ │ │ │ ├── SignIn.spec.ts │ │ │ │ ├── SignUp.spec.ts │ │ │ │ ├── academy.spec.ts │ │ │ │ ├── common.spec.ts │ │ │ │ ├── library/ │ │ │ │ │ ├── discussions.spec.ts │ │ │ │ │ ├── read.spec.ts │ │ │ │ │ ├── seo.spec.ts │ │ │ │ │ └── write.spec.ts │ │ │ │ ├── news/ │ │ │ │ │ ├── discussions.spec.ts │ │ │ │ │ ├── read.spec.ts │ │ │ │ │ ├── search.spec.ts │ │ │ │ │ └── write.spec.ts │ │ │ │ ├── notifications.spec.ts │ │ │ │ ├── profile.spec.ts │ │ │ │ ├── profileList.spec.ts │ │ │ │ ├── questions/ │ │ │ │ │ ├── discussions.spec.ts │ │ │ │ │ ├── read.spec.ts │ │ │ │ │ ├── search.spec.ts │ │ │ │ │ └── write.spec.ts │ │ │ │ ├── research/ │ │ │ │ │ ├── discussions.spec.ts │ │ │ │ │ ├── follow.spec.ts │ │ │ │ │ ├── list.spec.ts │ │ │ │ │ ├── read.spec.ts │ │ │ │ │ └── write.spec.ts │ │ │ │ └── settings.spec.ts │ │ │ ├── support/ │ │ │ │ ├── commands.ts │ │ │ │ ├── commandsUi.ts │ │ │ │ ├── hooks.ts │ │ │ │ ├── index.ts │ │ │ │ └── rules.ts │ │ │ └── utils/ │ │ │ ├── TestUtils.ts │ │ │ └── supabaseTestsService.ts │ │ └── tsconfig.json │ └── themes/ │ ├── .gitignore │ ├── assets/ │ │ └── fonts/ │ │ └── README.md │ ├── package.json │ ├── src/ │ │ ├── common/ │ │ │ ├── button.ts │ │ │ ├── commonStyles.ts │ │ │ └── index.ts │ │ ├── fonts/ │ │ │ ├── fonts.d.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ └── types/ │ │ └── index.ts │ └── tsconfig.json ├── react-router.config.ts ├── seed/ │ ├── profilesSeed.ts │ └── usersSeed.ts ├── seed.config.ts ├── seed.sql ├── seed.ts ├── server.js ├── shared/ │ ├── .gitignore │ ├── README.md │ ├── index.ts │ ├── messages.ts │ ├── mocks/ │ │ ├── auth/ │ │ │ ├── index.ts │ │ │ └── users.ts │ │ ├── data/ │ │ │ ├── badges.ts │ │ │ ├── categories.ts │ │ │ ├── discussions.ts │ │ │ ├── index.ts │ │ │ ├── mappins.ts │ │ │ ├── messages.ts │ │ │ ├── news.ts │ │ │ ├── profileTags.ts │ │ │ ├── profileTypes.ts │ │ │ ├── projects.ts │ │ │ ├── questions.ts │ │ │ ├── research.ts │ │ │ ├── researchUpdates.ts │ │ │ ├── tags.ts │ │ │ └── users.ts │ │ └── index.ts │ ├── models/ │ │ ├── author.ts │ │ ├── banner.ts │ │ ├── category.ts │ │ ├── comment.ts │ │ ├── common.ts │ │ ├── content.ts │ │ ├── db.ts │ │ ├── document.ts │ │ ├── filesForm.ts │ │ ├── index.ts │ │ ├── library.ts │ │ ├── maps.ts │ │ ├── media.ts │ │ ├── messages.ts │ │ ├── moderation.ts │ │ ├── news.ts │ │ ├── notifications.ts │ │ ├── notificationsPreferences.ts │ │ ├── patreon.ts │ │ ├── patreonSettings.ts │ │ ├── profile.ts │ │ ├── profileBadge.ts │ │ ├── profileTag.ts │ │ ├── profileType.ts │ │ ├── question.ts │ │ ├── research.ts │ │ ├── selectValue.ts │ │ ├── subscriber.ts │ │ ├── tag.ts │ │ ├── tags.ts │ │ ├── tenantSettings.ts │ │ ├── upgradeBadge.ts │ │ ├── user.ts │ │ ├── userCreatedDocs.ts │ │ ├── userEmailData.ts │ │ ├── voteUseful.ts │ │ └── webmanifest.ts │ ├── package.json │ ├── tsconfig.json │ └── utils/ │ ├── file.utils.ts │ ├── index.ts │ ├── markdown.test.ts │ └── markdown.ts ├── src/ │ ├── .server/ │ │ ├── models/ │ │ │ └── messageSettings.ts │ │ ├── resend.ts │ │ └── templates/ │ │ ├── Layout.tsx │ │ ├── ReceiverMessage.tsx │ │ ├── components/ │ │ │ ├── box-text.tsx │ │ │ ├── button.tsx │ │ │ ├── footer.tsx │ │ │ ├── header.tsx │ │ │ └── heading.tsx │ │ └── instant-notification-email.tsx │ ├── common/ │ │ ├── Alerts/ │ │ │ ├── AlertBanner.test.tsx │ │ │ ├── AlertBanner.tsx │ │ │ ├── AlertIncompleteProfile.tsx │ │ │ └── Alerts.tsx │ │ ├── Analytics/ │ │ │ ├── GoogleAnalytics.tsx │ │ │ └── index.tsx │ │ ├── AuthWrapper.test.tsx │ │ ├── AuthWrapper.tsx │ │ ├── DonationRequestModalContainer.test.tsx │ │ ├── DonationRequestModalContainer.tsx │ │ ├── DownloadWrapper.test.tsx │ │ ├── DownloadWrapper.tsx │ │ ├── Form/ │ │ │ ├── Checkbox.tsx │ │ │ ├── ErrorsContainer.test.tsx │ │ │ ├── ErrorsContainer.tsx │ │ │ ├── FieldContainer.ts │ │ │ ├── FileInput/ │ │ │ │ ├── FileDisplay.tsx │ │ │ │ └── FileInput.tsx │ │ │ ├── FileInput.field.tsx │ │ │ ├── FormWrapper.tsx │ │ │ ├── PasswordField.tsx │ │ │ ├── README.md │ │ │ ├── Select.field.tsx │ │ │ ├── TagsSelectField.tsx │ │ │ ├── UnsavedChangesDialog.tsx │ │ │ ├── labels.ts │ │ │ └── types.ts │ │ ├── HideDiscussionContainer.test.tsx │ │ ├── HideDiscussionContainer.tsx │ │ ├── Highlighter.tsx │ │ ├── PageHeader.tsx │ │ ├── PremiumTierWrapper.test.tsx │ │ ├── PremiumTierWrapper.tsx │ │ ├── Spinner.tsx │ │ ├── Tags/ │ │ │ ├── ProfileTagsSelect.tsx │ │ │ └── TagsSelect.tsx │ │ ├── Toast/ │ │ │ ├── CustomToast.tsx │ │ │ ├── index.tsx │ │ │ └── useToast.tsx │ │ ├── UserAction.test.tsx │ │ ├── UserAction.tsx │ │ └── hooks/ │ │ └── useClickOutside.ts │ ├── config/ │ │ ├── config.ts │ │ ├── constants.ts │ │ ├── default.json │ │ ├── imageTransforms.ts │ │ └── types.ts │ ├── constants.ts │ ├── entry.client.tsx │ ├── entry.server.tsx │ ├── factories/ │ │ ├── commentFactory.server.ts │ │ ├── mapPinFactory.server.ts │ │ └── profileFactory.server.ts │ ├── logger/ │ │ ├── index.test.ts │ │ └── index.ts │ ├── modules/ │ │ ├── index.test.ts │ │ └── index.ts │ ├── pages/ │ │ ├── Academy/ │ │ │ ├── Academy.test.tsx │ │ │ ├── Academy.tsx │ │ │ └── ExternalEmbed/ │ │ │ └── ExternalEmbed.tsx │ │ ├── Library/ │ │ │ ├── Content/ │ │ │ │ ├── Common/ │ │ │ │ │ ├── DeleteProjectButton.tsx │ │ │ │ │ ├── FormSettings.tsx │ │ │ │ │ ├── Library.form.test.tsx │ │ │ │ │ ├── LibraryCategory.field.tsx │ │ │ │ │ ├── LibraryCategoryGuidance.test.tsx │ │ │ │ │ ├── LibraryCategoryGuidance.tsx │ │ │ │ │ ├── LibraryDescription.field.test.tsx │ │ │ │ │ ├── LibraryDescription.field.tsx │ │ │ │ │ ├── LibraryDifficulty.field.tsx │ │ │ │ │ ├── LibraryFileLink.field.tsx │ │ │ │ │ ├── LibraryFileUpload.field.tsx │ │ │ │ │ ├── LibraryForm.tsx │ │ │ │ │ ├── LibraryFormProvider.tsx │ │ │ │ │ ├── LibraryPostingGuidelines.tsx │ │ │ │ │ ├── LibraryStep.field.test.tsx │ │ │ │ │ ├── LibraryStep.field.tsx │ │ │ │ │ ├── LibraryStepsContainer.field.test.tsx │ │ │ │ │ ├── LibraryStepsContainer.field.tsx │ │ │ │ │ ├── LibraryTime.field.tsx │ │ │ │ │ ├── LibraryTitle.field.test.tsx │ │ │ │ │ └── LibraryTitle.field.tsx │ │ │ │ ├── List/ │ │ │ │ │ ├── LibraryList.tsx │ │ │ │ │ ├── LibraryListHeader.tsx │ │ │ │ │ ├── LibrarySortOptions.ts │ │ │ │ │ └── ProjectCard.tsx │ │ │ │ ├── Page/ │ │ │ │ │ ├── LibraryDescription.tsx │ │ │ │ │ ├── LibraryStep.tsx │ │ │ │ │ └── ProjectPage.tsx │ │ │ │ └── utils/ │ │ │ │ ├── downloadCooldown.ts │ │ │ │ ├── index.ts │ │ │ │ ├── transformLibraryErrors.test.ts │ │ │ │ └── transformLibraryErrors.ts │ │ │ ├── constants.ts │ │ │ ├── labels.ts │ │ │ └── library.service.ts │ │ ├── Maps/ │ │ │ ├── Content/ │ │ │ │ ├── MapView/ │ │ │ │ │ ├── ButtonZoomIn.client.tsx │ │ │ │ │ ├── Cluster.client.tsx │ │ │ │ │ ├── MapList.tsx │ │ │ │ │ ├── MapView.tsx │ │ │ │ │ ├── MapWithListHeader.tsx │ │ │ │ │ ├── Popup.client.tsx │ │ │ │ │ ├── Sprites.tsx │ │ │ │ │ ├── popup.css │ │ │ │ │ └── sprites.css │ │ │ │ └── MemberTypeVerticalList/ │ │ │ │ └── MemberTypeVerticalList.client.tsx │ │ │ ├── MapContext.ts │ │ │ ├── MapFilterList.tsx │ │ │ ├── Maps.client.tsx │ │ │ ├── map.service.ts │ │ │ ├── styles.css │ │ │ └── utils/ │ │ │ ├── geolocation.ts │ │ │ ├── pinUtils.test.ts │ │ │ └── pinUtils.ts │ │ ├── News/ │ │ │ ├── Content/ │ │ │ │ └── Common/ │ │ │ │ ├── FormFields/ │ │ │ │ │ ├── NewsBodyField.tsx │ │ │ │ │ ├── NewsImageField.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── NewsForm.test.tsx │ │ │ │ ├── NewsForm.tsx │ │ │ │ └── NewsPostingGuidelines.tsx │ │ │ ├── NewsListHeader.tsx │ │ │ ├── NewsListItem.tsx │ │ │ ├── NewsListing.tsx │ │ │ ├── NewsPage.test.tsx │ │ │ ├── NewsPage.tsx │ │ │ ├── NewsSortOptions.ts │ │ │ ├── constants.ts │ │ │ ├── labels.ts │ │ │ └── newsContent.service.ts │ │ ├── NotFound/ │ │ │ └── NotFound.tsx │ │ ├── PageList.tsx │ │ ├── Question/ │ │ │ ├── Content/ │ │ │ │ └── Common/ │ │ │ │ ├── FormFields/ │ │ │ │ │ ├── QuestionDescription.field.tsx │ │ │ │ │ ├── QuestionImages.field.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── QuestionForm.tsx │ │ │ │ ├── QuestionPostingGuidelines.tsx │ │ │ │ └── index.ts │ │ │ ├── QuestionListHeader.tsx │ │ │ ├── QuestionListItem.tsx │ │ │ ├── QuestionListing.tsx │ │ │ ├── QuestionPage.test.tsx │ │ │ ├── QuestionPage.tsx │ │ │ ├── QuestionSortOptions.ts │ │ │ ├── constants.ts │ │ │ ├── labels.ts │ │ │ └── question.service.ts │ │ ├── Research/ │ │ │ ├── Content/ │ │ │ │ ├── Common/ │ │ │ │ │ ├── DeleteResearchButton.tsx │ │ │ │ │ ├── FormFields/ │ │ │ │ │ │ ├── ResearchCollaboratorsField.tsx │ │ │ │ │ │ ├── ResearchDescriptionField.tsx │ │ │ │ │ │ └── ResearchTitleField.tsx │ │ │ │ │ ├── ResearchCategorySelect.tsx │ │ │ │ │ ├── ResearchForm.tsx │ │ │ │ │ ├── ResearchPostingGuidelines.tsx │ │ │ │ │ ├── ResearchUpdateForm.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── CreateResearch/ │ │ │ │ │ └── Form/ │ │ │ │ │ ├── DescriptionField.tsx │ │ │ │ │ ├── ResearchImagesField.tsx │ │ │ │ │ ├── TitleField.tsx │ │ │ │ │ └── VideoUrlField.tsx │ │ │ │ ├── ResearchArticle.test.tsx │ │ │ │ ├── ResearchArticlePage.tsx │ │ │ │ ├── ResearchDescription.tsx │ │ │ │ ├── ResearchEngagementSection.tsx │ │ │ │ ├── ResearchFooter.tsx │ │ │ │ ├── ResearchLinkToUpdate.tsx │ │ │ │ ├── ResearchList.tsx │ │ │ │ ├── ResearchListHeader.tsx │ │ │ │ ├── ResearchListItem.tsx │ │ │ │ ├── ResearchSearchParams.ts │ │ │ │ ├── ResearchUpdate.tsx │ │ │ │ ├── helper.test.tsx │ │ │ │ └── helper.ts │ │ │ ├── ResearchSortOptions.ts │ │ │ ├── constants.ts │ │ │ ├── labels.ts │ │ │ ├── research.service.test.ts │ │ │ ├── research.service.ts │ │ │ ├── researchHelpers.test.ts │ │ │ └── researchHelpers.ts │ │ ├── SignUp/ │ │ │ └── SignUpMessage.tsx │ │ ├── User/ │ │ │ ├── constants.ts │ │ │ ├── contact/ │ │ │ │ ├── UserContactFieldMessage.tsx │ │ │ │ ├── UserContactFieldName.tsx │ │ │ │ ├── UserContactForm.test.tsx │ │ │ │ ├── UserContactForm.tsx │ │ │ │ ├── UserContactFormAvailable.tsx │ │ │ │ ├── UserContactFormNotLoggedIn.tsx │ │ │ │ ├── UserContactNotLoggedIn.tsx │ │ │ │ └── index.ts │ │ │ ├── content/ │ │ │ │ ├── ProfileContact.tsx │ │ │ │ ├── ProfileDetails.tsx │ │ │ │ ├── ProfileHeader.tsx │ │ │ │ ├── ProfileImage.tsx │ │ │ │ ├── ProfilePage.tsx │ │ │ │ ├── UserCreatedDocuments.test.tsx │ │ │ │ ├── UserCreatedDocuments.tsx │ │ │ │ ├── UserCreatedDocumentsItem.tsx │ │ │ │ └── UserProfile.tsx │ │ │ ├── impact/ │ │ │ │ ├── Impact.test.tsx │ │ │ │ ├── Impact.tsx │ │ │ │ ├── ImpactField.test.tsx │ │ │ │ ├── ImpactField.tsx │ │ │ │ ├── ImpactIcon.tsx │ │ │ │ ├── ImpactItem.test.tsx │ │ │ │ ├── ImpactItem.tsx │ │ │ │ ├── ImpactMissing.test.tsx │ │ │ │ ├── ImpactMissing.tsx │ │ │ │ ├── constants.ts │ │ │ │ └── labels.ts │ │ │ └── labels.ts │ │ ├── UserSettings/ │ │ │ ├── SettingsFormTab.tsx │ │ │ ├── SettingsFormTabList.tsx │ │ │ ├── SettingsPage.client.tsx │ │ │ ├── SettingsPageAccount.tsx │ │ │ ├── SettingsPageImpact.test.tsx │ │ │ ├── SettingsPageImpact.tsx │ │ │ ├── SettingsPageMapPin.test.tsx │ │ │ ├── SettingsPageMapPin.tsx │ │ │ ├── SettingsPageNotifications.tsx │ │ │ ├── SettingsPageUserProfile.test.tsx │ │ │ ├── SettingsPageUserProfile.tsx │ │ │ ├── SupabaseNotifications.tsx │ │ │ ├── SupabaseNotificationsForm.tsx │ │ │ ├── SupabaseNotificationsViaEmail.tsx │ │ │ ├── __mocks__/ │ │ │ │ └── FormProvider.tsx │ │ │ ├── constants.ts │ │ │ ├── content/ │ │ │ │ ├── elements.tsx │ │ │ │ ├── fields/ │ │ │ │ │ ├── ImpactQuestion.field.tsx │ │ │ │ │ ├── ImpactYear.field.tsx │ │ │ │ │ ├── ImpactYearDisplay.field.tsx │ │ │ │ │ ├── PatreonIntegration.test.tsx │ │ │ │ │ ├── PatreonIntegration.tsx │ │ │ │ │ └── ProfileTypeRadio.field.tsx │ │ │ │ ├── impactQuestions.ts │ │ │ │ └── sections/ │ │ │ │ ├── ChangeEmail.form.tsx │ │ │ │ ├── ChangePassword.form.tsx │ │ │ │ ├── EmailNotifications.section.tsx │ │ │ │ ├── ImpactYear.section.tsx │ │ │ │ ├── ProfileTags.section.tsx │ │ │ │ ├── ProfileType.section.test.tsx │ │ │ │ ├── ProfileType.section.tsx │ │ │ │ ├── PublicContact.section.test.tsx │ │ │ │ ├── PublicContact.section.tsx │ │ │ │ ├── UserImages.section.tsx │ │ │ │ ├── UserInfos.section.tsx │ │ │ │ └── VisitorSection.tsx │ │ │ ├── labels.ts │ │ │ ├── services/ │ │ │ │ └── account.service.ts │ │ │ ├── types.ts │ │ │ ├── utils.test.ts │ │ │ └── utils.ts │ │ ├── common/ │ │ │ ├── Breadcrumbs/ │ │ │ │ └── Breadcrumbs.tsx │ │ │ ├── Category/ │ │ │ │ └── CategoriesSelectV2.tsx │ │ │ ├── CommentsSupabase/ │ │ │ │ ├── CollapsableCommentSection.tsx │ │ │ │ ├── CommentItemSupabase.tsx │ │ │ │ ├── CommentReplySupabase.tsx │ │ │ │ ├── CommentSectionSupabase.tsx │ │ │ │ ├── CommentSort.tsx │ │ │ │ ├── CommentSortOptions.ts │ │ │ │ ├── CreateCommentSupabase.css │ │ │ │ ├── CreateCommentSupabase.tsx │ │ │ │ └── useCopyCommentLink.ts │ │ │ ├── Drafts/ │ │ │ │ ├── DraftButton.test.tsx │ │ │ │ ├── DraftButton.tsx │ │ │ │ ├── DraftTag.tsx │ │ │ │ └── useDraftsSupabase.tsx │ │ │ ├── FormFields/ │ │ │ │ ├── Category.field.tsx │ │ │ │ ├── FilesFields.tsx │ │ │ │ ├── FormFieldWrapper.test.tsx │ │ │ │ ├── FormFieldWrapper.tsx │ │ │ │ ├── ImageField.tsx │ │ │ │ ├── ImageInputFieldWrapper.tsx │ │ │ │ ├── ProfileBadgeField.tsx │ │ │ │ ├── StepImagesField.tsx │ │ │ │ ├── Tags.field.tsx │ │ │ │ ├── Title.field.tsx │ │ │ │ ├── index.ts │ │ │ │ └── labels.ts │ │ │ ├── GlobalSiteFooter/ │ │ │ │ └── GlobalSiteFooter.tsx │ │ │ ├── Header/ │ │ │ │ ├── Header.tsx │ │ │ │ ├── Menu/ │ │ │ │ │ ├── Logo/ │ │ │ │ │ │ └── Logo.tsx │ │ │ │ │ ├── MenuDesktop.tsx │ │ │ │ │ ├── MenuMobile/ │ │ │ │ │ │ ├── MenuMobileExternalLink.tsx │ │ │ │ │ │ ├── MenuMobileLink.tsx │ │ │ │ │ │ └── MenuMobilePanel.tsx │ │ │ │ │ ├── Notifications/ │ │ │ │ │ │ └── NotificationsSupabase.tsx │ │ │ │ │ ├── Profile/ │ │ │ │ │ │ ├── Profile.tsx │ │ │ │ │ │ ├── ProfileButtonItem.tsx │ │ │ │ │ │ ├── ProfileButtons.tsx │ │ │ │ │ │ ├── UpgradeBadgeLink.tsx │ │ │ │ │ │ └── profile.css │ │ │ │ │ └── ProfileModal/ │ │ │ │ │ └── ProfileModal.tsx │ │ │ │ └── MobileMenuContext.tsx │ │ │ ├── Layout/ │ │ │ │ ├── ListHeader.tsx │ │ │ │ ├── Main.tsx │ │ │ │ └── MobileSortModal.tsx │ │ │ ├── NotificationsContext.ts │ │ │ ├── SessionContext.ts │ │ │ ├── StickyButton.tsx │ │ │ ├── TenantContext.ts │ │ │ ├── UserNameSelect/ │ │ │ │ └── UserNameSelect.tsx │ │ │ ├── UserNameTag/ │ │ │ │ └── UserNameTag.tsx │ │ │ ├── banner.service.ts │ │ │ └── labels.ts │ │ ├── constants.ts │ │ └── policy/ │ │ ├── PrivacyPolicy.tsx │ │ └── TermsPolicy.tsx │ ├── repository/ │ │ ├── supabase.server.ts │ │ └── supabaseAdmin.server.ts │ ├── root.tsx │ ├── routes/ │ │ ├── [manifest.webmanifest].tsx │ │ ├── [robots.txt].tsx │ │ ├── _.$.tsx │ │ ├── _.academy.$.tsx │ │ ├── _.email-confirmation.tsx │ │ ├── _.email-preferences.tsx │ │ ├── _.feedback.tsx │ │ ├── _.forbidden.tsx │ │ ├── _.how-to.$slug._index.tsx │ │ ├── _.how-to.$slug.edit.tsx │ │ ├── _.how-to._index.tsx │ │ ├── _.how-to.create.tsx │ │ ├── _.how-to.tsx │ │ ├── _.library.$slug._index.tsx │ │ ├── _.library.$slug.edit.tsx │ │ ├── _.library._index.tsx │ │ ├── _.library.create.tsx │ │ ├── _.library.tsx │ │ ├── _.map.tsx │ │ ├── _.news.$slug._index.tsx │ │ ├── _.news.$slug.edit.tsx │ │ ├── _.news._index.tsx │ │ ├── _.news.create.tsx │ │ ├── _.news.tsx │ │ ├── _.patreon.tsx │ │ ├── _.privacy.tsx │ │ ├── _.questions.$slug._index.tsx │ │ ├── _.questions.$slug.edit.tsx │ │ ├── _.questions._index.tsx │ │ ├── _.questions.create.tsx │ │ ├── _.questions.tsx │ │ ├── _.research.$slug._index.tsx │ │ ├── _.research.$slug.edit-update.$updateId.tsx │ │ ├── _.research.$slug.edit.tsx │ │ ├── _.research.$slug.new-update.tsx │ │ ├── _.research._index.tsx │ │ ├── _.research.create.tsx │ │ ├── _.research.tsx │ │ ├── _.reset-password.tsx │ │ ├── _.settings.$.tsx │ │ ├── _.sign-in.tsx │ │ ├── _.sign-up-message.tsx │ │ ├── _.sign-up.tsx │ │ ├── _.terms.tsx │ │ ├── _.tsx │ │ ├── _.u.$id._index.tsx │ │ ├── _.u.tsx │ │ ├── _.update-password.tsx │ │ ├── _index.tsx │ │ ├── api.account.change-email.tsx │ │ ├── api.account.change-password.tsx │ │ ├── api.banner.ts │ │ ├── api.categories.$type.ts │ │ ├── api.comments.$id.source.ts │ │ ├── api.discussions.$sourceId.comments.$id.ts │ │ ├── api.discussions.$sourceType.$sourceId.comments.ts │ │ ├── api.documents.$type.$contentId.$docId.tsx │ │ ├── api.documents.ts │ │ ├── api.donation-settings.$profileId.ts │ │ ├── api.favicon.ts │ │ ├── api.images.ts │ │ ├── api.map-filters.ts │ │ ├── api.map-pin.ts │ │ ├── api.map-pins.$userId.ts │ │ ├── api.map-pins.ts │ │ ├── api.messages.tsx │ │ ├── api.news.$id.ts │ │ ├── api.news.drafts.count.ts │ │ ├── api.news.drafts.ts │ │ ├── api.news.ts │ │ ├── api.notifications-preferences-via-email.$userCode.ts │ │ ├── api.notifications-preferences.ts │ │ ├── api.notifications.$id.read.ts │ │ ├── api.notifications.all.read.ts │ │ ├── api.notifications.ts │ │ ├── api.patreon.ts │ │ ├── api.profile-badges.ts │ │ ├── api.profile-tags.ts │ │ ├── api.profile-types.ts │ │ ├── api.profile.ts │ │ ├── api.profile.username.ts │ │ ├── api.profiles.ts │ │ ├── api.projects.$id.ts │ │ ├── api.projects.drafts.count.ts │ │ ├── api.projects.drafts.ts │ │ ├── api.projects.ts │ │ ├── api.questions.$id.ts │ │ ├── api.questions.drafts.count.ts │ │ ├── api.questions.drafts.ts │ │ ├── api.questions.ts │ │ ├── api.research.$id.status.ts │ │ ├── api.research.$id.ts │ │ ├── api.research.$id.updates.$updateId.ts │ │ ├── api.research.$id.updates.ts │ │ ├── api.research.drafts.count.ts │ │ ├── api.research.drafts.ts │ │ ├── api.research.ts │ │ ├── api.settings.impact.ts │ │ ├── api.settings.map.ts │ │ ├── api.subscribers.$contentType.$contentId.subscribed.ts │ │ ├── api.subscribers.$contentType.$contentId.ts │ │ ├── api.tags.ts │ │ ├── api.upgrade-badges.ts │ │ ├── api.useful.$contentType.$contentId.ts │ │ ├── api.useful.$contentType.$contentId.users.ts │ │ ├── api.useful.$contentType.$contentId.voted.ts │ │ ├── api.useful.$contentType.$id.count.ts │ │ ├── logout.ts │ │ └── redirect.tsx │ ├── routes.ts │ ├── services/ │ │ ├── authService.server.ts │ │ ├── broadcastCoordinationService.server.ts │ │ ├── categoryService.ts │ │ ├── commentService.ts │ │ ├── contentRedirectService.server.ts │ │ ├── contentService.server.ts │ │ ├── discordService.server.ts │ │ ├── formDataHelper.ts │ │ ├── imageService.server.ts │ │ ├── impactService.server.ts │ │ ├── libraryService.server.ts │ │ ├── mapPinsService.server.ts │ │ ├── mapService.server.ts │ │ ├── messageService.ts │ │ ├── newsService.server.ts │ │ ├── newsService.ts │ │ ├── notificationEmailService.server.ts │ │ ├── notificationMapperService.server.ts │ │ ├── notificationsPreferencesService.ts │ │ ├── notificationsPreferencesViaEmailService.ts │ │ ├── notificationsSupabaseService.server.ts │ │ ├── notificationsSupabaseService.ts │ │ ├── patreonService.server.ts │ │ ├── patreonService.ts │ │ ├── profileBadgeService.ts │ │ ├── profileService.server.ts │ │ ├── profileService.ts │ │ ├── profileTagsService.ts │ │ ├── profileTypesService.server.ts │ │ ├── profileTypesService.ts │ │ ├── profilesService.ts │ │ ├── questionService.server.test.ts │ │ ├── questionService.server.ts │ │ ├── questionService.ts │ │ ├── redirectService.server.ts │ │ ├── researchService.server.ts │ │ ├── storageService.server.ts │ │ ├── storageService.ts │ │ ├── subscribersService.server.ts │ │ ├── subscribersService.ts │ │ ├── tagsService.server.ts │ │ ├── tagsService.ts │ │ ├── tenantSettingsService.server.ts │ │ ├── upgradeBadgeService.ts │ │ └── usefulService.ts │ ├── stores/ │ │ ├── Profile/ │ │ │ ├── profile.store.test.ts │ │ │ └── profile.store.tsx │ │ ├── Subscription/ │ │ │ ├── subscription.store.test.ts │ │ │ ├── subscription.store.tsx │ │ │ └── useSubscription.tsx │ │ └── UsefulVote/ │ │ ├── useUsefulVote.tsx │ │ └── usefulVote.store.tsx │ ├── styles/ │ │ ├── context.ts │ │ ├── createEmotionCache.ts │ │ └── leaflet.css │ ├── test/ │ │ ├── components/ │ │ │ └── SettingsFormProvider.tsx │ │ ├── factories/ │ │ │ ├── Category.ts │ │ │ ├── Comment.ts │ │ │ ├── Library.ts │ │ │ ├── MapPin.ts │ │ │ ├── News.ts │ │ │ ├── Question.ts │ │ │ ├── ResearchItem.ts │ │ │ ├── User.ts │ │ │ ├── dbNotification.ts │ │ │ ├── notificationsPreferences.ts │ │ │ ├── profile.ts │ │ │ └── supabaseNotification.ts │ │ ├── models/ │ │ │ └── profile.test.ts │ │ ├── routes/ │ │ │ └── api.notifications-preferences.test.ts │ │ ├── services/ │ │ │ ├── broadcastCoordinationService.server.ts │ │ │ ├── notificationsPreferencesService.test.ts │ │ │ ├── notificationsPreferencesViaEmailService.test.ts │ │ │ └── subscribersService.server.test.ts │ │ ├── setup.ts │ │ └── utils/ │ │ └── supabaseClientMock.ts │ ├── types/ │ │ └── emotion.d.ts │ └── utils/ │ ├── comparisons.ts │ ├── contentType.utils.ts │ ├── filters.ts │ ├── fireConfetti.ts │ ├── formatImageListForGallery.ts │ ├── getLocationData.ts │ ├── getSummaryFromMarkdown.test.ts │ ├── getSummaryFromMarkdown.ts │ ├── helpers.test.ts │ ├── helpers.ts │ ├── httpException.ts │ ├── mentions.utils.ts │ ├── redirect.server.ts │ ├── searchHelper.test.ts │ ├── searchHelper.ts │ ├── seo.utils.ts │ ├── sessionStorage.ts │ ├── slug.ts │ ├── statistics.tsx │ ├── stopwords.ts │ ├── storage.ts │ ├── tokens.server.ts │ ├── urlHelper.ts │ ├── urls.ts │ ├── validators.test.ts │ └── validators.ts ├── supabase/ │ ├── .gitignore │ ├── README.md │ ├── config.toml │ ├── functions/ │ │ └── send-email/ │ │ ├── .npmrc │ │ ├── _templates/ │ │ │ ├── components/ │ │ │ │ ├── box-text.tsx │ │ │ │ ├── button.tsx │ │ │ │ ├── footer.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── heading.tsx │ │ │ │ ├── hero.tsx │ │ │ │ ├── parent-box.tsx │ │ │ │ └── plain-text.tsx │ │ │ ├── email-change-new.tsx │ │ │ ├── layout.tsx │ │ │ ├── magic-link.tsx │ │ │ ├── moderation-email.tsx │ │ │ ├── reset-password.tsx │ │ │ └── sign-up.tsx │ │ ├── deno.json │ │ ├── getTenantSettings.ts │ │ ├── index.ts │ │ └── signWebhookHeader.ts │ ├── migrations/ │ │ ├── 20241125140428_profiles_and_comments.sql │ │ ├── 20250111151556_questions.sql │ │ ├── 20250113184950_profile_auth_columns.sql │ │ ├── 20250208130256_auth_rpc.sql │ │ ├── 20250209131232_profile_firebase.sql │ │ ├── 20250214235014_messages.sql │ │ ├── 20250225071100_unique_username.sql │ │ ├── 20250307061534_messages_temp_fix.sql │ │ ├── 20250312063008_patreon.sql │ │ ├── 20250331134409_add_news.sql │ │ ├── 20250416104948_research.sql │ │ ├── 20250422092704_add_notifications.sql │ │ ├── 20250512170000_research_sorting.sql │ │ ├── 20250513090512_update_for_email_notifications.sql │ │ ├── 20250514163118_update_notifications.sql │ │ ├── 20250520134140_update_notifications_for_research_comments.sql │ │ ├── 20250521044802_fix-research-sorting-useful.sql │ │ ├── 20250523104253_20250523_add-get-user-questions.sql │ │ ├── 20250603130017_add_notifications_preferences.sql │ │ ├── 20250605115351_research-latest-updated.sql │ │ ├── 20250609100159_research_query_ranking.sql │ │ ├── 20250609101144_library.sql │ │ ├── 20250617151457_update_notification_preferences.sql │ │ ├── 20250621165139_user_research_filter_draft.sql │ │ ├── 20250624101144_replace_get_projects_count.sql │ │ ├── 20250624101145_replace_get_user_projects.sql │ │ ├── 20250624101147_replace_get_user_projects_again.sql │ │ ├── 20250625162400_update_questions_news_is_draft.sql │ │ ├── 20250702155642_add_unsubscribe_to_preferences.sql │ │ ├── 20250722145300_profile.sql │ │ ├── 20250819103736_add_badge_id_to_news.sql │ │ ├── 20250824222054_fix-author-vote-count-deleted.sql │ │ ├── 20250830203011_banner.sql │ │ ├── 20250902101059_map_pins-optimizations.sql │ │ ├── 20250904101010_useful_comments.sql │ │ ├── 20250912091153_fix-delete-comment-trigger.sql │ │ ├── 20250912110000_optimize_get_comments_with_votes.sql │ │ ├── 20250912210000_optimize_profile_indexes.sql │ │ ├── 20250914104923_update_notifications.sql │ │ ├── 20251011132910_fix_messages_receiver.sql │ │ ├── 20251011233154_fix_policy_performance.sql │ │ ├── 20251011234304_fix_function_security_warning.sql │ │ ├── 20251012135652_fix_policy_performance_2.sql │ │ ├── 20251024140004_fix_get_project_count.sql │ │ ├── 20251025145021_add_most_views_sort.sql │ │ ├── 20251031045802_add_favicon.sql │ │ ├── 20251119072930_track_badges.sql │ │ ├── 20251124054625_profile_donations.sql │ │ ├── 20251130000401_create_upgrade_badge_table.sql │ │ ├── 20251203151049_research_author_search_fix.sql │ │ ├── 20251217132033_add_most_useful_last_week_feature.sql │ │ ├── 20260103002808_add_premium_tier.sql │ │ ├── 20260118140428_update_user_doc_gets.sql │ │ ├── 20260118233300_batch_notifications.sql │ │ ├── 20260216125228_more_tenant_settings.sql │ │ ├── 20260227070309_site_description.sql │ │ ├── 20260301000000_fix_partial_search.sql │ │ ├── 20260309135320_questions_search_rpc.sql │ │ ├── 20260317131051_add_published_at.sql │ │ ├── 20260322000000_add_get_storage_object_path.sql │ │ ├── 20260330124656_make_username_nullable.sql │ │ ├── 20260401000000_tenant_settings_ga.sql │ │ ├── 20260402103538_search_multiple_words.sql │ │ ├── 20260402150623_search_stop_words.sql │ │ └── 20260421051050_pwa_icons.sql │ ├── schemas/ │ │ ├── banners.sql │ │ ├── categories.sql │ │ ├── comments.sql │ │ ├── common.sql │ │ ├── map.sql │ │ ├── messages.sql │ │ ├── news.sql │ │ ├── notifications.sql │ │ ├── patreon.sql │ │ ├── profiles.sql │ │ ├── projects.sql │ │ ├── questions.sql │ │ ├── research.sql │ │ ├── subscribers.sql │ │ ├── tags.sql │ │ ├── tenant_settings.sql │ │ └── useful.sql │ └── seed.sql ├── tsconfig.json ├── tsconfig.prod.json ├── vite-env.d.ts └── vite.config.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .all-contributorsrc ================================================ { "files": [ "README.md" ], "imageSize": 60, "contributorsPerLine": 7, "contributorsSortAlphabetically": false, "badgeTemplate": "[![All Contributors](https://img.shields.io/badge/all_contributors-<%= contributors.length %>-orange.svg?style=flat-square)](#contributors)", "types": { "maintenance": { "symbol": "💪", "description": "Maintainer", "link": "[<%= symbol %>](\"<%= description %>\")," } }, "contributors": [ { "login": "davehakkens", "name": "Dave Hakkens", "avatar_url": "https://avatars.githubusercontent.com/u/13672737?v=4", "profile": "http://davehakkens.nl", "contributions": [ "design", "ideas", "projectManagement", "maintenance" ] }, { "login": "chrismclarke", "name": "Chris Clarke", "avatar_url": "https://avatars.githubusercontent.com/u/10515065?v=4", "profile": "https://c2dev.co.uk/", "contributions": [ "code", "maintenance" ] }, { "login": "thisislawatts", "name": "Luke Watts", "avatar_url": "https://avatars.githubusercontent.com/u/472589?v=4", "profile": "https://thisis.la/", "contributions": [ "code", "maintenance" ] }, { "login": "amuroBosetti", "name": "Mauro Bosetti", "avatar_url": "https://avatars.githubusercontent.com/u/46928545?v=4", "profile": "https://github.com/amuroBosetti", "contributions": [ "doc" ] }, { "login": "patrycjapraczyk", "name": "patrycjapraczyk", "avatar_url": "https://avatars.githubusercontent.com/u/35103888?v=4", "profile": "https://github.com/patrycjapraczyk", "contributions": [ "code" ] }, { "login": "tedspare", "name": "Ted Spare", "avatar_url": "https://avatars.githubusercontent.com/u/36117635?v=4", "profile": "https://tedspare.com", "contributions": [ "code" ] }, { "login": "eliasvelardezft", "name": "Elias Velardez", "avatar_url": "https://avatars.githubusercontent.com/u/40184787?v=4", "profile": "https://www.linkedin.com/in/eliasvelardez", "contributions": [ "code" ] }, { "login": "AlfonsoGhislieri", "name": "Alfonso", "avatar_url": "https://avatars.githubusercontent.com/u/652368?v=4", "profile": "https://github.com/AlfonsoGhislieri", "contributions": [ "code", "maintenance" ] }, { "login": "Xyli0", "name": "Xyli0", "avatar_url": "https://avatars.githubusercontent.com/u/10441748?v=4", "profile": "https://github.com/Xyli0", "contributions": [ "code" ] }, { "login": "laianbraum", "name": "Laian Braum", "avatar_url": "https://avatars.githubusercontent.com/u/61033391?v=4", "profile": "http://www.linkedin.com/in/laianbraum", "contributions": [ "code" ] }, { "login": "osouthwell-scottlogic", "name": "osouthwell-scottlogic", "avatar_url": "https://avatars.githubusercontent.com/u/98388720?v=4", "profile": "https://github.com/osouthwell-scottlogic", "contributions": [ "code" ] }, { "login": "asheerrizvi", "name": "Asheer Rizvi", "avatar_url": "https://avatars.githubusercontent.com/u/17976252?v=4", "profile": "http://asheerrizvi.com", "contributions": [ "code" ] }, { "login": "franknoirot", "name": "Frank Noirot", "avatar_url": "https://avatars.githubusercontent.com/u/23481541?v=4", "profile": "https://franknoirot.co", "contributions": [ "design" ] }, { "login": "LucasGabrielBecker", "name": "Lucas Becker ", "avatar_url": "https://avatars.githubusercontent.com/u/48301172?v=4", "profile": "https://github.com/LucasGabrielBecker", "contributions": [ "code" ] }, { "login": "cschilbe", "name": "Conrad Schilbe", "avatar_url": "https://avatars.githubusercontent.com/u/485557?v=4", "profile": "https://github.com/cschilbe", "contributions": [ "code" ] }, { "login": "ThakurKarthik", "name": "Thakur Karthik", "avatar_url": "https://avatars.githubusercontent.com/u/26309938?v=4", "profile": "https://github.com/ThakurKarthik", "contributions": [ "code" ] }, { "login": "danitrod", "name": "Daniel T. Rodrigues", "avatar_url": "https://avatars.githubusercontent.com/u/45438149?v=4", "profile": "https://www.linkedin.com/in/danitrod/", "contributions": [ "code" ] }, { "login": "adrianduke", "name": "Adrian Duke", "avatar_url": "https://avatars.githubusercontent.com/u/711058?v=4", "profile": "https://github.com/adrianduke", "contributions": [ "code" ] }, { "login": "missalyss", "name": "Alyssa Helgason", "avatar_url": "https://avatars.githubusercontent.com/u/19866110?v=4", "profile": "https://github.com/missalyss", "contributions": [ "code" ] }, { "login": "Kiebert", "name": "Kieb", "avatar_url": "https://avatars.githubusercontent.com/u/3414938?v=4", "profile": "https://github.com/Kiebert", "contributions": [ "code" ] }, { "login": "Sc4ramouche", "name": "Kovechenkov Vladislav", "avatar_url": "https://avatars.githubusercontent.com/u/25829136?v=4", "profile": "https://github.com/Sc4ramouche", "contributions": [ "code" ] }, { "login": "cerkiewny", "name": "Devtato", "avatar_url": "https://avatars.githubusercontent.com/u/4504330?v=4", "profile": "http://devtato.com", "contributions": [ "code" ] }, { "login": "NoHara42", "name": "Ned O'Hara", "avatar_url": "https://avatars.githubusercontent.com/u/43496778?v=4", "profile": "https://github.com/NoHara42", "contributions": [ "code" ] }, { "login": "SophXN", "name": "Sophia Nguyen", "avatar_url": "https://avatars.githubusercontent.com/u/80185757?v=4", "profile": "https://github.com/SophXN", "contributions": [ "code" ] }, { "login": "evakill", "name": "Eva Killenberg", "avatar_url": "https://avatars.githubusercontent.com/u/37253846?v=4", "profile": "https://www.evakillenberg.com", "contributions": [ "code", "maintenance" ] }, { "login": "iSCJT", "name": "Sean Thompson", "avatar_url": "https://avatars.githubusercontent.com/u/80723794?v=4", "profile": "https://speckledbanana.com", "contributions": [ "code", "maintenance" ] }, { "login": "NguyenVanDo51", "name": "Nguyễn Văn Đỏ", "avatar_url": "https://avatars.githubusercontent.com/u/30190734?v=4", "profile": "https://github.com/NguyenVanDo51", "contributions": [ "code" ] }, { "login": "KungRaseri", "name": "KungRaseri", "avatar_url": "https://avatars.githubusercontent.com/u/1054240?v=4", "profile": "https://kungraseri.dev", "contributions": [ "doc" ] }, { "login": "BaltacMihai", "name": "Mihai-Cristian Bâltac", "avatar_url": "https://avatars.githubusercontent.com/u/72079422?v=4", "profile": "https://github.com/BaltacMihai", "contributions": [ "code" ] }, { "login": "CDeighton", "name": "Cullum Deighton", "avatar_url": "https://avatars.githubusercontent.com/u/13475443?v=4", "profile": "https://github.com/CDeighton", "contributions": [ "code" ] }, { "login": "d-skowronski", "name": "Dawid Skowroński", "avatar_url": "https://avatars.githubusercontent.com/u/98740166?v=4", "profile": "https://github.com/d-skowronski", "contributions": [ "code" ] }, { "login": "jonboiser", "name": "Jonathan Boiser", "avatar_url": "https://avatars.githubusercontent.com/u/10248067?v=4", "profile": "http://jonboiser.com", "contributions": [ "code" ] }, { "login": "benfurber", "name": "benfurber", "avatar_url": "https://avatars.githubusercontent.com/u/16688508?v=4", "profile": "https://github.com/benfurber", "contributions": [ "code", "maintenance", "doc" ] }, { "login": "AlimurtuzaCodes", "name": "Alimurtuza", "avatar_url": "https://avatars.githubusercontent.com/u/88965204?v=4", "profile": "https://github.com/AlimurtuzaCodes", "contributions": [ "code" ] }, { "login": "AliAbuSalam", "name": "Askell", "avatar_url": "https://avatars.githubusercontent.com/u/17426615?v=4", "profile": "https://github.com/AliAbuSalam", "contributions": [ "code" ] }, { "login": "manacespereira", "name": "Manacés Pereira", "avatar_url": "https://avatars.githubusercontent.com/u/8915867?v=4", "profile": "https://linkedin.com/in/manacesneto", "contributions": [ "code" ] }, { "login": "5niperspider", "name": "Georg Karl", "avatar_url": "https://avatars.githubusercontent.com/u/62932392?v=4", "profile": "https://github.com/5niperspider", "contributions": [ "code" ] }, { "login": "asdFletcher", "name": "asdFletcher", "avatar_url": "https://avatars.githubusercontent.com/u/42685363?v=4", "profile": "https://www.linkedin.com/in/fletcher-larue/", "contributions": [ "code" ] }, { "login": "bagiyal", "name": "Abhishek Bagiyal", "avatar_url": "https://avatars.githubusercontent.com/u/63339447?v=4", "profile": "https://github.com/bagiyal", "contributions": [ "code" ] }, { "login": "CrowsVeldt", "name": "Zack", "avatar_url": "https://avatars.githubusercontent.com/u/8883408?v=4", "profile": "https://github.com/CrowsVeldt", "contributions": [ "code" ] }, { "login": "benkelaar", "name": "Bart Enkelaar", "avatar_url": "https://avatars.githubusercontent.com/u/1822855?v=4", "profile": "https://careers.bol.com/", "contributions": [ "code" ] }, { "login": "laviodias", "name": "Lávio Vale", "avatar_url": "https://avatars.githubusercontent.com/u/44332001?v=4", "profile": "https://github.com/laviodias", "contributions": [ "code" ] }, { "login": "manuelrurda", "name": "Manuel Rodriguez Urdapilelta", "avatar_url": "https://avatars.githubusercontent.com/u/62727899?v=4", "profile": "https://github.com/manuelrurda", "contributions": [ "code" ] }, { "login": "LiptonB", "name": "Ben Lipton", "avatar_url": "https://avatars.githubusercontent.com/u/467965?v=4", "profile": "https://github.com/LiptonB", "contributions": [ "code" ] }, { "login": "ayachish", "name": "Ayachi Sharma", "avatar_url": "https://avatars.githubusercontent.com/u/102033230?v=4", "profile": "https://github.com/ayachish", "contributions": [ "doc" ] }, { "login": "arthurtyukayev", "name": "Arthur Tyukayev", "avatar_url": "https://avatars.githubusercontent.com/u/9029936?v=4", "profile": "https://tyukayev.com", "contributions": [ "code" ] }, { "login": "jgable", "name": "Jacob Gable", "avatar_url": "https://avatars.githubusercontent.com/u/164497?v=4", "profile": "https://github.com/jgable", "contributions": [ "code" ] }, { "login": "BeeMargarida", "name": "Ana Margarida Silva", "avatar_url": "https://avatars.githubusercontent.com/u/25725586?v=4", "profile": "https://beemargarida.github.io", "contributions": [ "code" ] }, { "login": "cjh1212", "name": "cjh1212", "avatar_url": "https://avatars.githubusercontent.com/u/45911291?v=4", "profile": "https://github.com/cjh1212", "contributions": [ "code" ] }, { "login": "pizzaisdavid", "name": "David Germain", "avatar_url": "https://avatars.githubusercontent.com/u/4391884?v=4", "profile": "https://pizzaisdavid.medium.com/", "contributions": [ "doc", "code" ] }, { "login": "ajotka", "name": "AJOTKA", "avatar_url": "https://avatars.githubusercontent.com/u/15144546?v=4", "profile": "http://ajotka.com", "contributions": [ "code" ] }, { "login": "CheRayLiu", "name": "Ray Liu", "avatar_url": "https://avatars.githubusercontent.com/u/17478640?v=4", "profile": "http://rayliu.me", "contributions": [ "code" ] }, { "login": "Erkanerkisi", "name": "Erkan Erkişi", "avatar_url": "https://avatars.githubusercontent.com/u/22741824?v=4", "profile": "http://erkanerkisi.github.io", "contributions": [ "code" ] }, { "login": "denyilm", "name": "denyilm", "avatar_url": "https://avatars.githubusercontent.com/u/65300462?v=4", "profile": "https://github.com/denyilm", "contributions": [ "code" ] }, { "login": "zweertsk", "name": "Koen", "avatar_url": "https://avatars.githubusercontent.com/u/131855633?v=4", "profile": "https://github.com/zweertsk", "contributions": [ "code" ] }, { "login": "goratt12", "name": "Guy Ribak", "avatar_url": "https://avatars.githubusercontent.com/u/23094928?v=4", "profile": "https://github.com/goratt12", "contributions": [ "code" ] }, { "login": "onim-at", "name": "Cosimo Chetta", "avatar_url": "https://avatars.githubusercontent.com/u/45094836?v=4", "profile": "https://github.com/onim-at", "contributions": [ "code" ] }, { "login": "rchick", "name": "Roger Chick", "avatar_url": "https://avatars.githubusercontent.com/u/555883?v=4", "profile": "http://uk.linkedin.com/in/rogerchick", "contributions": [ "code" ] }, { "login": "IgnasPlace", "name": "Ignas", "avatar_url": "https://avatars.githubusercontent.com/u/76262712?v=4", "profile": "http://ignasplace.com", "contributions": [ "code" ] }, { "login": "mariojsnunes", "name": "Mário Nunes", "avatar_url": "https://avatars.githubusercontent.com/u/8073622?v=4", "profile": "https://github.com/mariojsnunes", "contributions": [ "code", "maintenance" ] }, { "login": "oktomus", "name": "Kevin Masson", "avatar_url": "https://avatars.githubusercontent.com/u/4656466?v=4", "profile": "https://github.com/oktomus", "contributions": [ "code" ] }, { "login": "darigovresearch", "name": "Darigov Research", "avatar_url": "https://avatars.githubusercontent.com/u/30328618?v=4", "profile": "https://www.darigovresearch.com/", "contributions": [ "doc" ] }, { "login": "Shamauk", "name": "Zachary Doucet", "avatar_url": "https://avatars.githubusercontent.com/u/21955868?v=4", "profile": "http://cs.mcgill.ca/~zdouce", "contributions": [ "code" ] }, { "login": "viracoding", "name": "viracoding", "avatar_url": "https://avatars.githubusercontent.com/u/20618068?v=4", "profile": "https://github.com/viracoding", "contributions": [ "code" ] }, { "login": "Gashmoh", "name": "Gashmoh", "avatar_url": "https://avatars.githubusercontent.com/u/24207256?v=4", "profile": "https://github.com/Gashmoh", "contributions": [ "code" ] }, { "login": "dariusmihut", "name": "dariusmihut", "avatar_url": "https://avatars.githubusercontent.com/u/7417010?v=4", "profile": "https://github.com/dariusmihut", "contributions": [ "code" ] }, { "login": "dcustodio", "name": "David Custódio", "avatar_url": "https://avatars.githubusercontent.com/u/2907004?v=4", "profile": "https://github.com/dcustodio", "contributions": [ "code" ] }, { "login": "saile515", "name": "Elias Jörgensen", "avatar_url": "https://avatars.githubusercontent.com/u/63782477?v=4", "profile": "https://www.eliasjorgensen.se", "contributions": [ "code" ] }, { "login": "devChary", "name": "devChary", "avatar_url": "https://avatars.githubusercontent.com/u/26999371?v=4", "profile": "http://www.jagan-chary.com", "contributions": [ "code" ] }, { "login": "paposeco", "name": "Fabi", "avatar_url": "https://avatars.githubusercontent.com/u/13892562?v=4", "profile": "https://github.com/paposeco", "contributions": [ "code" ] }, { "login": "Robert-LC", "name": "Robert", "avatar_url": "https://avatars.githubusercontent.com/u/72999492?v=4", "profile": "https://github.com/Robert-LC", "contributions": [ "code" ] }, { "login": "phlapjack", "name": "Phillip Atkinson", "avatar_url": "https://avatars.githubusercontent.com/u/1590042?v=4", "profile": "https://github.com/phlapjack", "contributions": [ "code" ] }, { "login": "exabyssus", "name": "Andris", "avatar_url": "https://avatars.githubusercontent.com/u/6299387?v=4", "profile": "http://agw.lv/", "contributions": [ "code" ] }, { "login": "EdwardAndress", "name": "Edward Andress", "avatar_url": "https://avatars.githubusercontent.com/u/7963978?v=4", "profile": "https://github.com/EdwardAndress", "contributions": [ "code" ] }, { "login": "tuliobluz", "name": "Túlio Luz", "avatar_url": "https://avatars.githubusercontent.com/u/21323883?v=4", "profile": "https://github.com/tuliobluz", "contributions": [ "code" ] }, { "login": "codisart", "name": "Louis", "avatar_url": "https://avatars.githubusercontent.com/u/1767237?v=4", "profile": "https://github.com/codisart", "contributions": [ "code" ] }, { "login": "V24039", "name": "Venu G Soganadgi", "avatar_url": "https://avatars.githubusercontent.com/u/52736045?v=4", "profile": "https://venugsportfolio.netlify.app/", "contributions": [ "code" ] }, { "login": "Augustindou", "name": "Augustindou", "avatar_url": "https://avatars.githubusercontent.com/u/44368825?v=4", "profile": "https://github.com/Augustindou", "contributions": [ "code" ] }, { "login": "prashik0202", "name": "Prashik Gamre", "avatar_url": "https://avatars.githubusercontent.com/u/88095936?v=4", "profile": "https://prashikgamre.vercel.app/", "contributions": [ "code" ] }, { "login": "simontree", "name": "Robert", "avatar_url": "https://avatars.githubusercontent.com/u/59532700?v=4", "profile": "https://github.com/simontree", "contributions": [ "code" ] }, { "login": "kimskovhusandersen", "name": "Kim Skovhus Andersen", "avatar_url": "https://avatars.githubusercontent.com/u/5513342?v=4", "profile": "https://github.com/kimskovhusandersen", "contributions": [ "code" ] }, { "login": "leoasimon", "name": "leoasimon", "avatar_url": "https://avatars.githubusercontent.com/u/89898967?v=4", "profile": "https://github.com/leoasimon", "contributions": [ "code" ] }, { "login": "lucaasrojas", "name": "Lucas Rojas", "avatar_url": "https://avatars.githubusercontent.com/u/26610409?v=4", "profile": "https://lucaasrojas-portfolio.vercel.app/", "contributions": [ "code" ] }, { "login": "luismgsantos", "name": "Luís Santos", "avatar_url": "https://avatars.githubusercontent.com/u/13033016?v=4", "profile": "http://luismgsantos.github.io", "contributions": [ "code" ] }, { "login": "koeppel", "name": "Janik Köppel", "avatar_url": "https://avatars.githubusercontent.com/u/12177323?v=4", "profile": "https://github.com/koeppel", "contributions": [ "code" ] }, { "login": "Shalankwa", "name": "Jonathan Goodman", "avatar_url": "https://avatars.githubusercontent.com/u/31330598?v=4", "profile": "https://github.com/Shalankwa", "contributions": [ "code" ] }, { "login": "LahuenGR", "name": "LahuenGR", "avatar_url": "https://avatars.githubusercontent.com/u/101137877?v=4", "profile": "https://github.com/LahuenGR", "contributions": [ "code" ] }, { "login": "JoseAConcepcion", "name": "José Antonio Concepción", "avatar_url": "https://avatars.githubusercontent.com/u/99701565?v=4", "profile": "https://github.com/JoseAConcepcion", "contributions": [ "code" ] }, { "login": "jaykayudo", "name": "Joshua Kelechi", "avatar_url": "https://avatars.githubusercontent.com/u/58009744?v=4", "profile": "https://github.com/jaykayudo", "contributions": [ "code" ] }, { "login": "mchen10", "name": "Michael Chen", "avatar_url": "https://avatars.githubusercontent.com/u/16161485?v=4", "profile": "https://github.com/mchen10", "contributions": [ "code" ] }, { "login": "motuz0001", "name": "Matúš Motyka", "avatar_url": "https://avatars.githubusercontent.com/u/61076969?v=4", "profile": "https://github.com/motuz0001", "contributions": [ "code" ] }, { "login": "cypherpepe", "name": "Cypher Pepe", "avatar_url": "https://avatars.githubusercontent.com/u/125112044?v=4", "profile": "https://github.com/cypherpepe", "contributions": [ "doc" ] }, { "login": "dalibormrska", "name": "Dalibor Mrška", "avatar_url": "https://avatars.githubusercontent.com/u/35503298?v=4", "profile": "https://www.behance.net/dalibormrska", "contributions": [ "design" ] }, { "login": "sky-coderay", "name": "Skylar Ray", "avatar_url": "https://avatars.githubusercontent.com/u/137945430?v=4", "profile": "https://github.com/sky-coderay", "contributions": [ "doc" ] }, { "login": "johannes-ross", "name": "Johannes Roß", "avatar_url": "https://avatars.githubusercontent.com/u/74828657?v=4", "profile": "http://johannesross.de", "contributions": [ "code" ] }, { "login": "detrina", "name": "Devkuni", "avatar_url": "https://avatars.githubusercontent.com/u/155117116?v=4", "profile": "https://github.com/detrina", "contributions": [ "doc" ] }, { "login": "Bilogweb3", "name": "Bilog WEB3", "avatar_url": "https://avatars.githubusercontent.com/u/155262265?v=4", "profile": "https://github.com/Bilogweb3", "contributions": [ "doc" ] }, { "login": "ebradbury", "name": "Elliot Bradbury", "avatar_url": "https://avatars.githubusercontent.com/u/253679?v=4", "profile": "https://github.com/ebradbury", "contributions": [ "code" ] }, { "login": "joseh29", "name": "José", "avatar_url": "https://avatars.githubusercontent.com/u/70706814?v=4", "profile": "http://www.linkedin.com/in/joseh29", "contributions": [ "code" ] }, { "login": "maximevtush", "name": "Maxim Evtush", "avatar_url": "https://avatars.githubusercontent.com/u/154841002?v=4", "profile": "https://github.com/maximevtush", "contributions": [ "doc" ] }, { "login": "kilavvy", "name": "kilavvy", "avatar_url": "https://avatars.githubusercontent.com/u/140459108?v=4", "profile": "https://github.com/kilavvy", "contributions": [ "doc" ] }, { "login": "mohitsharma23", "name": "Mohit Sharma", "avatar_url": "https://avatars.githubusercontent.com/u/32203733?v=4", "profile": "https://github.com/mohitsharma23", "contributions": [ "code" ] }, { "login": "paulaFenner", "name": "Paula Fenner", "avatar_url": "https://avatars.githubusercontent.com/u/18422622?v=4", "profile": "https://github.com/paulaFenner", "contributions": [ "code" ] }, { "login": "ismaelabujadur", "name": "Ismael Abu-jadur Garcia", "avatar_url": "https://avatars.githubusercontent.com/u/112013216?v=4", "profile": "http://ismaelabujadur.github.io", "contributions": [ "code" ] }, { "login": "NickTheWilder", "name": "Nick Wilder", "avatar_url": "https://avatars.githubusercontent.com/u/38483425?v=4", "profile": "https://nickthewilder.com", "contributions": [ "code" ] }, { "login": "dizer-ti", "name": "James Niken", "avatar_url": "https://avatars.githubusercontent.com/u/155266991?v=4", "profile": "https://github.com/dizer-ti", "contributions": [ "code" ] }, { "login": "omahs", "name": "omahs", "avatar_url": "https://avatars.githubusercontent.com/u/73983677?v=4", "profile": "https://github.com/omahs", "contributions": [ "doc" ] }, { "login": "GTaf", "name": "GTaf", "avatar_url": "https://avatars.githubusercontent.com/u/3484782?v=4", "profile": "https://github.com/GTaf", "contributions": [ "code", "doc" ] }, { "login": "cajohn2757", "name": "Corey Johnson", "avatar_url": "https://avatars.githubusercontent.com/u/71300075?v=4", "profile": "https://github.com/cajohn2757", "contributions": [ "code" ] }, { "login": "devianweb", "name": "Ian Webster", "avatar_url": "https://avatars.githubusercontent.com/u/87659522?v=4", "profile": "https://github.com/devianweb", "contributions": [ "code" ] }, { "login": "JCSergent", "name": "JC Sergent", "avatar_url": "https://avatars.githubusercontent.com/u/34434102?v=4", "profile": "https://github.com/JCSergent", "contributions": [ "code" ] }, { "login": "fel4-dev", "name": "Fel4", "avatar_url": "https://avatars.githubusercontent.com/u/94372355?v=4", "profile": "https://github.com/fel4-dev", "contributions": [ "code" ] }, { "login": "matteobu", "name": "Matteo Bucciol", "avatar_url": "https://avatars.githubusercontent.com/u/62759388?v=4", "profile": "http://matteo.codes", "contributions": [ "code" ] }, { "login": "ernestorbemx", "name": "ernestorbemx", "avatar_url": "https://avatars.githubusercontent.com/u/204041962?v=4", "profile": "https://github.com/ernestorbemx", "contributions": [ "code" ] }, { "login": "jproberson", "name": "jproberson", "avatar_url": "https://avatars.githubusercontent.com/u/50461518?v=4", "profile": "https://github.com/jproberson", "contributions": [ "code" ] }, { "login": "Abhishek-singh88", "name": "Abhishek Singh", "avatar_url": "https://avatars.githubusercontent.com/u/177325053?v=4", "profile": "https://abhishekdotsol.vercel.app/", "contributions": [ "code" ] }, { "login": "chris-staunton", "name": "Chris Staunton", "avatar_url": "https://avatars.githubusercontent.com/u/60261694?v=4", "profile": "https://github.com/chris-staunton", "contributions": [ "code" ] }, { "login": "rejected-l", "name": "Rej Ect", "avatar_url": "https://avatars.githubusercontent.com/u/99460023?v=4", "profile": "https://github.com/rejected-l", "contributions": [ "doc" ] }, { "login": "othman-shamla", "name": "othman-shamla", "avatar_url": "https://avatars.githubusercontent.com/u/16326221?v=4", "profile": "https://github.com/othman-shamla", "contributions": [ "code" ] }, { "login": "rsgb", "name": "RSGB", "avatar_url": "https://avatars.githubusercontent.com/u/96707736?v=4", "profile": "https://github.com/rsgb", "contributions": [ "code" ] }, { "login": "GMetaxakis", "name": "Georgios Metaxakis", "avatar_url": "https://avatars.githubusercontent.com/u/4234419?v=4", "profile": "https://github.com/GMetaxakis", "contributions": [ "code" ] }, { "login": "tmchow", "name": "Trevin Chow", "avatar_url": "https://avatars.githubusercontent.com/u/517103?v=4", "profile": "https://trev.in", "contributions": [ "code" ] }, { "login": "elbotho", "name": "Botho", "avatar_url": "https://avatars.githubusercontent.com/u/1258870?v=4", "profile": "https://botho.cc", "contributions": [ "code" ] } ], "projectName": "community-platform", "projectOwner": "ONEARMY", "repoType": "github", "repoHost": "https://github.com", "commitConvention": "angular", "commitType": "docs" } ================================================ FILE: .circleci/config.yml ================================================ version: 2.1 ###################################################################################################### # Pre-Requisites # # In order to use these scripts various env variables need to be set on CircleCI # See `packages/documentation/docs/Deployment/circle-ci.md` for more information # # For general config info see: https://circleci.com/docs/2.0/configuration-reference ###################################################################################################### ###################################################################################################### # Orbs - preconfigured environments for running specific jobs ###################################################################################################### orbs: # for use with cimg image, to install web browsers browser-tools: circleci/browser-tools@1.4.5 # used to track coverage codecov: codecov/codecov@3.2.5 ###################################################################################################### # Aliases - code snippets that can be included inline in any other markup ###################################################################################################### aliases: # use a base image running node v18 with chrome/firefox browsers preinstalled # This can be applied to any job via `docker: *docker` syntax - &docker - image: cimg/node:22.18.0-browsers # Use base image with support for node version parameter and matrix # This can be applied to any job via `<<: *docker_matrix` syntax - docker_matrix: &docker_matrix parameters: node-version: type: string default: 22.18.0-browsers docker: - image: cimg/node:<< parameters.node-version >> # These can also be created as commands, but slighly tidier to just use inline # restore/install/save can all be done with a single circle-ci orb, but less flexible and breaks intellisense - &install_bun run: name: Install Bun command: curl -fsSL https://bun.sh/install | bash && echo 'export PATH="$HOME/.bun/bin:$PATH"' >> $BASH_ENV - &restore_bun_cache restore_cache: name: Restore bun cache keys: - v1-bun-{{ checksum "bun.lock" }} - v1-bun- - &install_packages run: name: Install Packages command: bun install --frozen-lockfile - &save_bun_cache save_cache: paths: - /home/circleci/.bun/install/cache key: v1-bun-{{ checksum "bun.lock" }} - &filter_only_default_branch filters: branches: only: - master ###################################################################################################### # Commands - Reusable collections of steps ###################################################################################################### commands: setup_repo: description: checkout repo and install packages # no parameters currently used, but could be specified here to use within steps # parameters: steps: - checkout - *install_bun - *restore_bun_cache - *install_packages - *save_bun_cache ###################################################################################################### # Jobs - Independently specified lists of tasks and environments for execution ###################################################################################################### jobs: lint: docker: *docker resource_class: medium+ environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo - run: command: bun run lint - run: command: bun run --filter oa-components lint # Prepare node module caches so that future tasks run more quickly # NOTE - not currently used as we only have one workflow setup: docker: *docker environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo # Create a production build # NOTE - not currently used in test workflow as different build_env required for each machine test_unit: docker: *docker resource_class: medium+ environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo - run: # NOTE - run-in-band to try reduce memory leaks (https://github.com/facebook/jest/issues/7874) command: bun run test:unit - run: command: bun run test:components - store_artifacts: path: coverage - store_artifacts: path: packages/components/coverage - codecov/upload - store_artifacts: path: packages/components/reports - store_test_results: path: packages/components/reports - store_artifacts: path: reports - store_test_results: path: reports build: <<: *docker_matrix environment: GENERATE_SOURCEMAP: 'false' SKIP_PREFLIGHT_CHECK: 'true' NODE_OPTIONS: '--max-old-space-size=5632' CYPRESS_INSTALL_BINARY: 0 # If experiencing out-of-memory issues can increase resource_class below and max space size above # https://circleci.com/docs/2.0/configuration-reference/#resourceclass resource_class: large steps: - setup_repo # As environment variables can only be set from strings add additional dynamic variable mappings here # https://discuss.circleci.com/t/using-environment-variables-in-config-yml-not-working/14237/13 - run: name: Set branch environment command: | echo 'export VITE_SITE_VARIANT=test-ci' >> $BASH_ENV - run: name: Check environment variables command: | echo $VITE_SITE_VARIANT - run: command: bun run build - persist_to_workspace: root: . paths: - build storybook: docker: *docker resource_class: medium environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo - attach_workspace: at: '.' - run: command: bun run storybook:build deploy: docker: - image: cimg/node:22.18.0 resource_class: medium+ parameters: # optional environment variables to set during build process DEPLOY_ALIAS: type: string default: 'default' FLY_APP_NAME: type: string default: 'default' FLY_TOML: type: string default: 'default' environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo - attach_workspace: at: '.' - run: name: Prune Docker resources command: | docker system prune -a - run: name: Install fly command command: curl -L https://fly.io/install.sh | sh -s -- --non-interactive --setup-path - run: name: Add fly to PATH command: echo "export PATH=\"/home/circleci/.fly/bin:$PATH\"" >> $BASH_ENV - run: name: Login fly command: flyctl auth token $FLY_API_TOKEN --debug --verbose - run: name: Deploy to fly command: | flyctl deploy \ --app << parameters.FLY_APP_NAME >> \ --config << parameters.FLY_TOML >> \ --debug --verbose \ --build-secret NODE_ENV="production" \ --build-secret VITE_BRANCH="$VITE_BRANCH" \ --build-secret VITE_SENTRY_DSN="$VITE_SENTRY_DSN" - run: name: Set Server Secrets command: flyctl -a << parameters.FLY_APP_NAME >> secrets set SUPABASE_API_URL=$SUPABASE_API_URL SUPABASE_KEY=$SUPABASE_KEY SUPABASE_SERVICE_ROLE_KEY=$SUPABASE_SERVICE_ROLE_KEY DISCORD_WEBHOOK_URL=$DISCORD_WEBHOOK_URL RESEND_API_KEY=$RESEND_API_KEY TENANT_ID=$TENANT_ID PATREON_CLIENT_SECRET=$PATREON_CLIENT_SECRET TOKEN_SECRET="$TOKEN_SECRET" # deploy-supabase: # docker: # - image: cimg/node:20.7.0 # steps: # - checkout # - attach_workspace: # at: '.' # - run: npx supabase@2.6.8 login --token $SUPABASE_ACCESS_TOKEN # - run: (yes || true) | npx supabase@2.6.8 db push --db-url $SUPABASE_DB_URL --debug --password $SUPABASE_DB_PASSWORD # - run: npx supabase@2.6.8 functions deploy --no-verify-jwt # Run cypress e2e tests on chrome test_e2e: docker: *docker resource_class: medium+ # build matrix will run 4 parallel builds handled by cypress, so don't need to specify more here parallelism: 1 parameters: CI_NODE: type: integer CI_BROWSER: type: string steps: - setup_repo # retrieve build folder - attach_workspace: at: '.' # install testing browsers are required - when: condition: equal: ['chrome', << parameters.CI_BROWSER >>] steps: - browser-tools/install-chrome - when: condition: equal: ['firefox', << parameters.CI_BROWSER >>] steps: - browser-tools/install-firefox # call main testing script - run: command: npm run test ci prod environment: VITE_SITE_VARIANT: test-ci CI_BROWSER: << parameters.CI_BROWSER >> CI_NODE: << parameters.CI_NODE >> CI_GROUP: ci-<< parameters.CI_BROWSER >> - store_artifacts: path: ./packages/cypress/src/screenshots/ release: docker: *docker resource_class: medium+ environment: CYPRESS_INSTALL_BINARY: 0 steps: - setup_repo - attach_workspace: at: '.' - run: command: npx semantic-release@22 ###################################################################################################### # Workflows - Collections of jobs to define overall processes ###################################################################################################### workflows: version: 2 main_workflow: max_auto_reruns: 1 # by default jobs will run concurrently, so specify requires if want to run sequentially jobs: - lint: name: Lint code #---------------------- Test ---------------------- # Note - when calling test we also let the test script handle building as it injects random variables for seeding the DB - build: requires: - 'Lint code' name: Build Application - test_unit: name: 'Unit tests' requires: - 'Lint code' - storybook: name: Build Storybook requires: - 'Lint code' - test_e2e: name: e2e-<< matrix.CI_BROWSER >>-<< matrix.CI_NODE >> requires: - 'Build Application' - 'Unit tests' - 'Build Storybook' context: - e2e-tests matrix: parameters: CI_NODE: [1, 2, 3, 4] CI_BROWSER: ['chrome'] #---------------------- Approval ---------------------- - approve: type: approval name: 'Approve Production deployment' requires: - test_e2e <<: *filter_only_default_branch #---------------------- Deploy ---------------------- # - deploy-supabase: # name: 'Deploy to Supabase' # requires: # - 'Approve Production deployment' # <<: *filter_only_default_branch # context: # - supabase-deploy - deploy: name: 'Deploy: community.fixing.fashion' requires: - 'Approve Production deployment' <<: *filter_only_default_branch DEPLOY_ALIAS: fixing-fashion-prod FLY_APP_NAME: community-platform-ff FLY_TOML: fly-ff.toml context: - circle-ci-patreon-context - fixing-fashion-prod - fly-deploy - supabase-deploy - deploy: name: 'Deploy: community.preciousplastic.com' requires: - 'Approve Production deployment' <<: *filter_only_default_branch DEPLOY_ALIAS: 'production' FLY_APP_NAME: community-platform-pp FLY_TOML: fly-pp.toml context: - circle-ci-patreon-context - community-platform-production - fly-deploy - supabase-deploy - deploy: name: 'Deploy: community.projectkamp.com' requires: - 'Approve Production deployment' <<: *filter_only_default_branch DEPLOY_ALIAS: project-kamp-production FLY_APP_NAME: community-platform-pk FLY_TOML: fly-pk.toml context: - circle-ci-patreon-context - project-kamp-production - fly-deploy - supabase-deploy - release: name: Release new version to GitHub context: - release-context requires: - 'Deploy: community.preciousplastic.com' <<: *filter_only_default_branch ================================================ FILE: .dockerignore ================================================ *node_modules* dump build .circleci .github .nxs .yarn .yarnrc.yml yarn.lock docs packages/cypress packages/documentation # Build artifacts and caches coverage reports *.log .env.local .env.*.local # Storybook **/storybook-static # Git .git .gitignore # IDE .vscode .idea *.swp *.swo # Testing *.test.ts *.test.tsx *.spec.ts *.spec.tsx ================================================ FILE: .github/CODEOWNERS ================================================ * @ONEARMY/maintainers ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '[bug]' labels: "Type:Bug\U0001F41B" assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behaviour: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behaviour** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/build-new-component.md ================================================ --- name: Build new component about: Describe the new component you want to build title: '' labels: '' assignees: '' --- ## Component infos ### Description Write a quick description of what is the component for, and as mainy details you consider useful to the person that will have to build it. ### Page related Will be used in : - Related page name (#issue-number) ### Mockup [Add here the mockup of the component] ### Example(s) If you have any examples of existing similar components, add the link here. It can be a link to a code snippet and/or to a page of a projet/website where we can interact with the similar component. ### Build suggestion Here describe how you would build it, briefly but clearly. For example : the component MyComponent will take 2 props : `prop1`& `prop2`. It will extend the existing `Heading` component. ================================================ FILE: .github/ISSUE_TEMPLATE/feature-request-or-suggestion.md ================================================ --- name: Feature request or suggestion about: Suggest an idea for this project title: '[feature request]' labels: 'Feedback: Question' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/actions/destroy-fly-preview-app/Dockerfile ================================================ FROM alpine RUN apk add --no-cache curl jq RUN curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh COPY entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ================================================ FILE: .github/actions/destroy-fly-preview-app/action.yml ================================================ name: "Destroy fly.io app" description: "Destroy fly.io app if matching app name found" author: iSCJT branding: icon: "delete" color: "red" runs: using: "docker" image: "Dockerfile" inputs: name: description: Fly app name ================================================ FILE: .github/actions/destroy-fly-preview-app/entrypoint.sh ================================================ #!/bin/sh -l set -ex # Change underscores to hyphens. app="${INPUT_NAME//_/-}" if ! flyctl status --app "$app"; then echo "App name not found" exit 1 fi flyctl apps destroy "$app" -y echo "App $app successfully destroyed" exit 0 ================================================ FILE: .github/labels.yml ================================================ # This is currently just an export of labels used in the project # In the future we could also use to add more and keep in sync via actions such as: # https://github.com/micnncim/action-label-syncer [ { 'name': 'Backend', 'color': 'EF592F', 'description': '' }, { 'name': 'Code: Tidying', 'color': 'B2864C', 'description': '' }, { 'name': 'Design', 'color': '2FF7AB', 'description': null }, { 'name': 'Difficulty:Easy', 'color': 'FFDDB2', 'description': '' }, { 'name': 'Difficulty:Hard', 'color': 'C198E2', 'description': '' }, { 'name': 'Difficulty:Super-Easy', 'color': 'C2E0C6', 'description': '' }, { 'name': 'Difficulty:medium', 'color': '629AFC', 'description': '' }, { 'name': 'Discussions', 'color': 'CC317C', 'description': '' }, { 'name': 'Documentation', 'color': 'E0D323', 'description': '' }, { 'name': 'Frontend', 'color': '5319E7', 'description': '' }, { 'name': 'Global Good 🌍', 'color': 'B3E572', 'description': '' }, { 'name': 'Good first issue', 'color': '74B5E3', 'description': null }, { 'name': 'Help wanted', 'color': '56D639', 'description': '' }, { 'name': 'In progress', 'color': '440B89', 'description': '' }, { 'name': 'Mod: DevOps 🤖', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Discussions 💬', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Library 📰', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Maps 🗺', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Other ⬜️', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Profiles 👱', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Research 🔬', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Mod: Security👮', 'color': 'BFD4F2', 'description': '' }, { 'name': 'Module Overview 👀', 'color': 'CEF45D', 'description': '' }, { 'name': 'Non-Dev', 'color': 'ADADAD', 'description': '' }, { 'name': 'Priority: High❕', 'color': 'FFB266', 'description': '' }, { 'name': 'Priority: Low', 'color': 'C2E0C6', 'description': '' }, { 'name': 'Priority: Medium', 'color': 'FFFF00', 'description': '' }, { 'name': 'Priority: Urgent❕❕❕', 'color': 'FF0000', 'description': '' }, { 'name': 'Review: Assigned 👉', 'color': 'D7C0A1', 'description': 'Waiting on review from a specific dev', }, { 'name': 'Review: Changes Requested 🗨️', 'color': 'D7C0A1', 'description': 'Code reviewed, pending update to changes requested', }, { 'name': 'Review allow-preview ✅', 'color': 'FFF', 'description': 'Has received manual check for malicious code and can be safely built for preview', }, ] ================================================ FILE: .github/pull_request_template.md ================================================ ## PR Checklist - [ ] - Unit and/or e2e tests for the changes that have been added (for bug fixes / features) ## What kind of change does this PR introduce? - [ ] 🐛 Bugfix — fixes incorrect behavior without changing functionality - [ ] ✨ Feature — adds new functionality - [ ] ♻️ Refactoring — improves code structure with no functional changes - [ ] ⚡️ Performance — improves speed, memory, or efficiency - [ ] 🧪 Tests — adds or updates tests only - [ ] 🔧 Tools / CI — changes to build, deploy, or developer tooling - [ ] 📝 Documentation — updates docs, comments, or READMEs - [ ] 📦 Dependencies — upgrades, downgrades, or removes packages - [ ] 🔖 Other: ## What is the new behavior? _Describe the new behaviour_ _If useful, provide screenshot or capture to highlight main changes_ ## Does this PR introduce a DB Schema Change or Migration? - [ ] Yes - [ ] No ## Git Issues Closes # ## What happens next? Thank you for the contribution! We will review it ASAP. If you need more immediate feedback you can reach out to us on Discord in the [Community Platform `development` channel](https://discord.com/channels/586676777334865928/938781727017558018). ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ name: 'CodeQL' on: push: branches: [master] pull_request: branches: [master] schedule: - cron: '35 21 * * 2' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: ['javascript-typescript'] steps: - name: Checkout repository uses: actions/checkout@v4 - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 with: category: '/language:${{ matrix.language }}' ================================================ FILE: .github/workflows/pr-preview-fly-deploy.yml ================================================ name: Deploy Fly PR Preview on: pull_request_target: types: [labeled, synchronize] env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} FLY_REGION: ams FLY_ORG: one-army SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} SUPABASE_DB_PASSWORD: ${{ secrets.PREVIEW_DB_PASSWORD }} SUPABASE_PROJECT_ID: ${{ secrets.PREVIEW_PROJECT_ID }} FLY_APP_NAME: community-platform-pr-${{ github.event.number }} jobs: preview_app: if: contains(github.event.pull_request.labels.*.name, 'Review allow-preview ✅') runs-on: ubuntu-latest continue-on-error: false outputs: url: ${{ steps.deploy.outputs.url }} concurrency: group: pr-${{ github.event.number }}-${{ github.sha }} environment: name: preview url: ${{ steps.deploy.outputs.url }} steps: - name: Get code uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} - name: Install Fly CLI run: | curl -L https://fly.io/install.sh | sh echo "$HOME/.fly/bin" >> "$GITHUB_PATH" - name: Deploy PR app to Fly.io id: deploy uses: superfly/fly-pr-review-apps@1.5.0 with: config: fly-preview.toml name: community-platform-pr-${{ github.event.number }} args: --build-arg COMMIT_SHA=${{ github.event.pull_request.head.sha }} secrets: | SUPABASE_API_URL=${{ secrets.SUPABASE_API_URL }} SUPABASE_KEY=${{ secrets.SUPABASE_KEY }} SUPABASE_SERVICE_ROLE_KEY=${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} RESEND_API_KEY=${{ secrets.RESEND_API_KEY }} TENANT_ID=precious-plastic ================================================ FILE: .github/workflows/pr-preview-fly-destroy.yml ================================================ name: Destroy Fly PR Preview on: pull_request_target: types: - unlabeled - closed env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} jobs: label_removed: if: | (github.event.action == 'unlabeled' && github.event.label.name == 'Review allow-preview ✅') || (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'Review allow-preview ✅')) runs-on: ubuntu-latest continue-on-error: true concurrency: group: pr-${{ github.event.number }} steps: - name: Checkout uses: actions/checkout@v4 - name: Destroy fly.io preview app id: destroy uses: ./.github/actions/destroy-fly-preview-app with: name: community-platform-pr-${{ github.event.number }} ================================================ FILE: .github/workflows/pr-preview-remove-label.yml ================================================ name: Remove PR Preview Label on: # Run this workflow on every PR event. Existing review apps will be updated when the PR is updated. pull_request_target: # Trigger when labels are changed or more commits added to a PR that contains labels types: [closed] jobs: preview_app: if: contains(github.event.pull_request.labels.*.name, 'Review allow-preview ✅') runs-on: ubuntu-latest continue-on-error: true # Only run one deployment at a time per PR. concurrency: group: pr-${{ github.event.number }} steps: - name: Get code uses: actions/checkout@v4 with: # pull the repo from the pull request source, not the default local repo ref: ${{ github.event.pull_request.head.sha }} - name: Remove preview label uses: actions/github-script@v7 with: script: | await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: github.event.number, name: 'Review allow-preview ✅', }); env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/pr-stale.yml ================================================ name: 'Close stale issues and PRs' on: schedule: - cron: '30 1 * * *' jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v8 with: stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.' close-pr-message: 'This PR was closed because it has been stalled for 5 days with no activity.' days-before-pr-stale: 45 days-before-pr-close: 5 debug-only: true ================================================ FILE: .github/workflows/storybook-deploy.yml ================================================ # Build and deploy Storybook to GitHub Pages name: Storybook Deploy on: push: branches: - master # Only run action if changes have been made to the components or themes packages paths: - 'packages/components/**' - 'packages/themes/**' - '.github/workflows/storybook-deploy.yml' # Allow manual trigger workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write id-token: write # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: group: 'pages' cancel-in-progress: false jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22.18.0' - name: Install Bun uses: oven-sh/setup-bun@v2 - name: Setup Cache uses: actions/cache@v4 id: bun-cache with: path: ~/.bun/install/cache key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }} restore-keys: | ${{ runner.os }}-bun- - name: Install dependencies run: bun install --frozen-lockfile - name: Build themes run: bun run --filter oa-themes build - name: Build Storybook run: bun run --filter oa-components build:sb - name: Setup Pages uses: actions/configure-pages@v4 - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: path: ./packages/components/storybook-static deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 ================================================ FILE: .gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies node_modules # testing /coverage /reports /junit.xml # production /build /lib # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* .vscode/* !.vscode/launch.json *.log .vs # IntelliJ /.idea # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored .yarn/* !.yarn/patches !.yarn/releases !.yarn/plugins !.yarn/sdks !.yarn/versions .pnp.* *.tsbuildinfo supabase/.branches supabase/.temp supabase/.env .react-router/ ================================================ FILE: .husky/pre-commit ================================================ bun run lint-staged ================================================ FILE: .node-version ================================================ 22.18.0 ================================================ FILE: .releaserc.json ================================================ { "branches": ["master"], "ci": false, "plugins": [ [ "@semantic-release/commit-analyzer", { "preset": "angular", "releaseRules": [ { "type": "docs", "scope": "README", "release": "patch" }, { "type": "refactor", "release": "patch" }, { "type": "style", "release": "patch" } ], "parserOpts": { "noteKeywords": ["MAJOR VERSION", "MAJOR VERSIONS"] } } ], "@semantic-release/release-notes-generator", "@semantic-release/github", "@semantic-release/changelog" ] } ================================================ FILE: .snaplet/config.json ================================================ { "adapter": "pg" } ================================================ FILE: .snaplet/dataModel.json ================================================ { "models": { "_http_response": { "id": "net._http_response", "schemaName": "net", "tableName": "_http_response", "fields": [ { "id": "net._http_response.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.status_code", "name": "status_code", "columnName": "status_code", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.content_type", "name": "content_type", "columnName": "content_type", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.headers", "name": "headers", "columnName": "headers", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.content", "name": "content", "columnName": "content", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.timed_out", "name": "timed_out", "columnName": "timed_out", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.error_msg", "name": "error_msg", "columnName": "error_msg", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net._http_response.created", "name": "created", "columnName": "created", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "audit_log_entries": { "id": "auth.audit_log_entries", "schemaName": "auth", "tableName": "audit_log_entries", "fields": [ { "id": "auth.audit_log_entries.instance_id", "name": "instance_id", "columnName": "instance_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.audit_log_entries.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.audit_log_entries.payload", "name": "payload", "columnName": "payload", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.audit_log_entries.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.audit_log_entries.ip_address", "name": "ip_address", "columnName": "ip_address", "type": "varchar", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": 64 } ], "uniqueConstraints": [] }, "banners": { "id": "public.banners", "schemaName": "public", "tableName": "banners", "fields": [ { "id": "public.banners.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"banners_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.banners.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.banners.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.banners.text", "name": "text", "columnName": "text", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.banners.url", "name": "url", "columnName": "url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.banners.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "banners_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "buckets": { "id": "storage.buckets", "schemaName": "storage", "tableName": "buckets", "fields": [ { "id": "storage.buckets.id", "name": "id", "columnName": "id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "storage.buckets.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets.owner", "name": "owner", "columnName": "owner", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets.public", "name": "public", "columnName": "public", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets.avif_autodetection", "name": "avif_autodetection", "columnName": "avif_autodetection", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets.file_size_limit", "name": "file_size_limit", "columnName": "file_size_limit", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets.allowed_mime_types", "name": "allowed_mime_types", "columnName": "allowed_mime_types", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets.owner_id", "name": "owner_id", "columnName": "owner_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets.type", "name": "type", "columnName": "type", "type": "buckettype", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "objects", "type": "objects", "isRequired": false, "kind": "object", "relationName": "objectsTobuckets", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "s3_multipart_uploads", "type": "s3_multipart_uploads", "isRequired": false, "kind": "object", "relationName": "s3_multipart_uploadsTobuckets", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "s3_multipart_uploads_parts", "type": "s3_multipart_uploads_parts", "isRequired": false, "kind": "object", "relationName": "s3_multipart_uploads_partsTobuckets", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "bname", "fields": ["name"], "nullNotDistinct": false } ] }, "buckets_analytics": { "id": "storage.buckets_analytics", "schemaName": "storage", "tableName": "buckets_analytics", "fields": [ { "id": "storage.buckets_analytics.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.buckets_analytics.type", "name": "type", "columnName": "type", "type": "buckettype", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_analytics.format", "name": "format", "columnName": "format", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_analytics.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_analytics.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_analytics.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.buckets_analytics.deleted_at", "name": "deleted_at", "columnName": "deleted_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "iceberg_namespaces", "type": "iceberg_namespaces", "isRequired": false, "kind": "object", "relationName": "iceberg_namespacesTobuckets_analytics", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "iceberg_tables", "type": "iceberg_tables", "isRequired": false, "kind": "object", "relationName": "iceberg_tablesTobuckets_analytics", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "buckets_analytics_unique_name_idx", "fields": ["name"], "nullNotDistinct": false } ] }, "buckets_vectors": { "id": "storage.buckets_vectors", "schemaName": "storage", "tableName": "buckets_vectors", "fields": [ { "id": "storage.buckets_vectors.id", "name": "id", "columnName": "id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "storage.buckets_vectors.type", "name": "type", "columnName": "type", "type": "buckettype", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_vectors.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.buckets_vectors.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "vector_indexes", "type": "vector_indexes", "isRequired": false, "kind": "object", "relationName": "vector_indexesTobuckets_vectors", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "categories": { "id": "public.categories", "schemaName": "public", "tableName": "categories", "fields": [ { "id": "public.categories.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"categories_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.categories.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.categories.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.categories.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.categories.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.categories.type", "name": "type", "columnName": "type", "type": "content_types", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "news", "type": "news", "isRequired": false, "kind": "object", "relationName": "newsTocategories", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "projects", "type": "projects", "isRequired": false, "kind": "object", "relationName": "projectsTocategories", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "questions", "type": "questions", "isRequired": false, "kind": "object", "relationName": "questionsTocategories", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "research", "type": "research", "isRequired": false, "kind": "object", "relationName": "researchTocategories", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "categories_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "comments": { "id": "public.comments", "schemaName": "public", "tableName": "comments", "fields": [ { "id": "public.comments.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"comments_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.comments.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.comments.comment", "name": "comment", "columnName": "comment", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.source_id", "name": "source_id", "columnName": "source_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.parent_id", "name": "parent_id", "columnName": "parent_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.comments.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.source_type", "name": "source_type", "columnName": "source_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.source_id_legacy", "name": "source_id_legacy", "columnName": "source_id_legacy", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.comments.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "commentsToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "comments_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "custom_oauth_providers": { "id": "auth.custom_oauth_providers", "schemaName": "auth", "tableName": "custom_oauth_providers", "fields": [ { "id": "auth.custom_oauth_providers.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "auth.custom_oauth_providers.provider_type", "name": "provider_type", "columnName": "provider_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.identifier", "name": "identifier", "columnName": "identifier", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.client_id", "name": "client_id", "columnName": "client_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.client_secret", "name": "client_secret", "columnName": "client_secret", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.acceptable_client_ids", "name": "acceptable_client_ids", "columnName": "acceptable_client_ids", "type": "text[]", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.scopes", "name": "scopes", "columnName": "scopes", "type": "text[]", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.pkce_enabled", "name": "pkce_enabled", "columnName": "pkce_enabled", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.attribute_mapping", "name": "attribute_mapping", "columnName": "attribute_mapping", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.authorization_params", "name": "authorization_params", "columnName": "authorization_params", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.enabled", "name": "enabled", "columnName": "enabled", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.email_optional", "name": "email_optional", "columnName": "email_optional", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.issuer", "name": "issuer", "columnName": "issuer", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.discovery_url", "name": "discovery_url", "columnName": "discovery_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.skip_nonce_check", "name": "skip_nonce_check", "columnName": "skip_nonce_check", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.cached_discovery", "name": "cached_discovery", "columnName": "cached_discovery", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.discovery_cached_at", "name": "discovery_cached_at", "columnName": "discovery_cached_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.authorization_url", "name": "authorization_url", "columnName": "authorization_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.token_url", "name": "token_url", "columnName": "token_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.userinfo_url", "name": "userinfo_url", "columnName": "userinfo_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.jwks_uri", "name": "jwks_uri", "columnName": "jwks_uri", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.custom_oauth_providers.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "custom_oauth_providers_identifier_key", "fields": ["identifier"], "nullNotDistinct": false } ] }, "extensions": { "id": "_realtime.extensions", "schemaName": "_realtime", "tableName": "extensions", "fields": [ { "id": "_realtime.extensions.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "_realtime.extensions.type", "name": "type", "columnName": "type", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.extensions.settings", "name": "settings", "columnName": "settings", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.extensions.tenant_external_id", "name": "tenant_external_id", "columnName": "tenant_external_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.extensions.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.extensions.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "tenants", "type": "tenants", "isRequired": false, "kind": "object", "relationName": "extensionsTotenants", "relationFromFields": ["tenant_external_id"], "relationToFields": ["external_id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "extensions_tenant_external_id_type_index", "fields": ["tenant_external_id", "type"], "nullNotDistinct": false } ] }, "flow_state": { "id": "auth.flow_state", "schemaName": "auth", "tableName": "flow_state", "fields": [ { "id": "auth.flow_state.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.flow_state.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.auth_code", "name": "auth_code", "columnName": "auth_code", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.code_challenge_method", "name": "code_challenge_method", "columnName": "code_challenge_method", "type": "code_challenge_method", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.code_challenge", "name": "code_challenge", "columnName": "code_challenge", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.provider_type", "name": "provider_type", "columnName": "provider_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.provider_access_token", "name": "provider_access_token", "columnName": "provider_access_token", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.provider_refresh_token", "name": "provider_refresh_token", "columnName": "provider_refresh_token", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.authentication_method", "name": "authentication_method", "columnName": "authentication_method", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.auth_code_issued_at", "name": "auth_code_issued_at", "columnName": "auth_code_issued_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.invite_token", "name": "invite_token", "columnName": "invite_token", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.referrer", "name": "referrer", "columnName": "referrer", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.oauth_client_state_id", "name": "oauth_client_state_id", "columnName": "oauth_client_state_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.linking_target_id", "name": "linking_target_id", "columnName": "linking_target_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.flow_state.email_optional", "name": "email_optional", "columnName": "email_optional", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "saml_relay_states", "type": "saml_relay_states", "isRequired": false, "kind": "object", "relationName": "saml_relay_statesToflow_state", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "hooks": { "id": "supabase_functions.hooks", "schemaName": "supabase_functions", "tableName": "hooks", "fields": [ { "id": "supabase_functions.hooks.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"supabase_functions\".\"hooks_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "supabase_functions.hooks.hook_table_id", "name": "hook_table_id", "columnName": "hook_table_id", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "supabase_functions.hooks.hook_name", "name": "hook_name", "columnName": "hook_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "supabase_functions.hooks.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "supabase_functions.hooks.request_id", "name": "request_id", "columnName": "request_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "hooks_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "http_request_queue": { "id": "net.http_request_queue", "schemaName": "net", "tableName": "http_request_queue", "fields": [ { "id": "net.http_request_queue.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"net\".\"http_request_queue_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "net.http_request_queue.method", "name": "method", "columnName": "method", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net.http_request_queue.url", "name": "url", "columnName": "url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net.http_request_queue.headers", "name": "headers", "columnName": "headers", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net.http_request_queue.body", "name": "body", "columnName": "body", "type": "bytea", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "net.http_request_queue.timeout_milliseconds", "name": "timeout_milliseconds", "columnName": "timeout_milliseconds", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "iceberg_namespaces": { "id": "storage.iceberg_namespaces", "schemaName": "storage", "tableName": "iceberg_namespaces", "fields": [ { "id": "storage.iceberg_namespaces.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.iceberg_namespaces.bucket_name", "name": "bucket_name", "columnName": "bucket_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_namespaces.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_namespaces.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.iceberg_namespaces.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.iceberg_namespaces.metadata", "name": "metadata", "columnName": "metadata", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.iceberg_namespaces.catalog_id", "name": "catalog_id", "columnName": "catalog_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "buckets_analytics", "type": "buckets_analytics", "isRequired": true, "kind": "object", "relationName": "iceberg_namespacesTobuckets_analytics", "relationFromFields": ["catalog_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "iceberg_tables", "type": "iceberg_tables", "isRequired": false, "kind": "object", "relationName": "iceberg_tablesToiceberg_namespaces", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "idx_iceberg_namespaces_bucket_id", "fields": ["catalog_id", "name"], "nullNotDistinct": false } ] }, "iceberg_tables": { "id": "storage.iceberg_tables", "schemaName": "storage", "tableName": "iceberg_tables", "fields": [ { "id": "storage.iceberg_tables.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.iceberg_tables.namespace_id", "name": "namespace_id", "columnName": "namespace_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.bucket_name", "name": "bucket_name", "columnName": "bucket_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.location", "name": "location", "columnName": "location", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.remote_table_id", "name": "remote_table_id", "columnName": "remote_table_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.shard_key", "name": "shard_key", "columnName": "shard_key", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.shard_id", "name": "shard_id", "columnName": "shard_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.iceberg_tables.catalog_id", "name": "catalog_id", "columnName": "catalog_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "buckets_analytics", "type": "buckets_analytics", "isRequired": true, "kind": "object", "relationName": "iceberg_tablesTobuckets_analytics", "relationFromFields": ["catalog_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "iceberg_namespaces", "type": "iceberg_namespaces", "isRequired": true, "kind": "object", "relationName": "iceberg_tablesToiceberg_namespaces", "relationFromFields": ["namespace_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "idx_iceberg_tables_location", "fields": ["location"], "nullNotDistinct": false }, { "name": "idx_iceberg_tables_namespace_id", "fields": ["catalog_id", "name", "namespace_id"], "nullNotDistinct": false } ] }, "identities": { "id": "auth.identities", "schemaName": "auth", "tableName": "identities", "fields": [ { "id": "auth.identities.provider_id", "name": "provider_id", "columnName": "provider_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.identity_data", "name": "identity_data", "columnName": "identity_data", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.provider", "name": "provider", "columnName": "provider", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.last_sign_in_at", "name": "last_sign_in_at", "columnName": "last_sign_in_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.email", "name": "email", "columnName": "email", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.identities.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "name": "users", "type": "users", "isRequired": true, "kind": "object", "relationName": "identitiesTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "identities_provider_id_provider_unique", "fields": ["provider", "provider_id"], "nullNotDistinct": false } ] }, "instances": { "id": "auth.instances", "schemaName": "auth", "tableName": "instances", "fields": [ { "id": "auth.instances.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.instances.uuid", "name": "uuid", "columnName": "uuid", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.instances.raw_base_config", "name": "raw_base_config", "columnName": "raw_base_config", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.instances.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.instances.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "map_pins": { "id": "public.map_pins", "schemaName": "public", "tableName": "map_pins", "fields": [ { "id": "public.map_pins.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"map_pins_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.map_pins.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.map_pins.profile_id", "name": "profile_id", "columnName": "profile_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.country", "name": "country", "columnName": "country", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.country_code", "name": "country_code", "columnName": "country_code", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.administrative", "name": "administrative", "columnName": "administrative", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.post_code", "name": "post_code", "columnName": "post_code", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.lat", "name": "lat", "columnName": "lat", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.lng", "name": "lng", "columnName": "lng", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.moderation", "name": "moderation", "columnName": "moderation", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.moderation_feedback", "name": "moderation_feedback", "columnName": "moderation_feedback", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_pins.name", "name": "name", "columnName": "name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "map_pinsToprofiles", "relationFromFields": ["profile_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "map_pins_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "map_settings": { "id": "public.map_settings", "schemaName": "public", "tableName": "map_settings", "fields": [ { "id": "public.map_settings.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"map_settings_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.map_settings.default_type_filters", "name": "default_type_filters", "columnName": "default_type_filters", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_settings.setting_filters", "name": "setting_filters", "columnName": "setting_filters", "type": "text[]", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.map_settings.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "map_settings_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "public_messages": { "id": "public.messages", "schemaName": "public", "tableName": "messages", "fields": [ { "id": "public.messages.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"messages_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.messages.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.messages.message", "name": "message", "columnName": "message", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.messages.sender_id", "name": "sender_id", "columnName": "sender_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.messages.receiver_id", "name": "receiver_id", "columnName": "receiver_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.messages.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles_public_messages_receiver_idToprofiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "public_messages_receiver_idToprofiles", "relationFromFields": ["receiver_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles_public_messages_sender_idToprofiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "public_messages_sender_idToprofiles", "relationFromFields": ["sender_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "messages_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "realtime_messages": { "id": "realtime.messages", "schemaName": "realtime", "tableName": "messages", "fields": [ { "id": "realtime.messages.topic", "name": "topic", "columnName": "topic", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.messages.extension", "name": "extension", "columnName": "extension", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.messages.payload", "name": "payload", "columnName": "payload", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.messages.event", "name": "event", "columnName": "event", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.messages.private", "name": "private", "columnName": "private", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "realtime.messages.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "realtime.messages.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "realtime.messages.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null } ], "uniqueConstraints": [ { "name": "messages_pkey", "fields": ["id", "inserted_at"], "nullNotDistinct": false } ] }, "mfa_amr_claims": { "id": "auth.mfa_amr_claims", "schemaName": "auth", "tableName": "mfa_amr_claims", "fields": [ { "id": "auth.mfa_amr_claims.session_id", "name": "session_id", "columnName": "session_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_amr_claims.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_amr_claims.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_amr_claims.authentication_method", "name": "authentication_method", "columnName": "authentication_method", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_amr_claims.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "name": "sessions", "type": "sessions", "isRequired": true, "kind": "object", "relationName": "mfa_amr_claimsTosessions", "relationFromFields": ["session_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "mfa_amr_claims_session_id_authentication_method_pkey", "fields": ["authentication_method", "session_id"], "nullNotDistinct": false } ] }, "mfa_challenges": { "id": "auth.mfa_challenges", "schemaName": "auth", "tableName": "mfa_challenges", "fields": [ { "id": "auth.mfa_challenges.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.mfa_challenges.factor_id", "name": "factor_id", "columnName": "factor_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_challenges.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_challenges.verified_at", "name": "verified_at", "columnName": "verified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_challenges.ip_address", "name": "ip_address", "columnName": "ip_address", "type": "inet", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_challenges.otp_code", "name": "otp_code", "columnName": "otp_code", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_challenges.web_authn_session_data", "name": "web_authn_session_data", "columnName": "web_authn_session_data", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "mfa_factors", "type": "mfa_factors", "isRequired": true, "kind": "object", "relationName": "mfa_challengesTomfa_factors", "relationFromFields": ["factor_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "mfa_factors": { "id": "auth.mfa_factors", "schemaName": "auth", "tableName": "mfa_factors", "fields": [ { "id": "auth.mfa_factors.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.mfa_factors.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.friendly_name", "name": "friendly_name", "columnName": "friendly_name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.factor_type", "name": "factor_type", "columnName": "factor_type", "type": "factor_type", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.status", "name": "status", "columnName": "status", "type": "factor_status", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.secret", "name": "secret", "columnName": "secret", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.phone", "name": "phone", "columnName": "phone", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.last_challenged_at", "name": "last_challenged_at", "columnName": "last_challenged_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.web_authn_credential", "name": "web_authn_credential", "columnName": "web_authn_credential", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.web_authn_aaguid", "name": "web_authn_aaguid", "columnName": "web_authn_aaguid", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.mfa_factors.last_webauthn_challenge_data", "name": "last_webauthn_challenge_data", "columnName": "last_webauthn_challenge_data", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "users", "type": "users", "isRequired": true, "kind": "object", "relationName": "mfa_factorsTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "mfa_challenges", "type": "mfa_challenges", "isRequired": false, "kind": "object", "relationName": "mfa_challengesTomfa_factors", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "mfa_factors_last_challenged_at_key", "fields": ["last_challenged_at"], "nullNotDistinct": false }, { "name": "mfa_factors_user_friendly_name_unique", "fields": ["friendly_name", "user_id"], "nullNotDistinct": false }, { "name": "unique_phone_factor_per_user", "fields": ["phone", "user_id"], "nullNotDistinct": false } ] }, "storage_migrations": { "id": "storage.migrations", "schemaName": "storage", "tableName": "migrations", "fields": [ { "id": "storage.migrations.id", "name": "id", "columnName": "id", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "storage.migrations.name", "name": "name", "columnName": "name", "type": "varchar", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 100 }, { "id": "storage.migrations.hash", "name": "hash", "columnName": "hash", "type": "varchar", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 40 }, { "id": "storage.migrations.executed_at", "name": "executed_at", "columnName": "executed_at", "type": "timestamp", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "migrations_name_key", "fields": ["name"], "nullNotDistinct": false } ] }, "supabase_functions_migrations": { "id": "supabase_functions.migrations", "schemaName": "supabase_functions", "tableName": "migrations", "fields": [ { "id": "supabase_functions.migrations.version", "name": "version", "columnName": "version", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "supabase_functions.migrations.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "migrations_pkey", "fields": ["version"], "nullNotDistinct": false } ] }, "news": { "id": "public.news", "schemaName": "public", "tableName": "news", "fields": [ { "id": "public.news.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"news_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.news.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.news.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.news.comment_count", "name": "comment_count", "columnName": "comment_count", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.news.body", "name": "body", "columnName": "body", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.moderation", "name": "moderation", "columnName": "moderation", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.slug", "name": "slug", "columnName": "slug", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.previous_slugs", "name": "previous_slugs", "columnName": "previous_slugs", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.category", "name": "category", "columnName": "category", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.tags", "name": "tags", "columnName": "tags", "type": "int8[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.total_views", "name": "total_views", "columnName": "total_views", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.hero_image", "name": "hero_image", "columnName": "hero_image", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.summary", "name": "summary", "columnName": "summary", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.fts", "name": "fts", "columnName": "fts", "type": "tsvector", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.is_draft", "name": "is_draft", "columnName": "is_draft", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.news.profile_badge", "name": "profile_badge", "columnName": "profile_badge", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.news.published_at", "name": "published_at", "columnName": "published_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "categories", "type": "categories", "isRequired": false, "kind": "object", "relationName": "newsTocategories", "relationFromFields": ["category"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profile_badges", "type": "profile_badges", "isRequired": false, "kind": "object", "relationName": "newsToprofile_badges", "relationFromFields": ["profile_badge"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "newsToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "news_pkey", "fields": ["id"], "nullNotDistinct": false }, { "name": "news_tenant_id_slug_key", "fields": ["slug", "tenant_id"], "nullNotDistinct": false } ] }, "notifications": { "id": "public.notifications", "schemaName": "public", "tableName": "notifications", "fields": [ { "id": "public.notifications.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"notifications_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.notifications.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.notifications.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.notifications.owned_by_id", "name": "owned_by_id", "columnName": "owned_by_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.triggered_by_id", "name": "triggered_by_id", "columnName": "triggered_by_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.content_type", "name": "content_type", "columnName": "content_type", "type": "notification_content_types", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.content_id", "name": "content_id", "columnName": "content_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.is_read", "name": "is_read", "columnName": "is_read", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.notifications.action_type", "name": "action_type", "columnName": "action_type", "type": "notification_action_types", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.source_content_type", "name": "source_content_type", "columnName": "source_content_type", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.source_content_id", "name": "source_content_id", "columnName": "source_content_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.parent_comment_id", "name": "parent_comment_id", "columnName": "parent_comment_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.parent_content_id", "name": "parent_content_id", "columnName": "parent_content_id", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.should_email", "name": "should_email", "columnName": "should_email", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications.title", "name": "title", "columnName": "title", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles_notifications_owned_by_idToprofiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "notifications_owned_by_idToprofiles", "relationFromFields": ["owned_by_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles_notifications_triggered_by_idToprofiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "notifications_triggered_by_idToprofiles", "relationFromFields": ["triggered_by_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "notifications_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "notifications_preferences": { "id": "public.notifications_preferences", "schemaName": "public", "tableName": "notifications_preferences", "fields": [ { "id": "public.notifications_preferences.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"notifications_preferences_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.notifications_preferences.user_id", "name": "user_id", "columnName": "user_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications_preferences.comments", "name": "comments", "columnName": "comments", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications_preferences.replies", "name": "replies", "columnName": "replies", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications_preferences.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications_preferences.research_updates", "name": "research_updates", "columnName": "research_updates", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.notifications_preferences.is_unsubscribed", "name": "is_unsubscribed", "columnName": "is_unsubscribed", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "notifications_preferencesToprofiles", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "notifications_preferences_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "oauth_authorizations": { "id": "auth.oauth_authorizations", "schemaName": "auth", "tableName": "oauth_authorizations", "fields": [ { "id": "auth.oauth_authorizations.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.oauth_authorizations.authorization_id", "name": "authorization_id", "columnName": "authorization_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.client_id", "name": "client_id", "columnName": "client_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.redirect_uri", "name": "redirect_uri", "columnName": "redirect_uri", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.scope", "name": "scope", "columnName": "scope", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.state", "name": "state", "columnName": "state", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.resource", "name": "resource", "columnName": "resource", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.code_challenge", "name": "code_challenge", "columnName": "code_challenge", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.code_challenge_method", "name": "code_challenge_method", "columnName": "code_challenge_method", "type": "code_challenge_method", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.response_type", "name": "response_type", "columnName": "response_type", "type": "oauth_response_type", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.status", "name": "status", "columnName": "status", "type": "oauth_authorization_status", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.authorization_code", "name": "authorization_code", "columnName": "authorization_code", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.expires_at", "name": "expires_at", "columnName": "expires_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.approved_at", "name": "approved_at", "columnName": "approved_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_authorizations.nonce", "name": "nonce", "columnName": "nonce", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "oauth_clients", "type": "oauth_clients", "isRequired": true, "kind": "object", "relationName": "oauth_authorizationsTooauth_clients", "relationFromFields": ["client_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "users", "type": "users", "isRequired": false, "kind": "object", "relationName": "oauth_authorizationsTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "oauth_authorizations_authorization_code_key", "fields": ["authorization_code"], "nullNotDistinct": false }, { "name": "oauth_authorizations_authorization_id_key", "fields": ["authorization_id"], "nullNotDistinct": false } ] }, "oauth_client_states": { "id": "auth.oauth_client_states", "schemaName": "auth", "tableName": "oauth_client_states", "fields": [ { "id": "auth.oauth_client_states.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.oauth_client_states.provider_type", "name": "provider_type", "columnName": "provider_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_client_states.code_verifier", "name": "code_verifier", "columnName": "code_verifier", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_client_states.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "oauth_clients": { "id": "auth.oauth_clients", "schemaName": "auth", "tableName": "oauth_clients", "fields": [ { "id": "auth.oauth_clients.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.oauth_clients.client_secret_hash", "name": "client_secret_hash", "columnName": "client_secret_hash", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.registration_type", "name": "registration_type", "columnName": "registration_type", "type": "oauth_registration_type", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.redirect_uris", "name": "redirect_uris", "columnName": "redirect_uris", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.grant_types", "name": "grant_types", "columnName": "grant_types", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.client_name", "name": "client_name", "columnName": "client_name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.client_uri", "name": "client_uri", "columnName": "client_uri", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.logo_uri", "name": "logo_uri", "columnName": "logo_uri", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.deleted_at", "name": "deleted_at", "columnName": "deleted_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.client_type", "name": "client_type", "columnName": "client_type", "type": "oauth_client_type", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_clients.token_endpoint_auth_method", "name": "token_endpoint_auth_method", "columnName": "token_endpoint_auth_method", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "oauth_authorizations", "type": "oauth_authorizations", "isRequired": false, "kind": "object", "relationName": "oauth_authorizationsTooauth_clients", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "oauth_consents", "type": "oauth_consents", "isRequired": false, "kind": "object", "relationName": "oauth_consentsTooauth_clients", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "sessions", "type": "sessions", "isRequired": false, "kind": "object", "relationName": "sessionsTooauth_clients", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "oauth_consents": { "id": "auth.oauth_consents", "schemaName": "auth", "tableName": "oauth_consents", "fields": [ { "id": "auth.oauth_consents.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.oauth_consents.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_consents.client_id", "name": "client_id", "columnName": "client_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_consents.scopes", "name": "scopes", "columnName": "scopes", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.oauth_consents.granted_at", "name": "granted_at", "columnName": "granted_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.oauth_consents.revoked_at", "name": "revoked_at", "columnName": "revoked_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "oauth_clients", "type": "oauth_clients", "isRequired": true, "kind": "object", "relationName": "oauth_consentsTooauth_clients", "relationFromFields": ["client_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "users", "type": "users", "isRequired": true, "kind": "object", "relationName": "oauth_consentsTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "oauth_consents_user_client_unique", "fields": ["client_id", "user_id"], "nullNotDistinct": false } ] }, "objects": { "id": "storage.objects", "schemaName": "storage", "tableName": "objects", "fields": [ { "id": "storage.objects.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.objects.bucket_id", "name": "bucket_id", "columnName": "bucket_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.name", "name": "name", "columnName": "name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.owner", "name": "owner", "columnName": "owner", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.objects.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.objects.last_accessed_at", "name": "last_accessed_at", "columnName": "last_accessed_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.objects.metadata", "name": "metadata", "columnName": "metadata", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.path_tokens", "name": "path_tokens", "columnName": "path_tokens", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.version", "name": "version", "columnName": "version", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.owner_id", "name": "owner_id", "columnName": "owner_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.objects.user_metadata", "name": "user_metadata", "columnName": "user_metadata", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "buckets", "type": "buckets", "isRequired": false, "kind": "object", "relationName": "objectsTobuckets", "relationFromFields": ["bucket_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "bucketid_objname", "fields": ["bucket_id", "name"], "nullNotDistinct": false } ] }, "one_time_tokens": { "id": "auth.one_time_tokens", "schemaName": "auth", "tableName": "one_time_tokens", "fields": [ { "id": "auth.one_time_tokens.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.one_time_tokens.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.one_time_tokens.token_type", "name": "token_type", "columnName": "token_type", "type": "one_time_token_type", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.one_time_tokens.token_hash", "name": "token_hash", "columnName": "token_hash", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.one_time_tokens.relates_to", "name": "relates_to", "columnName": "relates_to", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.one_time_tokens.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.one_time_tokens.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "users", "type": "users", "isRequired": true, "kind": "object", "relationName": "one_time_tokensTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "one_time_tokens_user_id_token_type_key", "fields": ["token_type", "user_id"], "nullNotDistinct": false } ] }, "patreon_settings": { "id": "public.patreon_settings", "schemaName": "public", "tableName": "patreon_settings", "fields": [ { "id": "public.patreon_settings.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"patreon_settings_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.patreon_settings.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.patreon_settings.tiers", "name": "tiers", "columnName": "tiers", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.patreon_settings.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "patreon_settings_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profile_badges": { "id": "public.profile_badges", "schemaName": "public", "tableName": "profile_badges", "fields": [ { "id": "public.profile_badges.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profile_badges_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profile_badges.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges.display_name", "name": "display_name", "columnName": "display_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges.image_url", "name": "image_url", "columnName": "image_url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges.action_url", "name": "action_url", "columnName": "action_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges.premium_tier", "name": "premium_tier", "columnName": "premium_tier", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "news", "type": "news", "isRequired": false, "kind": "object", "relationName": "newsToprofile_badges", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profile_badges_relations", "type": "profile_badges_relations", "isRequired": false, "kind": "object", "relationName": "profile_badges_relationsToprofile_badges", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "upgrade_badge", "type": "upgrade_badge", "isRequired": false, "kind": "object", "relationName": "upgrade_badgeToprofile_badges", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profile_badges_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profile_badges_relations": { "id": "public.profile_badges_relations", "schemaName": "public", "tableName": "profile_badges_relations", "fields": [ { "id": "public.profile_badges_relations.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profile_badges_relations_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profile_badges_relations.profile_id", "name": "profile_id", "columnName": "profile_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges_relations.profile_badge_id", "name": "profile_badge_id", "columnName": "profile_badge_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges_relations.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_badges_relations.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "profile_badges", "type": "profile_badges", "isRequired": true, "kind": "object", "relationName": "profile_badges_relationsToprofile_badges", "relationFromFields": ["profile_badge_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "profile_badges_relationsToprofiles", "relationFromFields": ["profile_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profile_badges_relations_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profile_tags": { "id": "public.profile_tags", "schemaName": "public", "tableName": "profile_tags", "fields": [ { "id": "public.profile_tags.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profile_tags_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profile_tags.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.profile_tags.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_tags.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_tags.profile_type", "name": "profile_type", "columnName": "profile_type", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profile_tags_relations", "type": "profile_tags_relations", "isRequired": false, "kind": "object", "relationName": "profile_tags_relationsToprofile_tags", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profile_tags_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profile_tags_relations": { "id": "public.profile_tags_relations", "schemaName": "public", "tableName": "profile_tags_relations", "fields": [ { "id": "public.profile_tags_relations.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profile_tags_relations_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profile_tags_relations.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.profile_tags_relations.profile_id", "name": "profile_id", "columnName": "profile_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_tags_relations.profile_tag_id", "name": "profile_tag_id", "columnName": "profile_tag_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_tags_relations.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profile_tags", "type": "profile_tags", "isRequired": true, "kind": "object", "relationName": "profile_tags_relationsToprofile_tags", "relationFromFields": ["profile_tag_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "profile_tags_relationsToprofiles", "relationFromFields": ["profile_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profile_tags_relations_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profile_types": { "id": "public.profile_types", "schemaName": "public", "tableName": "profile_types", "fields": [ { "id": "public.profile_types.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profile_types_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profile_types.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.display_name", "name": "display_name", "columnName": "display_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.order", "name": "order", "columnName": "order", "type": "int2", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.image_url", "name": "image_url", "columnName": "image_url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.small_image_url", "name": "small_image_url", "columnName": "small_image_url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.map_pin_name", "name": "map_pin_name", "columnName": "map_pin_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.is_space", "name": "is_space", "columnName": "is_space", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profile_types.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "profilesToprofile_types", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profile_types_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "profiles": { "id": "public.profiles", "schemaName": "public", "tableName": "profiles", "fields": [ { "id": "public.profiles.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"profiles_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.profiles.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.profiles.firebase_auth_id", "name": "firebase_auth_id", "columnName": "firebase_auth_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.display_name", "name": "display_name", "columnName": "display_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.country", "name": "country", "columnName": "country", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.about", "name": "about", "columnName": "about", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.username", "name": "username", "columnName": "username", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.roles", "name": "roles", "columnName": "roles", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.impact", "name": "impact", "columnName": "impact", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.is_blocked_from_messaging", "name": "is_blocked_from_messaging", "columnName": "is_blocked_from_messaging", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.is_contactable", "name": "is_contactable", "columnName": "is_contactable", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.profiles.is_supporter", "name": "is_supporter", "columnName": "is_supporter", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.patreon", "name": "patreon", "columnName": "patreon", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.total_views", "name": "total_views", "columnName": "total_views", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.type", "name": "type", "columnName": "type", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.auth_id", "name": "auth_id", "columnName": "auth_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.cover_images", "name": "cover_images", "columnName": "cover_images", "type": "json[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.last_active", "name": "last_active", "columnName": "last_active", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.photo", "name": "photo", "columnName": "photo", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.visitor_policy", "name": "visitor_policy", "columnName": "visitor_policy", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.website", "name": "website", "columnName": "website", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.profile_type", "name": "profile_type", "columnName": "profile_type", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.profiles.donations_enabled", "name": "donations_enabled", "columnName": "donations_enabled", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "users", "type": "users", "isRequired": false, "kind": "object", "relationName": "profilesTousers", "relationFromFields": ["auth_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profile_types", "type": "profile_types", "isRequired": false, "kind": "object", "relationName": "profilesToprofile_types", "relationFromFields": ["profile_type"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "comments", "type": "comments", "isRequired": false, "kind": "object", "relationName": "commentsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "map_pins", "type": "map_pins", "isRequired": false, "kind": "object", "relationName": "map_pinsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "public_messages_public_messages_receiver_idToprofiles", "type": "public_messages", "isRequired": false, "kind": "object", "relationName": "public_messages_receiver_idToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "public_messages_public_messages_sender_idToprofiles", "type": "public_messages", "isRequired": false, "kind": "object", "relationName": "public_messages_sender_idToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "news", "type": "news", "isRequired": false, "kind": "object", "relationName": "newsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "notifications_notifications_owned_by_idToprofiles", "type": "notifications", "isRequired": false, "kind": "object", "relationName": "notifications_owned_by_idToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "notifications_notifications_triggered_by_idToprofiles", "type": "notifications", "isRequired": false, "kind": "object", "relationName": "notifications_triggered_by_idToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "notifications_preferences", "type": "notifications_preferences", "isRequired": false, "kind": "object", "relationName": "notifications_preferencesToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profile_badges_relations", "type": "profile_badges_relations", "isRequired": false, "kind": "object", "relationName": "profile_badges_relationsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profile_tags_relations", "type": "profile_tags_relations", "isRequired": false, "kind": "object", "relationName": "profile_tags_relationsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "projects", "type": "projects", "isRequired": false, "kind": "object", "relationName": "projectsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "questions", "type": "questions", "isRequired": false, "kind": "object", "relationName": "questionsToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "research", "type": "research", "isRequired": false, "kind": "object", "relationName": "researchToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "research_updates", "type": "research_updates", "isRequired": false, "kind": "object", "relationName": "research_updatesToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "subscribers", "type": "subscribers", "isRequired": false, "kind": "object", "relationName": "subscribersToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "useful_votes", "type": "useful_votes", "isRequired": false, "kind": "object", "relationName": "useful_votesToprofiles", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "profiles_auth_id_tenant_id_key", "fields": ["auth_id", "tenant_id"], "nullNotDistinct": false }, { "name": "profiles_pkey", "fields": ["id"], "nullNotDistinct": false }, { "name": "profiles_username_tenant_id_key", "fields": ["tenant_id", "username"], "nullNotDistinct": false } ] }, "project_steps": { "id": "public.project_steps", "schemaName": "public", "tableName": "project_steps", "fields": [ { "id": "public.project_steps.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"project_steps_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.project_steps.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.project_steps.project_id", "name": "project_id", "columnName": "project_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.images", "name": "images", "columnName": "images", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.video_url", "name": "video_url", "columnName": "video_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.order", "name": "order", "columnName": "order", "type": "int2", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.project_steps.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "projects", "type": "projects", "isRequired": true, "kind": "object", "relationName": "project_stepsToprojects", "relationFromFields": ["project_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "project_steps_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "projects": { "id": "public.projects", "schemaName": "public", "tableName": "projects", "fields": [ { "id": "public.projects.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"projects_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.projects.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.projects.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.slug", "name": "slug", "columnName": "slug", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.previous_slugs", "name": "previous_slugs", "columnName": "previous_slugs", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.category", "name": "category", "columnName": "category", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.difficulty_level", "name": "difficulty_level", "columnName": "difficulty_level", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.cover_image", "name": "cover_image", "columnName": "cover_image", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.file_link", "name": "file_link", "columnName": "file_link", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.files", "name": "files", "columnName": "files", "type": "json[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.tags", "name": "tags", "columnName": "tags", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.is_draft", "name": "is_draft", "columnName": "is_draft", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.time", "name": "time", "columnName": "time", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.file_download_count", "name": "file_download_count", "columnName": "file_download_count", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.moderation", "name": "moderation", "columnName": "moderation", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.moderation_feedback", "name": "moderation_feedback", "columnName": "moderation_feedback", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.fts", "name": "fts", "columnName": "fts", "type": "tsvector", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.total_views", "name": "total_views", "columnName": "total_views", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.comment_count", "name": "comment_count", "columnName": "comment_count", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.projects.published_at", "name": "published_at", "columnName": "published_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "categories", "type": "categories", "isRequired": false, "kind": "object", "relationName": "projectsTocategories", "relationFromFields": ["category"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "projectsToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "project_steps", "type": "project_steps", "isRequired": false, "kind": "object", "relationName": "project_stepsToprojects", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "projects_pkey", "fields": ["id"], "nullNotDistinct": false }, { "name": "projects_slug_key", "fields": ["slug", "tenant_id"], "nullNotDistinct": false }, { "name": "projects_title_key", "fields": ["tenant_id", "title"], "nullNotDistinct": false } ] }, "questions": { "id": "public.questions", "schemaName": "public", "tableName": "questions", "fields": [ { "id": "public.questions.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"questions_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.questions.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.questions.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.questions.comment_count", "name": "comment_count", "columnName": "comment_count", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.questions.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.moderation", "name": "moderation", "columnName": "moderation", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.slug", "name": "slug", "columnName": "slug", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.previous_slugs", "name": "previous_slugs", "columnName": "previous_slugs", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.category", "name": "category", "columnName": "category", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.tags", "name": "tags", "columnName": "tags", "type": "int8[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.total_views", "name": "total_views", "columnName": "total_views", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.fts", "name": "fts", "columnName": "fts", "type": "tsvector", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.images", "name": "images", "columnName": "images", "type": "json[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.questions.is_draft", "name": "is_draft", "columnName": "is_draft", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.questions.published_at", "name": "published_at", "columnName": "published_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "categories", "type": "categories", "isRequired": false, "kind": "object", "relationName": "questionsTocategories", "relationFromFields": ["category"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "questionsToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "questions_pkey", "fields": ["id"], "nullNotDistinct": false }, { "name": "unique_tenant_slug", "fields": ["slug", "tenant_id"], "nullNotDistinct": false } ] }, "refresh_tokens": { "id": "auth.refresh_tokens", "schemaName": "auth", "tableName": "refresh_tokens", "fields": [ { "id": "auth.refresh_tokens.instance_id", "name": "instance_id", "columnName": "instance_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.refresh_tokens.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"auth\".\"refresh_tokens_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "auth.refresh_tokens.token", "name": "token", "columnName": "token", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.refresh_tokens.user_id", "name": "user_id", "columnName": "user_id", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.refresh_tokens.revoked", "name": "revoked", "columnName": "revoked", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.refresh_tokens.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.refresh_tokens.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.refresh_tokens.parent", "name": "parent", "columnName": "parent", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.refresh_tokens.session_id", "name": "session_id", "columnName": "session_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "sessions", "type": "sessions", "isRequired": false, "kind": "object", "relationName": "refresh_tokensTosessions", "relationFromFields": ["session_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "refresh_tokens_token_unique", "fields": ["token"], "nullNotDistinct": false } ] }, "research": { "id": "public.research", "schemaName": "public", "tableName": "research", "fields": [ { "id": "public.research.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"research_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.research.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.research.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.research.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.slug", "name": "slug", "columnName": "slug", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.category", "name": "category", "columnName": "category", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.tags", "name": "tags", "columnName": "tags", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.total_views", "name": "total_views", "columnName": "total_views", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.total_useful", "name": "total_useful", "columnName": "total_useful", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.previous_slugs", "name": "previous_slugs", "columnName": "previous_slugs", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.status", "name": "status", "columnName": "status", "type": "research_status", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.is_draft", "name": "is_draft", "columnName": "is_draft", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.fts", "name": "fts", "columnName": "fts", "type": "tsvector", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.collaborators", "name": "collaborators", "columnName": "collaborators", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.image", "name": "image", "columnName": "image", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research.published_at", "name": "published_at", "columnName": "published_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "categories", "type": "categories", "isRequired": false, "kind": "object", "relationName": "researchTocategories", "relationFromFields": ["category"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "researchToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "research_updates", "type": "research_updates", "isRequired": false, "kind": "object", "relationName": "research_updatesToresearch", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "research_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "research_updates": { "id": "public.research_updates", "schemaName": "public", "tableName": "research_updates", "fields": [ { "id": "public.research_updates.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"research_updates_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.research_updates.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.research_updates.research_id", "name": "research_id", "columnName": "research_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.title", "name": "title", "columnName": "title", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.images", "name": "images", "columnName": "images", "type": "json[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.files", "name": "files", "columnName": "files", "type": "json[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.video_url", "name": "video_url", "columnName": "video_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.is_draft", "name": "is_draft", "columnName": "is_draft", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.comment_count", "name": "comment_count", "columnName": "comment_count", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.research_updates.deleted", "name": "deleted", "columnName": "deleted", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.file_link", "name": "file_link", "columnName": "file_link", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.file_download_count", "name": "file_download_count", "columnName": "file_download_count", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.created_by", "name": "created_by", "columnName": "created_by", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.research_updates.published_at", "name": "published_at", "columnName": "published_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "research_updatesToprofiles", "relationFromFields": ["created_by"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "research", "type": "research", "isRequired": true, "kind": "object", "relationName": "research_updatesToresearch", "relationFromFields": ["research_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "research_updates_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "s3_multipart_uploads": { "id": "storage.s3_multipart_uploads", "schemaName": "storage", "tableName": "s3_multipart_uploads", "fields": [ { "id": "storage.s3_multipart_uploads.id", "name": "id", "columnName": "id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "storage.s3_multipart_uploads.in_progress_size", "name": "in_progress_size", "columnName": "in_progress_size", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.upload_signature", "name": "upload_signature", "columnName": "upload_signature", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.bucket_id", "name": "bucket_id", "columnName": "bucket_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.key", "name": "key", "columnName": "key", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.version", "name": "version", "columnName": "version", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.owner_id", "name": "owner_id", "columnName": "owner_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads.user_metadata", "name": "user_metadata", "columnName": "user_metadata", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "buckets", "type": "buckets", "isRequired": true, "kind": "object", "relationName": "s3_multipart_uploadsTobuckets", "relationFromFields": ["bucket_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "s3_multipart_uploads_parts", "type": "s3_multipart_uploads_parts", "isRequired": false, "kind": "object", "relationName": "s3_multipart_uploads_partsTos3_multipart_uploads", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "s3_multipart_uploads_parts": { "id": "storage.s3_multipart_uploads_parts", "schemaName": "storage", "tableName": "s3_multipart_uploads_parts", "fields": [ { "id": "storage.s3_multipart_uploads_parts.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.upload_id", "name": "upload_id", "columnName": "upload_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.size", "name": "size", "columnName": "size", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.part_number", "name": "part_number", "columnName": "part_number", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.bucket_id", "name": "bucket_id", "columnName": "bucket_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.key", "name": "key", "columnName": "key", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.etag", "name": "etag", "columnName": "etag", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.owner_id", "name": "owner_id", "columnName": "owner_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.version", "name": "version", "columnName": "version", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.s3_multipart_uploads_parts.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "buckets", "type": "buckets", "isRequired": true, "kind": "object", "relationName": "s3_multipart_uploads_partsTobuckets", "relationFromFields": ["bucket_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "s3_multipart_uploads", "type": "s3_multipart_uploads", "isRequired": true, "kind": "object", "relationName": "s3_multipart_uploads_partsTos3_multipart_uploads", "relationFromFields": ["upload_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "saml_providers": { "id": "auth.saml_providers", "schemaName": "auth", "tableName": "saml_providers", "fields": [ { "id": "auth.saml_providers.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.saml_providers.sso_provider_id", "name": "sso_provider_id", "columnName": "sso_provider_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.entity_id", "name": "entity_id", "columnName": "entity_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.metadata_xml", "name": "metadata_xml", "columnName": "metadata_xml", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.metadata_url", "name": "metadata_url", "columnName": "metadata_url", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.attribute_mapping", "name": "attribute_mapping", "columnName": "attribute_mapping", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_providers.name_id_format", "name": "name_id_format", "columnName": "name_id_format", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "sso_providers", "type": "sso_providers", "isRequired": true, "kind": "object", "relationName": "saml_providersTosso_providers", "relationFromFields": ["sso_provider_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "saml_providers_entity_id_key", "fields": ["entity_id"], "nullNotDistinct": false } ] }, "saml_relay_states": { "id": "auth.saml_relay_states", "schemaName": "auth", "tableName": "saml_relay_states", "fields": [ { "id": "auth.saml_relay_states.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.saml_relay_states.sso_provider_id", "name": "sso_provider_id", "columnName": "sso_provider_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.request_id", "name": "request_id", "columnName": "request_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.for_email", "name": "for_email", "columnName": "for_email", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.redirect_to", "name": "redirect_to", "columnName": "redirect_to", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.saml_relay_states.flow_state_id", "name": "flow_state_id", "columnName": "flow_state_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "flow_state", "type": "flow_state", "isRequired": false, "kind": "object", "relationName": "saml_relay_statesToflow_state", "relationFromFields": ["flow_state_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "sso_providers", "type": "sso_providers", "isRequired": true, "kind": "object", "relationName": "saml_relay_statesTosso_providers", "relationFromFields": ["sso_provider_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "_realtime_schema_migrations": { "id": "_realtime.schema_migrations", "schemaName": "_realtime", "tableName": "schema_migrations", "fields": [ { "id": "_realtime.schema_migrations.version", "name": "version", "columnName": "version", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "_realtime.schema_migrations.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamp", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "auth_schema_migrations": { "id": "auth.schema_migrations", "schemaName": "auth", "tableName": "schema_migrations", "fields": [ { "id": "auth.schema_migrations.version", "name": "version", "columnName": "version", "type": "varchar", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": 255 } ], "uniqueConstraints": [] }, "realtime_schema_migrations": { "id": "realtime.schema_migrations", "schemaName": "realtime", "tableName": "schema_migrations", "fields": [ { "id": "realtime.schema_migrations.version", "name": "version", "columnName": "version", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "realtime.schema_migrations.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamp", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [] }, "supabase_migrations_schema_migrations": { "id": "supabase_migrations.schema_migrations", "schemaName": "supabase_migrations", "tableName": "schema_migrations", "fields": [ { "id": "supabase_migrations.schema_migrations.version", "name": "version", "columnName": "version", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "supabase_migrations.schema_migrations.statements", "name": "statements", "columnName": "statements", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "supabase_migrations.schema_migrations.name", "name": "name", "columnName": "name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "schema_migrations_pkey", "fields": ["version"], "nullNotDistinct": false } ] }, "secrets": { "id": "vault.secrets", "schemaName": "vault", "tableName": "secrets", "fields": [ { "id": "vault.secrets.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "vault.secrets.name", "name": "name", "columnName": "name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "vault.secrets.description", "name": "description", "columnName": "description", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "vault.secrets.secret", "name": "secret", "columnName": "secret", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "vault.secrets.key_id", "name": "key_id", "columnName": "key_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "vault.secrets.nonce", "name": "nonce", "columnName": "nonce", "type": "bytea", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "vault.secrets.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "vault.secrets.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "secrets_name_idx", "fields": ["name"], "nullNotDistinct": false } ] }, "seed_files": { "id": "supabase_migrations.seed_files", "schemaName": "supabase_migrations", "tableName": "seed_files", "fields": [ { "id": "supabase_migrations.seed_files.path", "name": "path", "columnName": "path", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "supabase_migrations.seed_files.hash", "name": "hash", "columnName": "hash", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "seed_files_pkey", "fields": ["path"], "nullNotDistinct": false } ] }, "sessions": { "id": "auth.sessions", "schemaName": "auth", "tableName": "sessions", "fields": [ { "id": "auth.sessions.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.sessions.user_id", "name": "user_id", "columnName": "user_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.factor_id", "name": "factor_id", "columnName": "factor_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.aal", "name": "aal", "columnName": "aal", "type": "aal_level", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.not_after", "name": "not_after", "columnName": "not_after", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.refreshed_at", "name": "refreshed_at", "columnName": "refreshed_at", "type": "timestamp", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.user_agent", "name": "user_agent", "columnName": "user_agent", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.ip", "name": "ip", "columnName": "ip", "type": "inet", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.tag", "name": "tag", "columnName": "tag", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.oauth_client_id", "name": "oauth_client_id", "columnName": "oauth_client_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.refresh_token_hmac_key", "name": "refresh_token_hmac_key", "columnName": "refresh_token_hmac_key", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.refresh_token_counter", "name": "refresh_token_counter", "columnName": "refresh_token_counter", "type": "int8", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sessions.scopes", "name": "scopes", "columnName": "scopes", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "oauth_clients", "type": "oauth_clients", "isRequired": false, "kind": "object", "relationName": "sessionsTooauth_clients", "relationFromFields": ["oauth_client_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "users", "type": "users", "isRequired": true, "kind": "object", "relationName": "sessionsTousers", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "mfa_amr_claims", "type": "mfa_amr_claims", "isRequired": false, "kind": "object", "relationName": "mfa_amr_claimsTosessions", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "refresh_tokens", "type": "refresh_tokens", "isRequired": false, "kind": "object", "relationName": "refresh_tokensTosessions", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "sso_domains": { "id": "auth.sso_domains", "schemaName": "auth", "tableName": "sso_domains", "fields": [ { "id": "auth.sso_domains.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.sso_domains.sso_provider_id", "name": "sso_provider_id", "columnName": "sso_provider_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_domains.domain", "name": "domain", "columnName": "domain", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_domains.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_domains.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "sso_providers", "type": "sso_providers", "isRequired": true, "kind": "object", "relationName": "sso_domainsTosso_providers", "relationFromFields": ["sso_provider_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "sso_providers": { "id": "auth.sso_providers", "schemaName": "auth", "tableName": "sso_providers", "fields": [ { "id": "auth.sso_providers.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.sso_providers.resource_id", "name": "resource_id", "columnName": "resource_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_providers.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_providers.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.sso_providers.disabled", "name": "disabled", "columnName": "disabled", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "saml_providers", "type": "saml_providers", "isRequired": false, "kind": "object", "relationName": "saml_providersTosso_providers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "saml_relay_states", "type": "saml_relay_states", "isRequired": false, "kind": "object", "relationName": "saml_relay_statesTosso_providers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "sso_domains", "type": "sso_domains", "isRequired": false, "kind": "object", "relationName": "sso_domainsTosso_providers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [] }, "subscribers": { "id": "public.subscribers", "schemaName": "public", "tableName": "subscribers", "fields": [ { "id": "public.subscribers.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"subscribers_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.subscribers.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.subscribers.user_id", "name": "user_id", "columnName": "user_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.subscribers.content_id", "name": "content_id", "columnName": "content_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.subscribers.content_type", "name": "content_type", "columnName": "content_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.subscribers.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "subscribersToprofiles", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "subscribers_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "subscription": { "id": "realtime.subscription", "schemaName": "realtime", "tableName": "subscription", "fields": [ { "id": "realtime.subscription.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": { "identifier": "\"realtime\".\"realtime.subscription_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "realtime.subscription.subscription_id", "name": "subscription_id", "columnName": "subscription_id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.subscription.entity", "name": "entity", "columnName": "entity", "type": "regclass", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.subscription.filters", "name": "filters", "columnName": "filters", "type": "user_defined_filter[]", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "realtime.subscription.claims", "name": "claims", "columnName": "claims", "type": "jsonb", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.subscription.claims_role", "name": "claims_role", "columnName": "claims_role", "type": "regrole", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "realtime.subscription.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "subscription_subscription_id_entity_filters_key", "fields": ["entity", "filters", "subscription_id"], "nullNotDistinct": false } ] }, "tags": { "id": "public.tags", "schemaName": "public", "tableName": "tags", "fields": [ { "id": "public.tags.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"tags_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.tags.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.tags.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tags.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tags.legacy_id", "name": "legacy_id", "columnName": "legacy_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tags.modified_at", "name": "modified_at", "columnName": "modified_at", "type": "date", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "tags_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "tenant_settings": { "id": "public.tenant_settings", "schemaName": "public", "tableName": "tenant_settings", "fields": [ { "id": "public.tenant_settings.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"tenant_settings_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.tenant_settings.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.site_name", "name": "site_name", "columnName": "site_name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.site_url", "name": "site_url", "columnName": "site_url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.message_sign_off", "name": "message_sign_off", "columnName": "message_sign_off", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.email_from", "name": "email_from", "columnName": "email_from", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.site_image", "name": "site_image", "columnName": "site_image", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.site_favicon", "name": "site_favicon", "columnName": "site_favicon", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.donation_settings", "name": "donation_settings", "columnName": "donation_settings", "type": "json", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.academy_resource", "name": "academy_resource", "columnName": "academy_resource", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.library_heading", "name": "library_heading", "columnName": "library_heading", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.no_messaging", "name": "no_messaging", "columnName": "no_messaging", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.patreon_id", "name": "patreon_id", "columnName": "patreon_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.profile_guidelines", "name": "profile_guidelines", "columnName": "profile_guidelines", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.questions_guidelines", "name": "questions_guidelines", "columnName": "questions_guidelines", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.supported_modules", "name": "supported_modules", "columnName": "supported_modules", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.site_description", "name": "site_description", "columnName": "site_description", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.color_primary", "name": "color_primary", "columnName": "color_primary", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.color_primary_hover", "name": "color_primary_hover", "columnName": "color_primary_hover", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.color_accent", "name": "color_accent", "columnName": "color_accent", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.color_accent_hover", "name": "color_accent_hover", "columnName": "color_accent_hover", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.show_impact", "name": "show_impact", "columnName": "show_impact", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.create_research_roles", "name": "create_research_roles", "columnName": "create_research_roles", "type": "text[]", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.ga_tracking_id", "name": "ga_tracking_id", "columnName": "ga_tracking_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.tenant_settings.pwa_icons", "name": "pwa_icons", "columnName": "pwa_icons", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null } ], "uniqueConstraints": [ { "name": "tenant_settings_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "tenants": { "id": "_realtime.tenants", "schemaName": "_realtime", "tableName": "tenants", "fields": [ { "id": "_realtime.tenants.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "_realtime.tenants.name", "name": "name", "columnName": "name", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.external_id", "name": "external_id", "columnName": "external_id", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.jwt_secret", "name": "jwt_secret", "columnName": "jwt_secret", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_concurrent_users", "name": "max_concurrent_users", "columnName": "max_concurrent_users", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.inserted_at", "name": "inserted_at", "columnName": "inserted_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamp", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_events_per_second", "name": "max_events_per_second", "columnName": "max_events_per_second", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.postgres_cdc_default", "name": "postgres_cdc_default", "columnName": "postgres_cdc_default", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_bytes_per_second", "name": "max_bytes_per_second", "columnName": "max_bytes_per_second", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_channels_per_client", "name": "max_channels_per_client", "columnName": "max_channels_per_client", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_joins_per_second", "name": "max_joins_per_second", "columnName": "max_joins_per_second", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.suspend", "name": "suspend", "columnName": "suspend", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.jwt_jwks", "name": "jwt_jwks", "columnName": "jwt_jwks", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.notify_private_alpha", "name": "notify_private_alpha", "columnName": "notify_private_alpha", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.private_only", "name": "private_only", "columnName": "private_only", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.migrations_ran", "name": "migrations_ran", "columnName": "migrations_ran", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.broadcast_adapter", "name": "broadcast_adapter", "columnName": "broadcast_adapter", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": 255 }, { "id": "_realtime.tenants.max_presence_events_per_second", "name": "max_presence_events_per_second", "columnName": "max_presence_events_per_second", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "_realtime.tenants.max_payload_size_in_kb", "name": "max_payload_size_in_kb", "columnName": "max_payload_size_in_kb", "type": "int4", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "extensions", "type": "extensions", "isRequired": false, "kind": "object", "relationName": "extensionsTotenants", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "tenants_external_id_index", "fields": ["external_id"], "nullNotDistinct": false } ] }, "upgrade_badge": { "id": "public.upgrade_badge", "schemaName": "public", "tableName": "upgrade_badge", "fields": [ { "id": "public.upgrade_badge.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"upgrade_badge_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.upgrade_badge.action_label", "name": "action_label", "columnName": "action_label", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.upgrade_badge.badge_id", "name": "badge_id", "columnName": "badge_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.upgrade_badge.is_space", "name": "is_space", "columnName": "is_space", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.upgrade_badge.action_url", "name": "action_url", "columnName": "action_url", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.upgrade_badge.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profile_badges", "type": "profile_badges", "isRequired": true, "kind": "object", "relationName": "upgrade_badgeToprofile_badges", "relationFromFields": ["badge_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "upgrade_badge_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "useful_votes": { "id": "public.useful_votes", "schemaName": "public", "tableName": "useful_votes", "fields": [ { "id": "public.useful_votes.id", "name": "id", "columnName": "id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": { "identifier": "\"public\".\"useful_votes_id_seq\"", "increment": 1, "start": 1 }, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "public.useful_votes.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "public.useful_votes.content_id", "name": "content_id", "columnName": "content_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.useful_votes.content_type", "name": "content_type", "columnName": "content_type", "type": "useful_content_types", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.useful_votes.user_id", "name": "user_id", "columnName": "user_id", "type": "int8", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "public.useful_votes.tenant_id", "name": "tenant_id", "columnName": "tenant_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "name": "profiles", "type": "profiles", "isRequired": true, "kind": "object", "relationName": "useful_votesToprofiles", "relationFromFields": ["user_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "useful_votes_pkey", "fields": ["id"], "nullNotDistinct": false } ] }, "users": { "id": "auth.users", "schemaName": "auth", "tableName": "users", "fields": [ { "id": "auth.users.instance_id", "name": "instance_id", "columnName": "instance_id", "type": "uuid", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.id", "name": "id", "columnName": "id", "type": "uuid", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": true, "maxLength": null }, { "id": "auth.users.aud", "name": "aud", "columnName": "aud", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.role", "name": "role", "columnName": "role", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.email", "name": "email", "columnName": "email", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.encrypted_password", "name": "encrypted_password", "columnName": "encrypted_password", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.email_confirmed_at", "name": "email_confirmed_at", "columnName": "email_confirmed_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.invited_at", "name": "invited_at", "columnName": "invited_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.confirmation_token", "name": "confirmation_token", "columnName": "confirmation_token", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.confirmation_sent_at", "name": "confirmation_sent_at", "columnName": "confirmation_sent_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.recovery_token", "name": "recovery_token", "columnName": "recovery_token", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.recovery_sent_at", "name": "recovery_sent_at", "columnName": "recovery_sent_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.email_change_token_new", "name": "email_change_token_new", "columnName": "email_change_token_new", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.email_change", "name": "email_change", "columnName": "email_change", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": 255 }, { "id": "auth.users.email_change_sent_at", "name": "email_change_sent_at", "columnName": "email_change_sent_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.last_sign_in_at", "name": "last_sign_in_at", "columnName": "last_sign_in_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.raw_app_meta_data", "name": "raw_app_meta_data", "columnName": "raw_app_meta_data", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.raw_user_meta_data", "name": "raw_user_meta_data", "columnName": "raw_user_meta_data", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.is_super_admin", "name": "is_super_admin", "columnName": "is_super_admin", "type": "bool", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.phone", "name": "phone", "columnName": "phone", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.users.phone_confirmed_at", "name": "phone_confirmed_at", "columnName": "phone_confirmed_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.phone_change", "name": "phone_change", "columnName": "phone_change", "type": "text", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.users.phone_change_token", "name": "phone_change_token", "columnName": "phone_change_token", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": 255 }, { "id": "auth.users.phone_change_sent_at", "name": "phone_change_sent_at", "columnName": "phone_change_sent_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.confirmed_at", "name": "confirmed_at", "columnName": "confirmed_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": true, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.email_change_token_current", "name": "email_change_token_current", "columnName": "email_change_token_current", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": 255 }, { "id": "auth.users.email_change_confirm_status", "name": "email_change_confirm_status", "columnName": "email_change_confirm_status", "type": "int2", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.users.banned_until", "name": "banned_until", "columnName": "banned_until", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.reauthentication_token", "name": "reauthentication_token", "columnName": "reauthentication_token", "type": "varchar", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": 255 }, { "id": "auth.users.reauthentication_sent_at", "name": "reauthentication_sent_at", "columnName": "reauthentication_sent_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.is_sso_user", "name": "is_sso_user", "columnName": "is_sso_user", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "auth.users.deleted_at", "name": "deleted_at", "columnName": "deleted_at", "type": "timestamptz", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "auth.users.is_anonymous", "name": "is_anonymous", "columnName": "is_anonymous", "type": "bool", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "identities", "type": "identities", "isRequired": false, "kind": "object", "relationName": "identitiesTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "mfa_factors", "type": "mfa_factors", "isRequired": false, "kind": "object", "relationName": "mfa_factorsTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "oauth_authorizations", "type": "oauth_authorizations", "isRequired": false, "kind": "object", "relationName": "oauth_authorizationsTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "oauth_consents", "type": "oauth_consents", "isRequired": false, "kind": "object", "relationName": "oauth_consentsTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "one_time_tokens", "type": "one_time_tokens", "isRequired": false, "kind": "object", "relationName": "one_time_tokensTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "sessions", "type": "sessions", "isRequired": false, "kind": "object", "relationName": "sessionsTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false }, { "name": "profiles", "type": "profiles", "isRequired": false, "kind": "object", "relationName": "profilesTousers", "relationFromFields": [], "relationToFields": [], "isList": true, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "confirmation_token_idx", "fields": ["confirmation_token"], "nullNotDistinct": false }, { "name": "email_change_token_current_idx", "fields": ["email_change_token_current"], "nullNotDistinct": false }, { "name": "email_change_token_new_idx", "fields": ["email_change_token_new"], "nullNotDistinct": false }, { "name": "reauthentication_token_idx", "fields": ["reauthentication_token"], "nullNotDistinct": false }, { "name": "recovery_token_idx", "fields": ["recovery_token"], "nullNotDistinct": false }, { "name": "users_email_partial_key", "fields": ["email"], "nullNotDistinct": false }, { "name": "users_phone_key", "fields": ["phone"], "nullNotDistinct": false } ] }, "vector_indexes": { "id": "storage.vector_indexes", "schemaName": "storage", "tableName": "vector_indexes", "fields": [ { "id": "storage.vector_indexes.id", "name": "id", "columnName": "id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": true, "maxLength": null }, { "id": "storage.vector_indexes.name", "name": "name", "columnName": "name", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.bucket_id", "name": "bucket_id", "columnName": "bucket_id", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.data_type", "name": "data_type", "columnName": "data_type", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.dimension", "name": "dimension", "columnName": "dimension", "type": "int4", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.distance_metric", "name": "distance_metric", "columnName": "distance_metric", "type": "text", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.metadata_configuration", "name": "metadata_configuration", "columnName": "metadata_configuration", "type": "jsonb", "isRequired": false, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.created_at", "name": "created_at", "columnName": "created_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "id": "storage.vector_indexes.updated_at", "name": "updated_at", "columnName": "updated_at", "type": "timestamptz", "isRequired": true, "kind": "scalar", "isList": false, "isGenerated": false, "sequence": false, "hasDefaultValue": true, "isId": false, "maxLength": null }, { "name": "buckets_vectors", "type": "buckets_vectors", "isRequired": true, "kind": "object", "relationName": "vector_indexesTobuckets_vectors", "relationFromFields": ["bucket_id"], "relationToFields": ["id"], "isList": false, "isId": false, "isGenerated": false, "sequence": false, "hasDefaultValue": false } ], "uniqueConstraints": [ { "name": "vector_indexes_name_bucket_id_idx", "fields": ["bucket_id", "name"], "nullNotDistinct": false } ] } }, "enums": { "aal_level": { "schemaName": "auth", "values": [ { "name": "aal1" }, { "name": "aal2" }, { "name": "aal3" } ] }, "code_challenge_method": { "schemaName": "auth", "values": [ { "name": "plain" }, { "name": "s256" } ] }, "factor_status": { "schemaName": "auth", "values": [ { "name": "unverified" }, { "name": "verified" } ] }, "factor_type": { "schemaName": "auth", "values": [ { "name": "phone" }, { "name": "totp" }, { "name": "webauthn" } ] }, "oauth_authorization_status": { "schemaName": "auth", "values": [ { "name": "approved" }, { "name": "denied" }, { "name": "expired" }, { "name": "pending" } ] }, "oauth_client_type": { "schemaName": "auth", "values": [ { "name": "confidential" }, { "name": "public" } ] }, "oauth_registration_type": { "schemaName": "auth", "values": [ { "name": "dynamic" }, { "name": "manual" } ] }, "oauth_response_type": { "schemaName": "auth", "values": [ { "name": "code" } ] }, "one_time_token_type": { "schemaName": "auth", "values": [ { "name": "confirmation_token" }, { "name": "email_change_token_current" }, { "name": "email_change_token_new" }, { "name": "phone_change_token" }, { "name": "reauthentication_token" }, { "name": "recovery_token" } ] }, "request_status": { "schemaName": "net", "values": [ { "name": "ERROR" }, { "name": "PENDING" }, { "name": "SUCCESS" } ] }, "content_types": { "schemaName": "public", "values": [ { "name": "comments" }, { "name": "news" }, { "name": "projects" }, { "name": "questions" }, { "name": "research" } ] }, "notification_action_types": { "schemaName": "public", "values": [ { "name": "newComment" }, { "name": "newContent" }, { "name": "newReply" } ] }, "notification_content_types": { "schemaName": "public", "values": [ { "name": "comment" }, { "name": "comments" }, { "name": "library" }, { "name": "news" }, { "name": "projects" }, { "name": "questions" }, { "name": "reply" }, { "name": "research" }, { "name": "researchUpdate" }, { "name": "research_updates" } ] }, "notification_source_content_type": { "schemaName": "public", "values": [ { "name": "library" }, { "name": "news" }, { "name": "projects" }, { "name": "questions" }, { "name": "research" }, { "name": "researchUpdate" }, { "name": "research_updates" } ] }, "research_status": { "schemaName": "public", "values": [ { "name": "archived" }, { "name": "complete" }, { "name": "in-progress" } ] }, "useful_content_types": { "schemaName": "public", "values": [ { "name": "comments" }, { "name": "news" }, { "name": "projects" }, { "name": "questions" }, { "name": "research" } ] }, "action": { "schemaName": "realtime", "values": [ { "name": "DELETE" }, { "name": "ERROR" }, { "name": "INSERT" }, { "name": "TRUNCATE" }, { "name": "UPDATE" } ] }, "equality_op": { "schemaName": "realtime", "values": [ { "name": "eq" }, { "name": "gt" }, { "name": "gte" }, { "name": "in" }, { "name": "lt" }, { "name": "lte" }, { "name": "neq" } ] }, "buckettype": { "schemaName": "storage", "values": [ { "name": "ANALYTICS" }, { "name": "STANDARD" }, { "name": "VECTOR" } ] } } } ================================================ FILE: .snaplet/library.json ================================================ [ { "title": "Basic Shredder Machine", "description": "Learn how to build a basic plastic shredder machine for breaking down plastic waste into small flakes.", "difficulty_level": "beginner", "time": "2-3 days", "moderation": "accepted", "file_link": "https://example.com/shredder-plans.pdf", "comments": [ { "aldaplaskett48": "This is a great starter project! I built one last month and it works perfectly for PET bottles." }, { "sampathpini67": "The motor specifications are really helpful. Make sure to get the right RPM for safety." } ], "steps": [ { "title": "Gather Materials", "description": "Collect all the necessary materials including steel frame, motor, and cutting blades.", "images": ["shredder-materials.jpg"], "video_url": null }, { "title": "Build the Frame", "description": "Construct the main frame structure using welding techniques.", "images": ["frame-construction.jpg"], "video_url": "https://example.com/frame-build.mp4" } ] }, { "title": "Injection Molding Machine", "description": "Build an injection molding machine to create new products from recycled plastic flakes.", "difficulty_level": "advanced", "time": "1-2 weeks", "moderation": "accepted", "file_link": "https://example.com/injection-molder-plans.pdf", "comments": [ { "galenagiugovaz15": "This machine is a game changer! I've been making phone cases and small containers with it." }, { "veniaminjewell33": "The temperature control is crucial. Don't skip the calibration steps." }, { "cortneybrown81": "Amazing project! The documentation is very detailed and easy to follow." } ], "steps": [ { "title": "Prepare the Barrel", "description": "Machine and prepare the injection barrel with proper heating elements.", "images": ["barrel-prep.jpg", "heating-elements.jpg"], "video_url": null }, { "title": "Install Control System", "description": "Set up the temperature and pressure control systems.", "images": ["control-panel.jpg"], "video_url": "https://example.com/control-setup.mp4" }, { "title": "Test and Calibrate", "description": "Run initial tests and calibrate the machine for optimal performance.", "images": ["testing.jpg"], "video_url": "https://example.com/calibration.mp4" } ] }, { "title": "Simple Extrusion Machine", "description": "Create continuous shapes and filaments from recycled plastic using this extrusion machine design.", "difficulty_level": "intermediate", "time": "4-5 days", "moderation": "accepted", "file_link": "https://example.com/extruder-plans.pdf", "comments": [ { "melisavang56": "Perfect for making 3D printing filament! The output quality is surprisingly good." }, { "lianabegam24": "I modified the design slightly for my needs and it works great for making plastic sheets." } ], "steps": [ { "title": "Build the Screw Mechanism", "description": "Construct the main screw that will push and heat the plastic material.", "images": ["screw-assembly.jpg"], "video_url": null }, { "title": "Create the Heating Chamber", "description": "Build the heated chamber where plastic will be melted and formed.", "images": ["heating-chamber.jpg"], "video_url": "https://example.com/chamber-build.mp4" } ] }, { "title": "Compression Molding Press", "description": "Build a compression molding press for creating flat products like plates and sheets.", "difficulty_level": "beginner", "time": "1-2 days", "moderation": "accepted", "file_link": "https://example.com/compression-press-plans.pdf", "comments": [ { "akromstarkova72": "Super simple build! Great for beginners who want to start with something achievable." } ], "steps": [ { "title": "Assemble the Press Frame", "description": "Build the main frame structure for the compression press.", "images": ["press-frame.jpg"], "video_url": null }, { "title": "Install Hydraulic System", "description": "Set up the hydraulic system for applying pressure during molding.", "images": ["hydraulic-setup.jpg"], "video_url": "https://example.com/hydraulic-install.mp4" } ] }, { "title": "Plastic Waste Sorting Guide", "description": "A comprehensive guide on how to properly sort and prepare plastic waste for recycling.", "difficulty_level": "beginner", "time": "30 minutes", "moderation": "accepted", "file_link": "https://example.com/sorting-guide.pdf", "comments": [ { "mirzoblazkova19": "This guide saved me so much time! The identification charts are really helpful." }, { "jereerickson92": "Essential reading before starting any plastic recycling project." } ], "steps": [ { "title": "Learn Plastic Types", "description": "Understand the different types of plastic and their recycling codes.", "images": ["plastic-types-chart.jpg"], "video_url": "https://example.com/plastic-identification.mp4" }, { "title": "Set Up Sorting System", "description": "Create an efficient system for sorting plastic waste by type and color.", "images": ["sorting-bins.jpg"], "video_url": null } ] } ] ================================================ FILE: .snaplet/questions.json ================================================ { "jereerickson92": [ { "title": "What is Precious Plastic?", "description": "Explain the mission and goals of the Precious Plastic initiative.", "comments": [ { "aldaplaskett48": "It's crazy how much plastic we're exposed to every day! Microplastics are literally in our food, water, and even the air. Scientists are still figuring out the long-term effects, but some studies suggest they can mess with hormones and digestion. Makes you think twice about drinking from plastic bottles!" }, { "sampathpini67": "The chemicals in plastic are no joke. BPA, phthalates, all that stuff—these can leach into food and drinks, and they've been linked to hormonal imbalances and fertility issues. And yet, plastic is everywhere! Hard to avoid completely, but switching to glass or stainless steel can help reduce exposure." }, { "galenagiugovaz15": "People don't realize how bad burning plastic is. The smoke from it releases toxic chemicals that can cause serious lung problems and even cancer. Where I live, some folks still burn their trash, and you can literally feel it in your chest when you breathe. It's awful!", "replies": [ { "aldaplaskett48": "I agree!" } ] }, { "aldaplaskett48": "Microplastics are literally everywhere—in our food, water, even the air. Kinda scary when you realize we’re basically consuming plastic every day without even knowing the full health effects." } ] }, { "title": "How does Precious Plastic help fight plastic pollution?", "description": "Describe the ways in which Precious Plastic contributes to reducing plastic waste.", "comments": [ { "aldaplaskett48": "outro" } ] }, { "title": "What types of machines does Precious Plastic offer?", "description": "List and describe the different machines available for recycling plastic." }, { "title": "How can individuals get involved with Precious Plastic?", "description": "Provide information on how people can participate in the Precious Plastic movement." }, { "title": "What materials can be processed using Precious Plastic machines?", "description": "Explain which types of plastic can be recycled using Precious Plastic machines." }, { "title": "How does the Precious Plastic community collaborate globally?", "description": "Describe the role of the global community in the Precious Plastic movement and how people connect." }, { "title": "What products can be made from recycled plastic?", "description": "Give examples of items that can be created using Precious Plastic's recycling process." }, { "title": "How can businesses benefit from Precious Plastic?", "description": "Explain how entrepreneurs and small businesses can use Precious Plastic's technology for commercial purposes." }, { "title": "What are the main challenges of plastic recycling?", "description": "Discuss the biggest obstacles faced in plastic recycling and how Precious Plastic addresses them." }, { "title": "Where can I find Precious Plastic workspaces near me?", "description": "Guide users on how to locate Precious Plastic workspaces in their region." } ], "aldaplaskett48": [ { "title": "How does plastic pollution impact marine life?", "description": "Explain the effects of plastic waste on ocean ecosystems and marine animals." }, { "title": "What are microplastics, and why are they a problem?", "description": "Describe what microplastics are, how they form, and their environmental and health impacts." }, { "title": "What are some sustainable alternatives to plastic?", "description": "List eco-friendly materials that can replace plastic in everyday products." }, { "title": "How does plastic contribute to climate change?", "description": "Discuss the relationship between plastic production, fossil fuels, and greenhouse gas emissions." }, { "title": "What policies and regulations exist to reduce plastic waste?", "description": "Provide an overview of international laws and local initiatives aimed at limiting plastic pollution." } ], "sampathpini67": [ { "title": "How long does plastic take to decompose in nature?", "description": "Explain the decomposition process of different types of plastic and their impact on ecosystems." }, { "title": "What role does recycling play in reducing plastic waste?", "description": "Discuss the importance of recycling in managing plastic waste and its effectiveness." }, { "title": "How does plastic pollution affect human health?", "description": "Describe the ways in which plastic exposure and pollution can impact human well-being." }, { "title": "What actions can individuals take to reduce plastic waste?", "description": "Provide practical steps for people to minimize their plastic consumption and waste." } ] } ================================================ FILE: .vscode/launch.json ================================================ { // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "Community Platform", "command": "bun run start", "request": "launch", "type": "node-terminal", "cwd": "${workspaceFolder}" }, { "name": "Community Platform Unit Tests", "command": "bun run test:unit", "request": "launch", "type": "node-terminal", "cwd": "${workspaceFolder}" }, { "name": "Attach by Process ID", "processId": "${command:PickProcess}", "request": "attach", "skipFiles": ["/**"], "type": "node" } ] } ================================================ FILE: .yarnrc.yml ================================================ checksumBehavior: update compressionLevel: mixed enableGlobalCache: false enableScripts: false logFilters: - code: YN0060 level: discard - code: YN0002 level: discard nmMode: hardlinks-local nodeLinker: node-modules yarnPath: .yarn/releases/yarn-4.12.0.cjs ================================================ FILE: CNAME ================================================ storybook.onearmy.earth ================================================ 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, socioeconomic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behaviour 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 behaviour 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 behaviour and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. 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 behaviours 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 behaviour may be reported by contacting the project team at [platform@onearmy.earth](mailto:platform@onearmy.earth?subject=contact%20from%20github%20conduct). 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 ================================================ # Contribution Guidelines Thanks for being here already! You'll find all the information you need to start contributing to the project. Make sure to read them before submitting your contribution. If you think something is missing, consider sending us a PR. ## 🍽 Summary - [Code of conduct](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-code-of-conduct) - [Getting started](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-getting-started) - [Project structure](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-project-structure) - [Branching](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-branching) - [Style guide](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-style-guide) - [Testing](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-testing) - [Joining the team](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-joining-the-team) - [Contributing with UX/UI Design](https://github.com/ONEARMY/community-platform/blob/master/CONTRIBUTING.md#-contributing-with-uxui-design) ## 👐 Code of Conduct This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behaviour to [platform@onearmy.earth](mailto:platform@onearmy.earth). Also check our [Team Principles](./docs/team-principles.md), which guide our work. ## 📟 Getting started ### Prerequisites - [Bun 1.3.10+](https://bun.sh/docs/installation) ### One time setup 1. Fork the repository. 2. Clone the project from the fork you have created previously at first step: ``` git clone https://github.com//community-platform.git ``` 3. Install dependencies ``` bun install ``` 4. [Create a local supabase instance](./docs/supabase.md) 5. Run the app ``` bun start ``` ## ⚙️ Technology Our main technologies are [React Router 7](./docs/react-router-7.md) and [Supabase](./docs/supabase.md) We try to document some important [Technical Decisions](./docs/technical-decisions.md). ## 🏠 Project Structure - **`src`** - **`routes`** : app routes, including api routes. - **`pages`** : Main components for each page are located here. Each folder should correspond to a **feature** of the platform. - **`services`** : client-side services to interact with our api and server-side services to interact with supabase or other external service like patreon. - **`assets`** : contains assets such as icons/images. - **`utils`** : contains utility functions. - **`packages/components/`**: - general stateless components that compose the app. - **`packages/themes/`**: - theme definitions for presentation inherited by components - **`packages/cypress/`** : contains the test automation of End-to-end tests. - **`shared`** : contains mainly type definitions ## 🌳 Branching We have a single `master` branch which is linked to production. QA sites will be created from Pull Request branches upon adding the `Review allow-preview ✅` label (Mainteiners only). To contribute, you should fork our `master` branch and create a branch from your own fork. When your changes are ready, submit a PR from your fork branch, into our `master` branch. It is recommended to update your `master` fork regularly to avoid conflicts. ## 🤓 Style guide We use `Biome` please install the extension on your IDE https://biomejs.dev/guides/editors/first-party-extensions/. Add to your `.vscode/settings.json`: ``` "editor.codeActionsOnSave": { "source.organizeImports.biome": "explicit", "source.fixAll.biome": "explicit" } ``` Also make sure you set your vscode default formatter to Biome (CTRL + SHIFT + P > Format Document With... -> Configure Default Formatter -> Biome). Running `bun run format` from the project root prior to committing will ensure the code you're adding is formatted to align with the standards of this project. We expect code to follow standard practices: - Simple, clear and self-documenting code, with meaningful names. - avoid deep nesting and prop-drilling and avoid unnecessary abstractions. - Create additional functions if it would aid code readability. - Extend existing functions over creating new ones. - Don't add code comments. ## ✅ Testing Writing tests is crucial for maintaining a robust and reliable codebase. Tests provide a safety net that helps catch errors and unintended behaviour changes before they reach production. Keep them simple and as easy to read as the other code. Test Writing Guidelines: - Unit Tests are useful for code that has logic, it could be a frontend component or a server-side function. - E2E tests are useful for full feature tests, which require database interaction. - Focus on testing significant aspects and edge cases, not for the sake of coverage. - Adhere to the testing conventions and styles established in the project. - If your changes affect existing functionality, update the corresponding tests to reflect the new behaviour. ## Security We have evolved our security practices with the introduction of React-Router 7, Fly.io and Supabase in 2024/2025. A few practices we ensure: - Database connection is done server-side only - Secrets are managed server-side on fly.io - We use `helmet` for CSP and other security checks - Reduce and keep packages up-to-date to reduce potential vulnerabilities ## First time contributing? Early on, and especially when contributing for the first time, please only submit and work on a single issue at a time. It's likely there's lots of little changes we'd like you to make while you get up to speed with how we work and what the code already does. ## 🤝 Joining the team We are always open to have more people involved. If you would like to contribute more often and become a maintainer, we would love to welcome you to the team. [Join us on Discord](https://discord.gg/gJ7Yyk4) and checkout the [development](https://discord.com/channels/586676777334865928/938781727017558018) channel. Feel free to introduce yourself and outline: 1. How much time you feel you can dedicate to the project 2. Any relevant experience working with web technologies We ask this so that we can better understand how you might fit in with the rest of the team, and maximise your contributions. Here are more details about what to expect from becoming a [Maintainer](./docs/maintainers.md) ## 🎨 Contributing with UX/UI Design We always welcome UX/UI design contributions in various shapes and forms. Whether it's designing new features, giving design feedback or optimising the existing flows. The best way to start would be to look at [open design issues](https://github.com/ONEARMY/community-platform/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22Task%3A%20Design%22) here in our GitHub Repository to see if anything sparks your interest. Another way is to get in touch with us [through Discord](https://discord.gg/p4hWHYeG), the introduce-yourself channel is a good place for that. :) Then we can chat about what would be interesting for you to help out with, as well as what we are currently working on. In the meantime you can check out our [UI Component library in Storybook](https://storybook.onearmy.earth). ================================================ FILE: Dockerfile ================================================ # syntax = docker/dockerfile:1 FROM oven/bun:1.3.10 AS base LABEL fly_launch_runtime="React Router" # App lives here WORKDIR /app # Set production environment ENV NODE_ENV="production" # Add CircleCI context variables as ARGs ARG VITE_BRANCH ARG VITE_SENTRY_DSN # Throw-away build stage to reduce size of final image FROM base AS build # Install packages needed to build node modules RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential pkg-config python-is-python3 # Copy source code ADD . . # Install packages RUN bun install RUN --mount=type=secret,id=VITE_BRANCH \ --mount=type=secret,id=VITE_SENTRY_DSN \ VITE_BRANCH="$(cat /run/secrets/VITE_BRANCH)" && \ VITE_SENTRY_DSN="$(cat /run/secrets/VITE_SENTRY_DSN)" && \ echo "VITE_BRANCH=\"${VITE_BRANCH}\"" >> .env && \ echo "VITE_SENTRY_DSN=\"${VITE_SENTRY_DSN}\"" >> .env # Build application (skip tsc type checking - already done in CI) RUN bun run build:shared && bun run build:themes && bun run build:components && bun run build:vite # Final stage for app image FROM base # Copy built application COPY --from=build /app /app # Start the server by default, this can be overwritten at runtime ENV PORT=3000 EXPOSE 3000 CMD [ "bun", "./server.js" ] ================================================ FILE: Dockerfile.preview ================================================ # syntax = docker/dockerfile:1 FROM oven/bun:1.3.10 AS base LABEL fly_launch_runtime="React Router" # App lives here WORKDIR /app # Set production environment ENV NODE_ENV="production" # Throw-away build stage to reduce size of final image FROM base AS build # Cache-busting arg - changes on each commit ARG COMMIT_SHA # Install packages needed to build node modules RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential pkg-config python-is-python3 # Copy source code COPY . . # Install packages RUN bun install RUN echo "VITE_SITE_VARIANT=\"preview\"" >> .env # Build application (skip tsc type checking - already done in CI) RUN bun run build:shared && bun run build:themes && bun run build:components && bun run build:vite # Final stage for app image FROM base # Copy built application COPY --from=build /app /app # Start the server by default, this can be overwritten at runtime ENV PORT=3000 EXPOSE 3000 CMD [ "bun", "./server.js" ] ================================================ FILE: FUNDING.yml ================================================ # These are supported funding model platforms patreon: one_army open_collective: onearmy ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2026 OneArmyWorld Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE 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: README.md ================================================ [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/from-referrer/) [![Build Status](https://circleci.com/gh/ONEARMY/community-platform/tree/master.svg?style=shield)](https://app.circleci.com/pipelines/github/ONEARMY/community-platform?branch=master) [![GitHub license](https://badgen.net/github/license/ONEARMY/community-platform)](https://github.com/ONEARMY/community-platform/blob/master/LICENSE) [![GitHub license](https://badgen.net/github/tag/ONEARMY/community-platform)](https://github.com/ONEARMY/community-platform/blob/master/LICENSE) [![GitHub contributors](https://img.shields.io/github/contributors/ONEARMY/community-platform)](https://github.com/ONEARMY/community-platform/graphs/contributors/) [![discord](https://badges.aleen42.com/src/discord.svg)](https://discord.gg/gJ7Yyk4) # 🔗   tl;dr Quick Links - [Our Project Website](https://www.onearmy.earth/community-platform) - [Precious Plastic Community (live site)](http://community.preciousplastic.com/) - [Project Kamp Community (live site)](http://community.projectkamp.com/) - [Developer documentation](/CONTRIBUTING.md) # 🌍   Community Platform Welcome to our Community Platform! At [One Army](https://www.onearmy.earth) we are building this platform to help unite people and contribute to social & environmental projects, such as [Precious Plastic](https://preciousplastic.com), [Project Kamp](https://projectkamp.com/) and [Fixing Fashion](https://fixing.fashion). A platform to connect, educate and empower our global community (65K) to solve society's greatest challenges. Together. ## 👀   Why? For the past 5+ years we’ve worked together with thousands of people from all over the world on open hardware projects to tackle some of the most pressing environmental issues, building machines and tools to fix the mess. The more we worked on these projects, the more we realised that there are two main hurdles to the success of a project: - A project's success is closely linked to its community, and for a new project starting up, finding and creating their own strong community is often a time-consuming activity that can take lots of resources. - While working on a project we often find ourselves having to use a multitude of digital tools that are often incomplete, disconnected, privately owned and not open source. This platform aims to tackle these problems by creating a strong unified community for the different projects under its umbrella and offering the necessary tools to collaborate and connect in one single place. Free and open-source. ## ⚡️   What is this platform? Our platform helps communities to grow and makes it easier to collaborate on environmental projects in one single place. A place where people can meet, help each other, ask and answer questions, share their innovative ways of fixing problems, discover people around them, connect locally and more. It aims to provide the tools to connect both online and offline. Amongst other features we have a library of projects, research, questions, news updates and a map. ### Have a look on our [website](https://www.onearmy.earth/community-platform) to have a clear overview ## 👐 Open Source Society and the environment are kind of screwed 💩 in many ways. We think free knowledge and open source are the fastest and most efficient ways to bring about innovation to tackle some of the most pressing humanity’s fuck ups. Simple. ## 🤝   Contributions Contributions, issues and feature requests are very welcome. Please make sure to read the [Contributing Guide](/CONTRIBUTING.md) before making a pull request. It also covers lots of handy additional information such as setting up a local server, or finding [good first issues](https://github.com/ONEARMY/community-platform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Good+first+issue%22) to work on. To startup the project locally use `bun start`, but before that, follow [Getting Started](/packages/documentation/docs/supabase.md) If needed you can [drop us a line here](mailto:platform@onearmy.earth?subject=contact%20from%20github) 👋 Or join our [Discord channel](https://discord.gg/gJ7Yyk4) ## Contributors ✨ Thanks go to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
Dave Hakkens
Dave Hakkens

🎨 🤔 📆 💪
Chris Clarke
Chris Clarke

💻 💪
Luke Watts
Luke Watts

💻 💪
Mauro Bosetti
Mauro Bosetti

📖
patrycjapraczyk
patrycjapraczyk

💻
Ted Spare
Ted Spare

💻
Elias Velardez
Elias Velardez

💻
Alfonso
Alfonso

💻 💪
Xyli0
Xyli0

💻
Laian Braum
Laian Braum

💻
osouthwell-scottlogic
osouthwell-scottlogic

💻
Asheer Rizvi
Asheer Rizvi

💻
Frank Noirot
Frank Noirot

🎨
Lucas Becker
Lucas Becker

💻
Conrad Schilbe
Conrad Schilbe

💻
Thakur Karthik
Thakur Karthik

💻
Daniel T. Rodrigues
Daniel T. Rodrigues

💻
Adrian Duke
Adrian Duke

💻
Alyssa Helgason
Alyssa Helgason

💻
Kieb
Kieb

💻
Kovechenkov Vladislav
Kovechenkov Vladislav

💻
Devtato
Devtato

💻
Ned O'Hara
Ned O'Hara

💻
Sophia Nguyen
Sophia Nguyen

💻
Eva Killenberg
Eva Killenberg

💻 💪
Sean Thompson
Sean Thompson

💻 💪
Nguyễn Văn Đỏ
Nguyễn Văn Đỏ

💻
KungRaseri
KungRaseri

📖
Mihai-Cristian Bâltac
Mihai-Cristian Bâltac

💻
Cullum Deighton
Cullum Deighton

💻
Dawid Skowroński
Dawid Skowroński

💻
Jonathan Boiser
Jonathan Boiser

💻
benfurber
benfurber

💻 💪 📖
Alimurtuza
Alimurtuza

💻
Askell
Askell

💻
Manacés Pereira
Manacés Pereira

💻
Georg Karl
Georg Karl

💻
asdFletcher
asdFletcher

💻
Abhishek Bagiyal
Abhishek Bagiyal

💻
Zack
Zack

💻
Bart Enkelaar
Bart Enkelaar

💻
Lávio Vale
Lávio Vale

💻
Manuel Rodriguez Urdapilelta
Manuel Rodriguez Urdapilelta

💻
Ben Lipton
Ben Lipton

💻
Ayachi Sharma
Ayachi Sharma

📖
Arthur Tyukayev
Arthur Tyukayev

💻
Jacob Gable
Jacob Gable

💻
Ana Margarida Silva
Ana Margarida Silva

💻
cjh1212
cjh1212

💻
David Germain
David Germain

📖 💻
AJOTKA
AJOTKA

💻
Ray Liu
Ray Liu

💻
Erkan Erkişi
Erkan Erkişi

💻
denyilm
denyilm

💻
Koen
Koen

💻
Guy Ribak
Guy Ribak

💻
Cosimo Chetta
Cosimo Chetta

💻
Roger Chick
Roger Chick

💻
Ignas
Ignas

💻
Mário Nunes
Mário Nunes

💻 💪
Kevin Masson
Kevin Masson

💻
Darigov Research
Darigov Research

📖
Zachary Doucet
Zachary Doucet

💻
viracoding
viracoding

💻
Gashmoh
Gashmoh

💻
dariusmihut
dariusmihut

💻
David Custódio
David Custódio

💻
Elias Jörgensen
Elias Jörgensen

💻
devChary
devChary

💻
Fabi
Fabi

💻
Robert
Robert

💻
Phillip Atkinson
Phillip Atkinson

💻
Andris
Andris

💻
Edward Andress
Edward Andress

💻
Túlio Luz
Túlio Luz

💻
Louis
Louis

💻
Venu G Soganadgi
Venu G Soganadgi

💻
Augustindou
Augustindou

💻
Prashik Gamre
Prashik Gamre

💻
Robert
Robert

💻
Kim Skovhus Andersen
Kim Skovhus Andersen

💻
leoasimon
leoasimon

💻
Lucas Rojas
Lucas Rojas

💻
Luís Santos
Luís Santos

💻
Janik Köppel
Janik Köppel

💻
Jonathan Goodman
Jonathan Goodman

💻
LahuenGR
LahuenGR

💻
José Antonio Concepción
José Antonio Concepción

💻
Joshua Kelechi
Joshua Kelechi

💻
Michael Chen
Michael Chen

💻
Matúš Motyka
Matúš Motyka

💻
Cypher Pepe
Cypher Pepe

📖
Dalibor Mrška
Dalibor Mrška

🎨
Skylar Ray
Skylar Ray

📖
Johannes Roß
Johannes Roß

💻
Devkuni
Devkuni

📖
Bilog WEB3
Bilog WEB3

📖
Elliot Bradbury
Elliot Bradbury

💻
José
José

💻
Maxim Evtush
Maxim Evtush

📖
kilavvy
kilavvy

📖
Mohit Sharma
Mohit Sharma

💻
Paula Fenner
Paula Fenner

💻
Ismael Abu-jadur Garcia
Ismael Abu-jadur Garcia

💻
Nick Wilder
Nick Wilder

💻
James Niken
James Niken

💻
omahs
omahs

📖
GTaf
GTaf

💻 📖
Corey Johnson
Corey Johnson

💻
Ian Webster
Ian Webster

💻
JC Sergent
JC Sergent

💻
Fel4
Fel4

💻
Matteo Bucciol
Matteo Bucciol

💻
ernestorbemx
ernestorbemx

💻
jproberson
jproberson

💻
Abhishek Singh
Abhishek Singh

💻
Chris Staunton
Chris Staunton

💻
Rej Ect
Rej Ect

📖
othman-shamla
othman-shamla

💻
RSGB
RSGB

💻
Georgios Metaxakis
Georgios Metaxakis

💻
Trevin Chow
Trevin Chow

💻
Botho
Botho

💻
This project adopted the [all-contributors](https://allcontributors.org) specification in June 2022. Contributions of any kind are welcome! ================================================ FILE: SECURITY.md ================================================ # Security Policy # Reporting Security Issues If you believe you have found a security vulnerability on our platform, we encourage you to let us know right away. We will investigate all legitimate reports and do our best to quickly fix the problem. Submit your report to platform@onearmy.earth (one issue per report) and respond to the report with any updates. ### Reporting a Vulnerability We ask that: - You give us reasonable time to investigate and mitigate an issue you report before making public any information about the report or sharing such information with others. - You do not interact with an individual account (which includes modifying or accessing data from the account) if the account owner has not consented to such actions. - You make a good faith effort to avoid privacy violations and disruptions to others, including (but not limited to) destruction of data and interruption or degradation of our services. - You do not exploit a security issue you discover for any reason. (This includes demonstrating additional risk, such as attempted compromise of sensitive company data or probing for additional issues). - You do not violate any other applicable laws or regulations. ================================================ FILE: biome.json ================================================ { "$schema": "https://biomejs.dev/schemas/2.4.4/schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "files": { "ignoreUnknown": false, "includes": [ "**", "!**/build", "!**/storybook-static", "!**/dist", "!**/shared/lib", "!**/cypress", "!**/.react-router", "!**/*.md", "!**/*.spec.*", "!**/*.stories.*", "!**/*.test.*", "!**/styles/*", "!**/supabase" ] }, "formatter": { "enabled": true, "formatWithErrors": false, "indentStyle": "space", "indentWidth": 2, "lineEnding": "lf", "lineWidth": 100, "attributePosition": "auto", "bracketSameLine": false, "bracketSpacing": true, "expand": "auto", "useEditorconfig": true, "includes": [ "**", "!**/build", "!**/storybook-static", "!**/dist", "!shared/lib", "!**/.react-router", "!**/*.md" ] }, "linter": { "enabled": true, "rules": { "recommended": false, "complexity": { "noAdjacentSpacesInRegex": "error", "noExtraBooleanCast": "error", "noUselessCatch": "error", "noUselessEscapeInRegex": "error", "noUselessTypeConstraint": "error" }, "correctness": { "noChildrenProp": "error", "noConstAssign": "error", "noConstantCondition": "error", "noEmptyCharacterClassInRegex": "error", "noEmptyPattern": "error", "noGlobalObjectCalls": "error", "noInnerDeclarations": "error", "noInvalidConstructorSuper": "error", "noInvalidUseBeforeDeclaration": "off", "noNonoctalDecimalEscape": "error", "noPrecisionLoss": "error", "noSelfAssign": "error", "noSetterReturn": "error", "noSwitchDeclarations": "error", "noUndeclaredVariables": "error", "noUnreachable": "error", "noUnreachableSuper": "error", "noUnsafeFinally": "error", "noUnsafeOptionalChaining": "error", "noUnusedImports": "warn", "noUnusedLabels": "error", "noUnusedVariables": "error", "useIsNan": "error", "useJsxKeyInIterable": "error", "useValidForDirection": "error", "useValidTypeof": "error", "useYield": "error" }, "security": { "noDangerouslySetInnerHtmlWithChildren": "error" }, "style": { "noCommonJs": "off", "noInferrableTypes": "off", "noNamespace": "off", "noNonNullAssertion": "off", "noRestrictedImports": "error", "noRestrictedTypes": "error", "useArrayLiterals": "error", "useAsConstAssertion": "error", "useBlockStatements": "off" }, "suspicious": { "noAlert": "error", "noAsyncPromiseExecutor": "error", "noCatchAssign": "error", "noClassAssign": "error", "noCommentText": "error", "noCompareNegZero": "error", "noConsole": "off", "noControlCharactersInRegex": "error", "noDebugger": "error", "noDuplicateCase": "error", "noDuplicateClassMembers": "error", "noDuplicateElseIf": "error", "noDuplicateJsxProps": "error", "noDuplicateObjectKeys": "error", "noDuplicateParameters": "error", "noEmptyBlockStatements": "off", "noExplicitAny": "off", "noExtraNonNullAssertion": "error", "noFallthroughSwitchClause": "error", "noFunctionAssign": "error", "noGlobalAssign": "error", "noImportAssign": "error", "noIrregularWhitespace": "error", "noMisleadingCharacterClass": "error", "noMisleadingInstantiator": "error", "noNonNullAssertedOptionalChain": "error", "noPrototypeBuiltins": "error", "noRedeclare": "error", "noShadowRestrictedNames": "error", "noSparseArray": "error", "noUnsafeDeclarationMerging": "error", "noUnsafeNegation": "error", "noUselessRegexBackrefs": "error", "noWith": "error", "useGetterReturn": "error", "useNamespaceKeyword": "error" } }, "includes": [ "**", "!**/node_modules", "!**/functions", "!**/build", "!**/lib", "!**/storybook-static", "!**/dist", "!shared/lib" ] }, "javascript": { "globals": ["Bun"], "formatter": { "jsxQuoteStyle": "double", "quoteProperties": "asNeeded", "trailingCommas": "all", "semicolons": "always", "arrowParentheses": "always", "bracketSameLine": false, "quoteStyle": "single", "attributePosition": "auto", "bracketSpacing": true } }, "html": { "formatter": { "indentScriptAndStyle": false, "selfCloseVoidElements": "always" } }, "assist": { "enabled": true, "actions": { "source": { "organizeImports": "on" } } } } ================================================ FILE: codecov.yml ================================================ ignore: - "packages/documentation" coverage: status: project: default: target: auto threshold: 1% patch: off comment: require_changes: true ================================================ FILE: docs/circle-ci.md ================================================ # Deployment via CircleCI We use CircleCI to handle automated build-test-deploy cycles when PRs and releases are created from the GitHub Repository ## Environment Variables Environment variables should be set via [CircleCI Contexts](https://circleci.com/docs/2.0/contexts/) ================================================ FILE: docs/db-seeding.md ================================================ # Supabase seeding This file describes how supabase seeding works. ## Snaplet Snaplet is a tool used to seed mostly PostgresSQL databases. Read through their documentation to understand better how it works here: https://snaplet-seed.netlify.app/seed/getting-started/quick-start To initialize snaplet: ```sh npx @snaplet/seed init ``` To sync snaplet (essentially generate snaplet models and docs from `seed.config.ts`): > Note: every time the config file is changed, this command has to be ran. ```sh bun run db:reset ``` Or if you need to run the commands separately: ```sh npx supabase db reset npx @snaplet/seed sync npx tsx seed.ts > seed.sql bun run format ``` ================================================ FILE: docs/maintainers.md ================================================ ### ⚡️ Things that Maintainers do: #### Review incoming code from Contributors 1. Validate code quality and give feedback as needed. 2. Help resolve problems. 3. Ask to add tests as needed. 4. Add new contributors to our [contributors list](#recognising-contributors). ### 🔧 Things that Core Maintainers do: #### Release changes - The PR is merged into the `master` branch. - `master` branch triggers an automated CircleCI build. - Production deployment requires approval by a maintainer on CircleCI. - After approval, an automated build deploys to production sites. #### Recognising Contributors We have adopted [all contributors](https://allcontributors.org/) and their tooling for managing the contributors listing on the [project README.md](https://github.com/ONEARMY/community-platform/blob/master/README.md). After merging a new contributors PR: 1. Add a comment to the merged PR mentioning the bot, contributor and their contribution [type](https://allcontributors.org/docs/en/emoji-key), for example: `@all-contributors add @username for code`. 2. A PR will be automatically raised, [example](https://github.com/ONEARMY/community-platform/pull/1952). 3. The PR raised by the `All Contributors` bot will need to be merged with admin privileges as the required CI skips are deliberately skipped. ### Payment There is a hourly pay for Maintainers. Aimed at developers who help more consistently. If you're interested to become a maintainer feel free to reach out on [Discord](https://discord.gg/gJ7Yyk4). ================================================ FILE: docs/pwa-setup.md ================================================ # PWA Setup Documentation ## Overview This project has Progressive Web App (PWA) capabilities configured for React Router 7 with **SSR mode**. **Important:** React Router 7 SSR doesn't generate `index.html` (server renders HTML dynamically), so the PWA uses a `NetworkFirst` caching strategy for navigation instead of precaching static HTML. ## Configuration 1. **vite.config.ts** - PWA plugin configuration 2. **src/root.tsx** - Manifest links added 3. **src/entry.client.tsx** - Service worker registration ## React Router 7 SSR Considerations With SSR mode (`ssr: true` in react-router.config.ts), there is **no static `index.html` file** generated. The PWA configuration uses: - `navigateFallback: null` - Disables static navigation fallback - `NetworkFirst` strategy for navigation requests - Server handles HTML rendering, cached for offline fallback - Asset precaching still works for JS/CSS/images This is different from SPA mode where a static `index.html` would be precached. ## Icons and webmanifest We need to serve icons and webmanifest dynamically as we are a multi-tenant app. `/manifest.webmanifest` route generates the manifest from `tenant_settings` `tenant_settings` now has a `pwa_icons` column with the 5 icon sizes required. ## Development PWA features are **disabled in development** mode because React Router 7's dev server conflicts with the service worker registration. To test it locally, run `bun run build` and then `bun cross-env NODE_ENV=production bun ./server.js`. Don't forget to build it again on every change. ================================================ FILE: docs/react-router-7.md ================================================ ### What is React Router 7? A React Fullstack Framework that provides server-side rendering and an API Layer. https://reactrouter.com/start/modes#framework ## Routing Each route is a normal React component file that should include a [loader](https://reactrouter.com/start/framework/data-loading#server-data-loading) function, that function runs exclusively on the server (or clientLoader for browser only). Parts of the component might be rendered client side, for that we can use React.lazy or wrap them with `` component from `remix-utils`. Additionally, routes could also export [Links](https://reactrouter.com/api/components/Links#links) and [Meta](https://reactrouter.com/api/components/Meta#meta) functions that will be added to the html head. For the API, we can use [action/loader](https://reactrouter.com/start/framework/actions#server-actions) routes. ================================================ FILE: docs/supabase.md ================================================ # What is Supabase? Supabase is an open source Firebase alternative, based on Postgres. ## Getting Started ### Local supabase instance To install the supabase locally, follow https://supabase.com/docs/guides/local-development/cli/getting-started?queryGroups=platform&platform=windows Installing as a dev dependency doesn't always work well, so it is recommended to install for your OS. Install Docker Desktop. Make sure you have the docker app open. Run `supabase start` (Ensure you run it on the project folder root.) Run `supabase status` Create a .env.local file at the project root (same level as .env) and fill in the keys with values from the command above: ``` SUPABASE_API_URL= SUPABASE_KEY= SUPABASE_SERVICE_ROLE_KEY= ``` Run `bun run db:seed` to run the DB migration scripts and update your local database schema. You will have to run this again whenever there are DB schema changes. For more in-depth seeding, please check [Seeding](./db-seeding.md). Now you can start the project with `bun start`. To sign-up locally, you can get the email confirmation link at http://localhost:54324/monitor ### Updating local supabase https://supabase.com/docs/guides/local-development/cli/getting-started?queryGroups=platform&platform=macos#updating-the-supabase-cli ### Using online Supabase free version #### Create your Supabase instance You can also use the online Supabase free version. For this, create your Supabase account at https://supabase.com and create a new project. Install the Supabase CLI : https://supabase.com/docs/guides/local-development/cli/getting-started #### Link your Supabase instance and push the db schema First, connect to the Supabase CLI using `supabase login`. Get your project-id from your Supabase project, you can find it in the project settings general section. Now from the project root, run `supabase link --project-ref your-project-id`. Finally, push your schema using `supabase db push`. To finish you should fill the .env.local file with the values from the "Data API" section of your project settings: ``` SUPABASE_API_URL= SUPABASE_KEY= SUPABASE_SERVICE_ROLE_KEY= ``` ## Migrations We use Supabase Declarative Schemas feature. Schema changes must be made in the /supabase/schemas folder, following the current pattern (1 sql file per feature). After making the schema changes, a migration file needs to be generated, use this command: `supabase db diff --file [migration_name]` ## Running Cypress Tests Running cypress tests locally will use the local database, while running on CI will use the QA database. For each test run, a new tenant_id is generated, which has a few benefits: - ensures no conflicts between parallel test runs - easier to cleanup - if the data isn't cleaned for some reason, it won't affect other runs For each test file, there should be a `before` and `after` block to, respectively, seed and clean the database. Create a .env.local file at the packages/cypress folder SUPABASE_API_URL=your_api_key (probably http://127.0.0.1:54321) SUPABASE_KEY=your_key SUPABASE_SERVICE_ROLE_KEY=your service key All done! Tests will use your local database. More info about how it works below. ## Supabase Edge Functions Currently used for customizing Auth Emails. Supabase Auth Hooks have a timeout of 5 seconds which can easily be exceeded. To reduce the risk, the `resend` call is not awaited. If it is exceeded, the user gets an error on the UI, but still receives the email and can continue his flow. Haven't managed to run the functions locally yet (contributions welcome!). ================================================ FILE: docs/team-principles.md ================================================ --- id: team-principles title: Team Principles --- The principles we hold close to guide the work we prioritise, the ways we work and the product we aim for. ## Team working values - Sustainability - Quality over quantity - Empower Contributors - Empowering good approaches (rather than restricting the bad) - Only invent new wheels when we absolutely have to ## Product values - Features for all projects - Should feel natural and easy to navigate projects' whole web presence - Our tech feels human - is playful and organic ## Foundational values - Stability and longevity - Accessible to users - Privacy - Legal Compliance - Separation from Big Tech ================================================ FILE: docs/technical-decisions.md ================================================ # Technical Decisions ## Multi-tenant Multi-tenancy is a requirement because: - Single login for all websites. - Easier maintenance and migrations. With supabase there are a few ways we can do multi-tenancy: 1. Have Multiple projects 2. Have a Single project with multiple schemas 3. Have a Single project, with 1 common schema, using RLS (Row Level Security) to ensure data separation Decision: 3. Why? - A single project is easier to manage and deploy - Same for a single common schema... and multiple schemas wouldn't give any security benefits - With RLS, we can ensure, based on an Environment Variable, only the respective rows of that tenant are queried. How? - Each table has a tenant_id column - On each request, to supabase (via its sdk) we pass a header 'x-tenant-id' with the process.env.TENANT_ID variable, which is set for each app, via Fly.io secret. ## Comment Counts Currently we can sort questions/research/library by the number of comments. With supabase there are a few ways we can do this: 1. A comment count view 2. A comment count materialized view 3. Triggers, where the main table has a comment_count column which is updated when a comment is inserted/deleted Decision: 3. Why? - A simple view isn't performant, would be querying for the total on each query. - A materialized view keeps state and is simple enough to update it, but doesn't support RLS. (Would be a better contender if it supported RLS) How? - Whenever a comment is created or deleted, it triggers the update_comment_count function. - The function checks the Operation kind (Insert/Delete), the source_type and source_id. - From the source_type it will update the according content total (library, research, questions) that matches the source_id ## Research Search Research is composed by: 1. research main item 2. research updates When searching for `research`, we want to search both the main item and its updates. To search using postgres textSearch, all info needs to be stored in a vector column. If updates were stored in the main research item as json, it would be straightforward to index. Having updates being stored in their own table is beneficial: - easier CRUD - enforcing a schema ensures data integrity and structure compared to json - easier to extract metrics The solution is to have a `tsvector` column that also contains update data which comes from the `research_updates` table. To build the column data, a `combined_research_search_fields(research_id)` is used to combine all data (main + updates). To automate indexing from `research_update` an INSERT/UPDATE trigger is needed. ## Notifications Notifications are sent in batch to avoid hitting resend limits. Batches of 100 emails, wait 1 second per batch. Notifications are sent to users who subscribed a specific kind of content, when said content is created. There is a `subscriptions` table to keep track of that. Current notification types: - new research upgrade - new comment (for each of these content types - news, research_updates, questions, projects) - new reply (to a comment) As such, there are 3 action types: `newContent`, `newComment`, `newReply`. Why not merge `newComment` and `newReply`? - They were the same at first, but the logic is different enough to warrant the separation. For instance, in a `newReply` the "parent" is a comment, and to obtain extra info (content title), need to "go up a level" twice. - Due to this complexity, a decision was made to cut showing the `newReply` respective content title (can be added again later if necessary). - This also made the logic of `newComment` more straighforward. Notification links are redirects: `/redirect?id={content_id}&ct={content_type}` where the `content_type` could be comments, questions, research_updates, news, questions, projects By using the redirect, we no longer need to generate the direct link in the "send notifications" step, which reduces complexity significantly. ## Subscription Store ### Architecture ``` ProfileStoreProvider (existing) └── SubscriptionStoreProvider (new) └── App Components ``` ### Pros - ✅ Clear separation of concerns - ✅ Independent lifecycle management - ✅ Easy to test in isolation - ✅ No impact on existing ProfileStore - ✅ Can be used anywhere in the component tree - ✅ Follows existing patterns in your codebase ### Cons - ⚠️ Adds one more context provider layer - ⚠️ Need to nest providers in layout ### Implementation Details **Store Structure:** ```typescript class SubscriptionStore { // Cache: Map<"contentType-itemId", subscriptionState> subscriptions: Map // Track loading states to prevent duplicate calls loadingStates: Map Methods: - checkAndCacheSubscription(contentType, itemId): Promise - subscribe(contentType, itemId): Promise - unsubscribe(contentType, itemId): Promise - isSubscribed(contentType, itemId): boolean | undefined - clearCache(): void } ``` **Provider Setup:** Place in `src/routes/_.tsx` inside `ProfileStoreProvider`: ```tsx {/* existing app */} ``` **Hook Usage:** ```tsx const { isSubscribed, subscribe, unsubscribe } = useSubscriptionStore(); const subscribed = isSubscribed('comments', 123); ``` ================================================ FILE: fly-ff.toml ================================================ app = 'community-platform-ff' primary_region = 'ams' [build] [http_service] internal_port = 3000 force_https = true auto_stop_machines = 'suspend' min_machines_running = 1 processes = ['app'] [env] VITE_BRANCH = "production" NODE_ENV = "production" [[vm]] memory = '2gb' cpu_kind = 'shared' cpus = 2 ================================================ FILE: fly-pk.toml ================================================ app = 'community-platform-pk' primary_region = 'ams' [build] [http_service] internal_port = 3000 force_https = true auto_stop_machines = 'off' processes = ['app'] [env] VITE_BRANCH = "production" NODE_ENV = "production" [[vm]] memory = '2gb' cpu_kind = 'shared' cpus = 2 ================================================ FILE: fly-pp.toml ================================================ app = 'community-platform-pp' primary_region = 'ams' [build] [http_service] internal_port = 3000 force_https = true auto_stop_machines = 'off' processes = ['app'] [env] VITE_BRANCH = "production" NODE_ENV = "production" [[vm]] memory = '2gb' cpu_kind = 'shared' cpus = 2 ================================================ FILE: fly-preview.toml ================================================ primary_region = 'ams' [build] dockerfile = "Dockerfile.preview" [http_service] internal_port = 3000 force_https = true auto_stop_machines = 'stop' processes = ['app'] [env] VITE_BRANCH = "preview" [[vm]] memory = '1gb' cpu_kind = 'shared' cpus = 2 ================================================ FILE: index.html ================================================ Precious Plastic Community
================================================ FILE: package.json ================================================ { "name": "one-army-community-platform", "repository": { "type": "git", "url": "https://github.com/ONEARMY/community-platform.git" }, "workspaces": ["shared", "packages/*"], "private": true, "main": "lib/index.js", "type": "module", "scripts": { "start": "bun ./server.js", "start-ci": "bun run build:shared && cross-env NODE_ENV=production bun ./server.js", "start:themes": "bun run --filter oa-themes dev", "start:components": "bun run --filter oa-components dev", "start:shared": "bun run --filter oa-shared dev", "start:prod": "bun run build:shared && bun run build:themes && bun run build:components && cross-env NODE_ENV=production && bun ./server.js", "start:platform": "bun ./server.js", "start:platform-ci": "cross-env NODE_ENV=production bun ./server.js", "build:themes": "bun run --filter oa-themes build", "build:components": "bun run --filter oa-components build", "build:vite": "react-router build", "build:shared": "bun run --filter oa-shared build", "build": "bun run build:shared && bun run build:themes && bun run build:components && bun run build:vite && tsc", "lint": "biome lint --write", "format": "biome check --write", "serve": "npx serve -s build", "test": "bun run --filter oa-cypress start", "test:components": "bun run build:shared && bun run build:themes && bun run build:components && bun run --filter oa-components test", "test:unit": "bun run build:themes && bun run build:components && vitest", "test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores", "storybook": "bun run --filter oa-components start", "storybook:build": "bun run build:shared && bun run build:themes && bun run --filter oa-components build:sb", "docs": "bun run --filter oa-docs start", "db:reset": "npx supabase db reset && bun run db:seed", "db:seed": "npx @snaplet/seed sync && npx tsx seed.ts > seed.sql", "commit": "git-cz", "prepare": "husky" }, "lint-staged": { "*.{js,ts,tsx,json}": ["biome check --write --no-errors-on-unmatched"] }, "browserslist": { "production": [">0.2%", "not dead", "not op_mini all", "iOS >= 15", "safari >= 15"], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version", "iOS >= 15" ] }, "resolutions": { "__note1__": "Pin react version consistently in all child workspaces", "react": "19.2.1", "react-dom": "19.2.1", "@types/react": "19.2.7" }, "dependencies": { "@emotion/cache": "^11.14.0", "@emotion/react": "^11.14.0", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.14.1", "@mui/base": "next", "@react-email/components": "0.3.3", "@react-router/fs-routes": "^7.13.1", "@react-spring/web": "^10.0.3", "@sentry/react": "10.40.0", "@sentry/react-router": "^10.40.0", "@supabase/ssr": "^0.7.0", "@supabase/storage-js": "^2.76.1", "@supabase/supabase-js": "^2.76.1", "@theme-ui/components": "^0.17.4", "@theme-ui/core": "^0.17.4", "canvas-confetti": "^1.9.3", "countries-list": "^3.2.2", "country-to-iso": "^1.6.1", "date-fns": "^4.1.0", "debounce": "^3.0.0", "final-form": "4.20.2", "final-form-arrays": "^3.0.2", "hono": "^4.12.7", "isbot": "^5.1.13", "jsonwebtoken": "^9.0.3", "keyv": "^5.1.2", "leaflet": "^1.9.4", "leaflet.markercluster": "^1.5.3", "lodash": "^4.17.23", "marked": "^17.0.1", "mobx": "6.15.0", "mobx-react": "9.2.1", "oa-components": "workspace:*", "oa-shared": "workspace:*", "oa-themes": "workspace:*", "react": "19.2.1", "react-country-flag": "^3.1.0", "react-dom": "19.2.1", "react-final-form": "6.5.3", "react-final-form-arrays": "^3.1.3", "react-ga4": "^2.1.0", "react-highlight-words": "^0.21.0", "react-leaflet": "^5.0.0", "react-leaflet-markercluster": "^5.0.0-rc.0", "react-router": "^7.13.1", "react-spring": "^10.0.3", "react-tooltip": "^5.30.0", "remix-utils": "^9.0.1", "resend": "6.5.2", "sharp": "^0.34.5", "sonner": "^2.0.7", "styled-system": "^5.1.5", "theme-ui": "^0.17.2", "tslog": "^4.10.2", "use-debounce": "^10.1.0", "yup": "^1.7.1" }, "devDependencies": { "@biomejs/biome": "2.4.4", "@faker-js/faker": "^10.1.0", "@hono/node-server": "^1.19.11", "@react-router/dev": "^7.13.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/git": "^10.0.1", "@snaplet/copycat": "^6.0.0", "@snaplet/seed": "0.98.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", "@types/bun": "^1.3.9", "@types/canvas-confetti": "^1", "@types/jsonwebtoken": "^9", "@types/leaflet": "^1.9.21", "@types/node": "^22.18.0", "@types/pg": "^8.11.11", "@types/react": "19.2.7", "@types/react-dom": "19.2.3", "@types/react-leaflet": "^1.1.6", "@types/react-leaflet-markercluster": "^2.0.0", "@types/styled-system": "^5.1.25", "@vitejs/plugin-react": "^5.1.1", "all-contributors-cli": "^6.20.0", "cross-env": "^7.0.3", "husky": "^9.1.7", "lint-staged": "^16.2.7", "pg": "^8.13.3", "supabase": "2.53.6", "tsx": "^4.20.3", "typescript": "^5.7.2", "vite": "^7.1.12", "vite-plugin-env-compatible": "^2.0.1", "vite-plugin-pwa": "^1.2.0", "vite-plugin-svgr": "^4.5.0", "vite-tsconfig-paths": "^5.1.4", "vitest": "^4.0.5", "workbox-window": "^7.4.0" }, "dependenciesMeta": { "cypress": { "built": true } }, "engines": { "bun": ">=1.3.10" } } ================================================ FILE: packages/components/.gitignore ================================================ node_modules storybook-static dist coverage reports ================================================ FILE: packages/components/.storybook/main.ts ================================================ import type { StorybookConfig } from '@storybook/react-vite'; import { createRequire } from 'module'; import { dirname, join } from 'path'; import { mergeConfig } from 'vite'; const require = createRequire(import.meta.url); const Config: StorybookConfig = { stories: ['../src/**/*.stories.tsx', '../src/*.mdx'], addons: [getAbsolutePath('@storybook/addon-links'), getAbsolutePath('@storybook/addon-docs')], framework: { name: getAbsolutePath('@storybook/react-vite'), options: {}, }, async viteFinal(config) { return mergeConfig(config, { publicDir: '../public', }); }, }; export default Config; function getAbsolutePath(value: string): any { return dirname(require.resolve(join(value, 'package.json'))); } ================================================ FILE: packages/components/.storybook/manager.js ================================================ import { addons } from 'storybook/manager-api'; import { create } from 'storybook/theming'; addons.setConfig({ isFullscreen: false, showNav: true, showPanel: true, panelPosition: 'bottom', enableShortcuts: true, isToolshown: true, theme: create({ base: 'light', brandTitle: 'Platform Components', brandUrl: 'https://onearmy.world', }), selectedPanel: undefined, initialActive: 'sidebar', sidebar: { showRoots: false, collapsedRoots: ['other'], }, }); // automatically import all files ending in *.stories.js // configure(require.context('../src', true, /\.stories\.(js|jsx|mdx)$/), module) ================================================ FILE: packages/components/.storybook/preview.tsx ================================================ import { Global } from '@emotion/react'; import type { Preview } from '@storybook/react-vite'; import { ThemeProvider } from '@theme-ui/core'; import { theme } from 'oa-themes'; import { useEffect } from 'react'; import { createRoutesStub } from 'react-router'; import { GlobalStyles } from '../src/GlobalStyles/GlobalStyles'; export const themeColors = { 'precious-plastic': { primary: '#fee77b', primaryHover: '#ffde45', accent: '#fee77b', accentHover: '#ffde45', }, 'project-kamp': { primary: '#8ab57f', primaryHover: 'hsl(108, 25%, 68%)', accent: '#8ab57f', accentHover: 'hsl(108, 25%, 68%)', }, 'fixing-fashion': { primary: '#f82f03', primaryHover: 'hsl(14, 81%, 63%)', accent: '#f82f03', accentHover: 'hsl(14, 81%, 63%)', }, } as const; export type ThemeName = keyof typeof themeColors; export function getThemeCSSVariables(themeName: ThemeName): string { const colors = themeColors[themeName]; return ` --color-primary: ${colors.primary}; --color-primary-hover: ${colors.primaryHover}; --color-accent: ${colors.accent}; --color-accent-hover: ${colors.accentHover}; `.trim(); } const themeMap: Record = { pp: 'precious-plastic', pk: 'project-kamp', ff: 'fixing-fashion', }; // Component to inject CSS variables dynamically function ThemeVariables({ themeName }: { themeName: ThemeName }) { useEffect(() => { const cssVars = getThemeCSSVariables(themeName); const styleId = 'theme-variables'; let styleTag = document.getElementById(styleId) as HTMLStyleElement; if (!styleTag) { styleTag = document.createElement('style'); styleTag.id = styleId; document.head.appendChild(styleTag); } styleTag.textContent = `:root { ${cssVars} }`; }, [themeName]); return null; } const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, options: { storySort: { order: ['Welcome'], }, }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, }, globalTypes: { theme: { name: 'Theme', description: 'Platform Theme', defaultValue: 'pp', toolbar: { icon: 'paintbrush', items: [ { value: 'pp', title: 'Precious Plastic' }, { value: 'pk', title: 'Project Kamp' }, { value: 'ff', title: 'Fixing Fashion' }, ], }, }, }, decorators: [ (Story, context) => { const themeName = themeMap[context.globals.theme] || 'precious-plastic'; const RouterStub = createRoutesStub([ { path: '/', Component: () => ( <> ), }, ]); return ; }, ], }; export default preview; ================================================ FILE: packages/components/README.md ================================================ # Platform Components A collection of react components for reuse across the platform. Built with [Theme UI](https://theme-ui.com/) for styling. These components are stored within the [Community Platform monorepo](https://github.com/ONEARMY/community-platform) and configured as a standalone package using [Bun workspaces](https://bun.sh/docs/install/workspaces). The aim of packaging these components separately is to: 1. Encourage separation between presentation layer and business logic 2. Reduce the overhead for contributors looking to work **only** on the component layer without needing to spin up the entire application locally. We are using [Storybook](https://storybook.js.org/) to provide a browser accessible interface for our components. > Storybook is a tool for UI development. It makes development faster and easier by isolating components. This allows you to work on one component at a time. You can develop entire UIs without needing to start up a complex dev stack, force certain data into your database, or navigate around your application. (Optional) For anyone unfamiliar with Storybook looking to better understand the tool, we recommend reading their guide on [What's a Story](https://storybook.js.org/docs/react/get-started/whats-a-story). ## Getting started After [cloning the repo](https://github.com/ONEARMY/community-platform), you can start the Storybook instance, which will make the application available in your browser at [http://localhost:6006](http://localhost:6006/). ``` cd ./packages/components bun install bun start ``` ## Creating a new Component You can quickly create a new component using the command `bun run new-component MyNewComponentName`, which will generate the following items: ``` src/ MyNewComponentName/ MyNewComponentName.tsx # Component MyNewComponentName.test.tsx # Storybook documentation MyNewComponentName.stories.tsx # Storybook documentation ``` ================================================ FILE: packages/components/package.json ================================================ { "name": "oa-components", "version": "1.0.0", "private": true, "type": "module", "exports": { ".": { "bun": "./src/index.ts", "import": "./dist/index.js", "default": "./dist/index.js" }, "./*": { "bun": "./src/*.ts", "import": "./dist/*.js", "default": "./dist/*.js" } }, "main": "./dist/index.js", "types": "./dist/index.d.ts", "typesVersions": { "*": { "*": ["./src/index.ts", "./src/*"] } }, "scripts": { "storybook": "bun run start", "start": "storybook dev -p 6006 --loglevel verbose", "format": "biome check --write", "build:sb": "bun run build --build && storybook build", "build": "tsc --build --verbose", "dev": "tsc --watch", "lint": "biome lint --write", "test": "vitest", "test-ci": "vitest --coverage --reporter=junit --outputFile.junit=./reports/output.xml" }, "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@faker-js/faker": "^10.1.0", "@mdxeditor/editor": "^3.52.0", "@mui/base": "next", "@react-spring/web": "^10.0.3", "@theme-ui/core": "^0.17.4", "country-to-iso": "^1.6.1", "date-fns": "^4.1.0", "linkify-plugin-mention": "^4.3.2", "linkify-react": "^4.3.2", "linkifyjs": "^4.3.2", "marked": "^17.0.1", "oa-themes": "workspace:^", "photoswipe": "^5.4.4", "react-country-flag": "^3.1.0", "react-horizontal-scrolling-menu": "^8.2.0", "react-icons": "^5.3.0", "react-player": "3.0.0-canary.4", "react-router": "^7.13.1", "react-select": "^5.8.1", "react-spring": "^10.0.3", "react-tooltip": "^5.30.0", "storybook": "10.1.4", "styled-system": "^5.1.5", "theme-ui": "^0.17.2", "use-debounce": "^10.1.0", "yup": "^1.7.1" }, "peerDependencies": { "react": "19.2.1" }, "devDependencies": { "@biomejs/biome": "2.3.14", "@react-router/dev": "^7.13.1", "@storybook/addon-docs": "10.1.4", "@storybook/addon-links": "^10.1.4", "@storybook/react-vite": "10.1.4", "@storybook/react": "10.1.4", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.0", "@types/react": "^19.2.7", "@vitejs/plugin-react": "^5.1.1", "eslint": "^8.50.0", "eslint-plugin-import": "^2.28.1", "eslint-plugin-storybook": "^9.0.15", "eslint-plugin-vitest": "^0.5.4", "jsdom": "^21.1.1", "react": "^19.2.1", "react-dom": "^19.2.1", "typescript": "^5.7.2", "vitest": "^4.0.5" } } ================================================ FILE: packages/components/src/Accordion/Accordion.stories.tsx ================================================ import { Text } from 'theme-ui'; import { Accordion } from './Accordion'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Components/Accordion', component: Accordion, } as Meta; export const Default: StoryFn = () => ( Now you see me! ); ================================================ FILE: packages/components/src/Accordion/Accordion.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { act, screen } from '@testing-library/react'; import { Text } from 'theme-ui'; import { describe, expect, it } from 'vitest'; import { render } from '../test/utils'; import { Accordion } from './Accordion'; describe('Accordion', () => { it('displays the accordion body on click', () => { const { getByText } = render( Now you see me! , ); const accordionTitle = getByText('Accordion Title'); expect(screen.queryByText('Now you see me!')).not.toBeInTheDocument(); act(() => { accordionTitle.click(); }); expect(getByText('Now you see me!')).toBeInTheDocument(); }); }); ================================================ FILE: packages/components/src/Accordion/Accordion.tsx ================================================ import { useState } from 'react'; import type { ThemeUIStyleObject } from 'theme-ui'; import { Flex, Heading, Text } from 'theme-ui'; import { Icon } from '../Icon/Icon'; export interface IProps { children: React.ReactNode; sx?: ThemeUIStyleObject | undefined; title: string; subtitle?: string; } export const Accordion = (props: IProps) => { const [isExpanded, setIsExpanded] = useState(false); const { children, sx, title, subtitle } = props; return ( { if (!isExpanded) { setIsExpanded(true); } }} > setIsExpanded(!isExpanded)} > {title} {subtitle != undefined && {subtitle}} {isExpanded && children} ); }; ================================================ FILE: packages/components/src/ActionSet/ActionSet.tsx ================================================ import type { ReactNode } from 'react'; import { useEffect, useRef, useState } from 'react'; import { Card, Flex } from 'theme-ui'; import { Button } from '../Button/Button'; interface IProps { children: ReactNode[]; itemType: 'ReplyItem' | 'CommentItem'; } export const ActionSet = ({ children, itemType }: IProps) => { const [show, setShow] = useState(false); const cardRef = useRef(null); const toDisplay = children.filter((child) => !!child); if (!children || toDisplay.length === 0) { return null; } const onClick = () => setShow((show) => !show); useEffect(() => { const handleClickOutsideDropdownCard = (event: MouseEvent) => { if (cardRef.current && !cardRef.current.contains(event.target as Node)) { setShow((prev) => !prev); } }; if (show) document.addEventListener('mousedown', handleClickOutsideDropdownCard); return () => { document.removeEventListener('mousedown', handleClickOutsideDropdownCard); }; }, [show]); return ( {show && ( setShow(false)} sx={{ alignItems: 'stretch', justifyItems: 'stretch', flexDirection: 'column', }} > {...children} )} ); }; ================================================ FILE: packages/components/src/Alert/Alert.stories.tsx ================================================ import { Alert } from 'theme-ui'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/Alert', component: Alert, } as Meta; export const Success: StoryFn = () => ( A successful message ); export const Failure: StoryFn = () => ( An error message ); export const Information: StoryFn = () => ( An information message ); export const FailureLong: StoryFn = () => ( An error message: Veniam explicabo dolor ipsam impedit. Eum eos ut et consequatur eos eaque explicabo et inventore. Aperiam aut consequatur sit ut. Iusto consequatur enim placeat enim quia voluptas pariatur. Culpa quaerat placeat magni et autem earum placeat deserunt eum. A autem enim dolorum. Quo sint nisi vel. Voluptate voluptates alias repudiandae doloribus nemo. Quia aperiam nihil magnam quos ut id. Pariatur itaque sint. Id vel aliquid ullam delectus animi quis.{' '} ); ================================================ FILE: packages/components/src/ArrowIcon/ArrowIcon.stories.tsx ================================================ import { Arrow } from './ArrowIcon'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { /* 👇 The title prop is optional. * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ title: 'Components/Arrow icon', component: Arrow, } as Meta; export const Left: StoryFn = () => ; export const Right: StoryFn = () => ; ================================================ FILE: packages/components/src/ArrowIcon/ArrowIcon.tsx ================================================ import type { ThemeUIStyleObject } from 'theme-ui'; import { Flex } from 'theme-ui'; import { Icon } from '../Icon/Icon'; import type { availableGlyphs } from '../Icon/types'; import './styles.css'; interface IProps { disabled?: boolean; direction: 'left' | 'right'; sx?: ThemeUIStyleObject; onClick?: () => void; } export const Arrow = ({ disabled, direction, onClick, sx }: IProps) => { const glyph: availableGlyphs = direction === 'left' ? 'chevron-left' : 'chevron-right'; return ( {disabled ? null : ( )} ); }; ================================================ FILE: packages/components/src/ArrowIcon/styles.css ================================================ .react-horizontal-scrolling-menu--inner-wrapper { position: relative; } .react-horizontal-scrolling-menu--arrow-left { position: absolute; left: 0; z-index: 1; height: 100%; } .react-horizontal-scrolling-menu--arrow-right { position: absolute; right: 0; z-index: 1; height: 100%; } ================================================ FILE: packages/components/src/ArticleCallToActionSupabase/ArticleCallToActionSupabase.stories.tsx ================================================ import { faker } from '@faker-js/faker'; import { Button } from '../Button/Button'; import { UsefulStatsButton } from '../UsefulStatsButton/UsefulStatsButton'; import { ArticleCallToActionSupabase } from './ArticleCallToActionSupabase'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { Author } from 'oa-shared'; export default { title: 'Layout/ArticleCallToActionSupabase', component: ArticleCallToActionSupabase, } as Meta; export const ArticleCallToActionSupabaseCommentAndUseful: StoryFn< typeof ArticleCallToActionSupabase > = () => ( Promise.resolve()} /> ); export const ArticleCallToActionSupabaseUseful: StoryFn< typeof ArticleCallToActionSupabase > = () => ( Promise.resolve()} /> ); export const ArticleCallToActionSupabaseSingleContributor: StoryFn< typeof ArticleCallToActionSupabase > = () => ( ); const makeFakeUser = (): Author => ({ id: faker.number.int(), country: faker.location.countryCode(), displayName: faker.person.firstName(), badges: [ { id: 1, name: 'pro', displayName: 'PRO', imageUrl: faker.image.avatar(), }, { id: 2, name: 'supporter', displayName: 'Supporter', actionUrl: faker.internet.url(), imageUrl: faker.image.avatar(), }, ], photo: { id: faker.string.uuid(), publicUrl: faker.image.avatar(), }, username: faker.internet.username(), }); export const ArticleCallToActionSupabaseMultipleContributors: StoryFn< typeof ArticleCallToActionSupabase > = () => ( ); ================================================ FILE: packages/components/src/ArticleCallToActionSupabase/ArticleCallToActionSupabase.tsx ================================================ import type { Author } from 'oa-shared'; import { Flex, Heading, Text } from 'theme-ui'; import { Username } from '../Username/Username'; export interface IProps { author: Author; children: React.ReactNode; contributors?: Author[]; } export const ArticleCallToActionSupabase = (props: IProps) => { const { author, children, contributors } = props; return ( Made by {contributors && contributors.length ? ( With contributions from:{' '} {contributors.map((contributor, key) => ( ))} ) : null} Like what you see? 👇 {children} ); }; ================================================ FILE: packages/components/src/AuthorDisplay/AuthorDisplay.tsx ================================================ import type { Author } from 'oa-shared'; import { Avatar, Flex } from 'theme-ui'; import { Username } from '../Username/Username'; interface IProps { author: Author | null; } export const AuthorDisplay = ({ author }: IProps) => { if (!author) { return null; } return ( {author.photo && ( )} ); }; ================================================ FILE: packages/components/src/Banner/Banner.stories.tsx ================================================ import { Banner } from './Banner'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/Banner', component: Banner, } as Meta; export const Default: StoryFn = () => ( Defaults to a failure banner when no varient defined ); export const AccentWithOnclick: StoryFn = () => ( null}> This is an accent with onClick ); export const InfoWithCustomStylings: StoryFn = () => ( Info with custom stylings ); export const Success: StoryFn = () => ( null}> Success Banner ); ================================================ FILE: packages/components/src/Banner/Banner.test.tsx ================================================ import { fireEvent } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import { render } from '../test/utils'; import { Banner } from './Banner'; describe('Banner', () => { it('sets the default variant if none is provided', () => { const onClick = vi.fn(); const { getByText } = render(Some words); fireEvent.click(getByText('Some words')); expect(onClick).toHaveBeenCalled(); }); }); ================================================ FILE: packages/components/src/Banner/Banner.tsx ================================================ import type { ThemeUIStyleObject } from 'theme-ui'; import { Alert } from 'theme-ui'; // Types of alert currently specified in the theme type AlertVariants = 'accent' | 'failure' | 'info' | 'success'; export interface IProps { children: React.ReactNode; onClick?: () => void; sx?: ThemeUIStyleObject | undefined; variant?: AlertVariants; } export const Banner = (props: IProps) => { const { children, onClick, sx, variant } = props; return ( {children} ); }; ================================================ FILE: packages/components/src/BlockedRoute/BlockedRoute.stories.tsx ================================================ import { faker } from '@faker-js/faker'; import { BlockedRoute } from './BlockedRoute'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/BlockedRoute', component: BlockedRoute, } as Meta; export const Default: StoryFn = () => ( {faker.lorem.sentences(2)} ); export const OverrideButton: StoryFn = () => ( {faker.lorem.sentences(2)} ); ================================================ FILE: packages/components/src/BlockedRoute/BlockedRoute.tsx ================================================ import { Box, Flex, Text } from 'theme-ui'; import { Button } from '../Button/Button'; import { InternalLink } from '../InternalLink/InternalLink'; export interface BlockedRouteProps { children: React.ReactNode; redirectUrl?: string; redirectLabel?: string; } export const BlockedRoute = (props: BlockedRouteProps) => { const redirectLabel = props.redirectLabel || 'Back to home'; const redirectUrl = props.redirectUrl || '/'; return ( {props.children} ); }; ================================================ FILE: packages/components/src/Breadcrumbs/Breadcrumbs.stories.tsx ================================================ import { Breadcrumbs } from './Breadcrumbs'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/Breadcrumbs', component: Breadcrumbs, } as Meta; export const Default: StoryFn = () => ( ); export const NoCategory: StoryFn = () => ( ); ================================================ FILE: packages/components/src/Breadcrumbs/Breadcrumbs.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { describe, expect, it } from 'vitest'; import { render } from '../test/utils'; import { Breadcrumbs } from './Breadcrumbs'; describe('Breadcrumbs', () => { it('validate full breadcrumbs', () => { const { getByText, getAllByTestId } = render( , ); expect(getByText('Question')).toBeInTheDocument(); expect(getByText('Category')).toBeInTheDocument(); expect(getByText('Are we real?')).toBeInTheDocument(); const chevrons = getAllByTestId('breadcrumbsChevron'); expect(chevrons).toHaveLength(2); }); it('validate no category breadcrumbs', () => { const { getByText, getAllByTestId } = render( , ); expect(getByText('Question')).toBeInTheDocument(); expect(getByText('Are we real?')).toBeInTheDocument(); const chevrons = getAllByTestId('breadcrumbsChevron'); expect(chevrons).toHaveLength(1); }); }); ================================================ FILE: packages/components/src/Breadcrumbs/Breadcrumbs.tsx ================================================ import { Flex } from 'theme-ui'; import { Icon } from '../Icon/Icon'; import { BreadcrumbItem } from './BreadcrumbsItem'; type Step = { text: string; link?: string }; export interface BreadcrumbsProps { steps: Step[]; } export const Breadcrumbs = ({ steps }: BreadcrumbsProps) => { return ( {steps.map((step, index) => { const isLast = index === steps.length - 1; return ( {!isLast && ( )} ); })} ); }; ================================================ FILE: packages/components/src/Breadcrumbs/BreadcrumbsItem.tsx ================================================ import { Link } from 'react-router'; import { Box, Text } from 'theme-ui'; import { Button } from '../Button/Button'; interface BreadcrumbButtonProps { text: string; link?: string; } interface BreadcrumbItemProps { text: string; link?: string; isLast: boolean; } const BreadcrumbButton = ({ text, link }: BreadcrumbButtonProps) => { return link ? ( ) : ( ); }; export const BreadcrumbItem = ({ text, link, isLast }: BreadcrumbItemProps) => ( {!isLast ? ( ) : ( {text} )} ); ================================================ FILE: packages/components/src/Button/Button.stories.tsx ================================================ import { glyphs } from '../Icon/Icon'; import { Button } from './Button'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { /* 👇 The title prop is optional. * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading * to learn how to generate automatic titles */ title: 'Components/Button', component: Button, } as Meta; const sizeOptions = [ { small: true, label: 'Small', }, { label: 'Default', }, { large: true, label: 'Large', }, ]; export const Basic: StoryFn = () => ; export const Disabled: StoryFn = () => ( <> ); export const Primary: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Secondary: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Destructive: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Success: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Subtle: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Outline: StoryFn = () => ( <> {sizeOptions.map((v, k) => ( ))} ); export const Small: StoryFn = () => ( <> ); export const Large: StoryFn = () => ( <> ); export const IconOnly: StoryFn = () => ( <> ); export const Icons: StoryFn = () => ( <> {sizeOptions.map((size) => ['primary', 'secondary', 'outline'].map((variant) => Object.keys(glyphs).map((glyph: any, key) => ( )), ), )} ); ================================================ FILE: packages/components/src/Button/Button.tsx ================================================ import type { Colors } from 'oa-themes'; import React from 'react'; import type { ButtonProps as ThemeUiButtonProps } from 'theme-ui'; import { Flex, Text, Button as ThemeUiButton } from 'theme-ui'; import { Icon } from '../Icon/Icon'; import type { IGlyphs } from '../Icon/types'; // extend to allow any default button props (e.g. onClick) to also be passed export interface IBtnProps extends React.ButtonHTMLAttributes { icon?: keyof IGlyphs; disabled?: boolean; small?: boolean; large?: boolean; showIconOnly?: boolean; iconColor?: Colors; iconFilter?: string; } type ToArray = [Type] extends [any] ? Type[] : never; type AvailableButtonProps = ToArray; const buttonSizeProps: { [key: string]: any } = { small: { px: 2, py: 1, pl: '2rem', fontSize: 1, height: '2rem', }, default: { px: 3, pl: 9, }, large: { px: 4, py: 3, pl: 10, fontSize: 4, height: '3.5rem', }, }; export type BtnProps = IBtnProps & ThemeUiButtonProps; function getSizeProps(size: string, hasIcon: boolean) { if (!buttonSizeProps[size] && !hasIcon) { return {}; } if (!buttonSizeProps[size] && hasIcon) { return { px: 3, pl: 9, }; } const sizeProps = { ...buttonSizeProps[size] }; if (!hasIcon) { delete sizeProps.pl; } return sizeProps; } function getScaleTransform(size: string) { if (size === 'large') { return 1.25; } return 1; } function sanitizedProps(obj: BtnProps, keysToRemove: AvailableButtonProps) { const sanitizedObj = { ...obj }; keysToRemove.forEach((prop) => { if (sanitizedObj[prop]) { delete sanitizedObj[prop]; } }); return sanitizedObj; } export const Button = (props: BtnProps) => { let size = 'default'; if (props.small === true) { size = 'small'; } else if (props.large === true) { size = 'large'; } return ( {props.icon && ( )} {props.children} ); }; ================================================ FILE: packages/components/src/ButtonIcon/ButtonIcon.stories.tsx ================================================ import { ButtonIcon } from './ButtonIcon'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Map/ButtonIcon', component: ButtonIcon, } as Meta; export const WithClose: StoryFn = () => ; ================================================ FILE: packages/components/src/ButtonIcon/ButtonIcon.tsx ================================================ import type { ThemeUIStyleObject } from 'theme-ui'; import { Button } from 'theme-ui'; import { Icon } from '../Icon/Icon'; import type { IGlyphs } from '../Icon/types'; export interface IProps extends React.ButtonHTMLAttributes { icon: keyof IGlyphs; sx?: ThemeUIStyleObject | undefined; } export const ButtonIcon = (props: IProps) => { return ( ); }; ================================================ FILE: packages/components/src/ButtonShowReplies/ButtonShowReplies.stories.tsx ================================================ import { useState } from 'react'; import { createFakeCommentsSB } from '../utils'; import { ButtonShowReplies } from './ButtonShowReplies'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Components/ButtonShowReplies', component: ButtonShowReplies, } as Meta; export const DefaultComponent = () => { const [isShowReplies, setIsShowReplies] = useState(false); const replies = createFakeCommentsSB(7); return ( setIsShowReplies(!isShowReplies)} /> ); }; export const Default: StoryFn = () => { return ; }; export const RepliesShowing: StoryFn = () => { const replies = createFakeCommentsSB(6); return null} />; }; export const OneReply: StoryFn = () => { const replies = createFakeCommentsSB(1); return ( null} /> ); }; export const NoReplies: StoryFn = () => { return null} />; }; export const NoCreatorName: StoryFn = () => { const replies = createFakeCommentsSB(1); return ( null} /> ); }; ================================================ FILE: packages/components/src/ButtonShowReplies/ButtonShowReplies.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { describe, expect, it } from 'vitest'; import { render } from '../test/utils'; import { createFakeCommentsSB } from '../utils'; import { ButtonShowReplies } from './ButtonShowReplies'; import { DefaultComponent } from './ButtonShowReplies.stories'; describe('ButtonShowReplies', () => { it('renders the button text', () => { const { getByTestId, getByText } = render(); const icon = getByTestId('show-replies'); expect(getByText('Show 7 replies')).toBeInTheDocument(); expect(icon.getAttribute('icon')).toContain('chevron-down'); }); it('renders the button text', () => { const replies = createFakeCommentsSB(6); const { getByTestId } = render( null} />, ); const icon = getByTestId('show-replies'); expect(icon.getAttribute('icon')).toContain('chevron-up'); }); it('renders the word reply when expected', () => { const replies = createFakeCommentsSB(1); const { getByText } = render( null} />, ); expect(getByText('Show 1 reply')).toBeInTheDocument(); }); it('renders the number zero when expected', () => { const { getByText } = render( null} />, ); expect(getByText('Reply')).toBeInTheDocument(); }); }); ================================================ FILE: packages/components/src/ButtonShowReplies/ButtonShowReplies.tsx ================================================ import type { Comment } from 'oa-shared'; import { Button } from '../Button/Button'; export interface Props { isShowReplies: boolean; replies: Comment[]; setIsShowReplies: () => void; } export const ButtonShowReplies = (props: Props) => { const { isShowReplies, replies, setIsShowReplies } = props; const count = replies.filter(({ deleted }) => deleted !== true).length; const icon = isShowReplies ? 'chevron-up' : 'chevron-down'; const text = count ? isShowReplies ? `Hide ${count} ${count === 1 ? 'reply' : 'replies'}` : `Show ${count} ${count === 1 ? 'reply' : 'replies'}` : isShowReplies ? `Hide` : `Reply`; return ( ); }; ================================================ FILE: packages/components/src/CardButton/CardButton.stories.tsx ================================================ import { CardButton } from './CardButton'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Components/CardButton', component: CardButton, } as Meta; export const Basic: StoryFn = () => (
Basic Implementation
); ================================================ FILE: packages/components/src/CardButton/CardButton.tsx ================================================ import type { BoxProps, ThemeUIStyleObject } from 'theme-ui'; import { Card } from 'theme-ui'; export interface IProps extends BoxProps { children: React.ReactNode; extrastyles?: ThemeUIStyleObject | undefined; isSelected?: boolean; } export const CardButton = (props: IProps) => { const { children, extrastyles, isSelected } = props; return ( {children} ); }; ================================================ FILE: packages/components/src/CardListItem/CardListItem.stories.tsx ================================================ import { faker } from '@faker-js/faker'; import { CardListItem } from './CardListItem'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { MapPin, Moderation, ProfileType } from 'oa-shared'; export default { title: 'Map/CardListItem', component: CardListItem, } as Meta; const onPinClick = () => undefined; const viewport = 'desktop'; const member: ProfileType = { name: 'member', description: 'A member profile', displayName: 'Member', id: 2, imageUrl: faker.image.avatar(), mapPinName: 'Member', order: 1, smallImageUrl: faker.image.avatar(), isSpace: false, }; const space: ProfileType = { name: 'space', description: 'A space profile', displayName: 'Space', id: 3, imageUrl: faker.image.avatar(), mapPinName: 'Space', order: 1, smallImageUrl: faker.image.avatar(), isSpace: true, }; export const DefaultMember: StoryFn = () => { const item = { id: 1, lat: 0, lng: 0, administrative: '', country: 'Brazil', countryCode: 'BR', moderation: 'accepted' as Moderation, profile: { id: 1, photo: { publicUrl: faker.image.avatar(), }, displayName: 'member_no1', isContactable: false, type: member, }, } as MapPin; return (
); }; export const DefaultSpace: StoryFn = () => { const item = { id: 1, lat: 0, lng: 0, administrative: '', country: 'United Kingdom', countryCode: 'UK', moderation: 'accepted' as Moderation, profile: { id: 1, photo: { publicUrl: faker.image.avatar(), }, about: 'Lorem ipsum odor amet, consectetuer adipiscing elit. Lorem ipsum odor amet, consectetuer adipiscing elit.', displayName: 'member_no1', isContactable: false, type: space, tags: [{ id: 1, name: 'Sheetpress' }], }, } as MapPin; return (
); }; ================================================ FILE: packages/components/src/CardListItem/CardListItem.tsx ================================================ import type { MapPin } from 'oa-shared'; import { Box } from 'theme-ui'; import { CardButton } from '../CardButton/CardButton'; import { CardProfile } from '../CardProfile/CardProfile'; import { InternalLink } from '../InternalLink/InternalLink'; export interface IProps { item: MapPin; isSelectedPin: boolean; onPinClick: (arg: MapPin) => void; viewport: string; } export const CardListItem = (props: IProps) => { const { item, onPinClick, isSelectedPin, viewport } = props; const testProp = `CardListItem${isSelectedPin ? '-selected' : ''}`; const Card = ( ); const wrapperProps = { 'data-cy': testProp, 'data-testid': testProp, sx: { borderRadius: 2, padding: 2, }, }; if (viewport === 'mobile') { return ( {Card} ); } return ( onPinClick(item)} sx={{ borderRadius: 2, padding: 2, }} > {Card} ); }; ================================================ FILE: packages/components/src/CardProfile/CardDetailsMemberProfile.tsx ================================================ import type { PinProfile } from 'oa-shared'; import { Avatar, Box, Flex } from 'theme-ui'; import defaultProfileImage from '../../assets/images/default_member.svg'; import { MemberBadge } from '../MemberBadge/MemberBadge'; import { ProfileTagsList } from '../ProfileTagsList/ProfileTagsList'; import { Username } from '../Username/Username'; interface IProps { profile: PinProfile; isLink: boolean; } export const CardDetailsMemberProfile = ({ profile, isLink }: IProps) => { const photoUrl = profile.photo?.publicUrl; return ( {profile.tags && profile.tags.length > 0 && ( )} ); }; ================================================ FILE: packages/components/src/CardProfile/CardDetailsSpaceProfile.tsx ================================================ import type { PinProfile } from 'oa-shared'; import { Box, Flex, Image, Text } from 'theme-ui'; import { MemberBadge } from '../MemberBadge/MemberBadge'; import { ProfileTagsList } from '../ProfileTagsList/ProfileTagsList'; import { Username } from '../Username/Username'; interface IProps { profile: PinProfile; isLink: boolean; } export const CardDetailsSpaceProfile = ({ profile, isLink }: IProps) => { const coverImage = profile.coverImages && profile.coverImages[0] && profile.coverImages[0]?.publicUrl; const profileUrl = profile.photo?.publicUrl; const hasImage = coverImage || profileUrl; const aboutText = profile.about && profile.about.length > 80 ? profile.about.slice(0, 78) + '...' : profile.about; return ( {hasImage && ( <> )} {!hasImage && } {profile.tags && profile.tags.length > 0 && ( )} {aboutText && ( {aboutText} )} ); }; ================================================ FILE: packages/components/src/CardProfile/CardProfile.stories.tsx ================================================ import { fakePinProfile, fakeProfileType } from '../utils'; import { CardProfile } from './CardProfile'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { MapPin, PinProfile } from 'oa-shared'; export default { title: 'Components/CardProfile', component: CardProfile, } as Meta; const member: PinProfile = fakePinProfile(); const space: PinProfile = fakePinProfile({ type: fakeProfileType({ isSpace: true }), }); export const Member: StoryFn = () => ( ); export const Space: StoryFn = () => ( ); ================================================ FILE: packages/components/src/CardProfile/CardProfile.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { describe, expect, it } from 'vitest'; import { render } from '../test/utils'; import { fakePinProfile, fakeProfileType } from '../utils'; import { CardProfile } from './CardProfile'; import type { MapPin, PinProfile } from 'oa-shared'; describe('CardProfile', () => { it('renders the member profile', () => { const member: PinProfile = fakePinProfile(); const { getByTestId } = render(); expect(getByTestId('CardDetailsMemberProfile')).toBeInTheDocument(); }); it('renders the space profile', () => { const space: PinProfile = fakePinProfile({ type: fakeProfileType({ isSpace: true }), }); const { getByTestId } = render(); expect(getByTestId('CardDetailsSpaceProfile')).toBeInTheDocument(); }); }); ================================================ FILE: packages/components/src/CardProfile/CardProfile.tsx ================================================ import type { MapPin } from 'oa-shared'; import { Flex } from 'theme-ui'; import { CardDetailsMemberProfile } from './CardDetailsMemberProfile'; import { CardDetailsSpaceProfile } from './CardDetailsSpaceProfile'; export interface IProps { item: MapPin; isLink?: boolean; } export const CardProfile = ({ item, isLink = false }: IProps) => { const { profile } = item; const isWorkspace = profile?.type && profile?.type.isSpace; return ( {isWorkspace ? ( ) : ( )} ); }; ================================================ FILE: packages/components/src/Category/Category.stories.tsx ================================================ import { Category } from './Category'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { Category as CategoryType } from 'oa-shared'; export default { title: 'Components/Category', component: Category, } as Meta; export const Default: StoryFn = () => ( ); ================================================ FILE: packages/components/src/Category/Category.tsx ================================================ import type { Category as CategoryType } from 'oa-shared'; import type { ThemeUIStyleObject } from 'theme-ui'; import { Flex, Text } from 'theme-ui'; export interface Props { category: CategoryType; sx?: ThemeUIStyleObject | undefined; } export const Category = (props: Props) => { const { category, sx } = props; return ( {category.name} ); }; ================================================ FILE: packages/components/src/CategoryHorizonalList/CategoryHorizonalList.stories.tsx ================================================ import { useState } from 'react'; import { CategoryHorizonalList } from './CategoryHorizonalList'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { Category, ContentType } from 'oa-shared'; export default { title: 'Components/CategoryHorizonalList', component: CategoryHorizonalList, } as Meta; const allCategoriesForPreciousPlastic = [ { createdAt: new Date('2024-12-03T18:03:51.313Z'), id: 1, modifiedAt: null, name: 'Guides', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-01T18:03:51.313Z'), id: 2, modifiedAt: null, name: 'Machines', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 3, modifiedAt: null, name: 'Moulds', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 4, modifiedAt: null, name: 'Products', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 5, modifiedAt: null, name: 'Starter Kits', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-04T18:03:51.313Z'), id: 6, modifiedAt: null, name: 'Recycling', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-05T18:03:51.313Z'), id: 7, modifiedAt: null, name: 'Version 5', type: 'questions' as ContentType, }, ]; const allCategoriesForProjectKamp = [ { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 8, modifiedAt: null, name: 'Construction', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 9, modifiedAt: null, name: 'Food', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 10, modifiedAt: null, name: 'Landscape', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 11, modifiedAt: null, name: 'Other', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 12, modifiedAt: null, name: 'Utilities', type: 'questions' as ContentType, }, ]; export const Basic: StoryFn = () => { const [activeCategory, setActiveCategory] = useState(null); const allCategories = [...allCategoriesForPreciousPlastic, ...allCategoriesForProjectKamp]; return (
); }; export const WhenGlyphNotPresent: StoryFn = () => { const [activeCategory, setActiveCategory] = useState(null); const noGlyphCategories = [ { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 13, modifiedAt: null, name: 'No Glphy A', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 14, modifiedAt: null, name: 'No Glphy B', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 15, modifiedAt: null, name: 'No Glphy C', type: 'questions' as ContentType, }, ]; return (
); }; export const OnlyOne: StoryFn = () => { const [activeCategory, setActiveCategory] = useState(null); const twoCategories = [allCategoriesForPreciousPlastic[0], allCategoriesForPreciousPlastic[1]]; return (
(Shouldn't see anything, only renders for two or more)
); }; ================================================ FILE: packages/components/src/CategoryHorizonalList/CategoryHorizonalList.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { render } from '../test/utils'; import { CategoryHorizonalList } from './CategoryHorizonalList'; import type { ContentType } from 'oa-shared'; const allCategoriesForPreciousPlastic = [ { createdAt: new Date('2024-12-03T18:03:51.313Z'), id: 1, modifiedAt: null, name: 'Guides', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-01T18:03:51.313Z'), id: 2, modifiedAt: null, name: 'Machines', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 3, modifiedAt: null, name: 'Moulds', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 4, modifiedAt: null, name: 'Products', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 5, modifiedAt: null, name: 'Starter Kits', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-04T18:03:51.313Z'), id: 6, modifiedAt: null, name: 'Recycling', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-05T18:03:51.313Z'), id: 7, modifiedAt: null, name: 'Version 5', type: 'questions' as ContentType, }, ]; const allCategoriesForProjectKamp = [ { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 8, modifiedAt: null, name: 'Construction', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 9, modifiedAt: null, name: 'Food', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 10, modifiedAt: null, name: 'Landscape', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 11, modifiedAt: null, name: 'Other', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 12, modifiedAt: null, name: 'Utilities', type: 'questions' as ContentType, }, ]; describe('CategoryHorizonalList', () => { // https://stackoverflow.com/a/62148101 beforeEach(() => { window.IntersectionObserver = vi.fn().mockImplementation(function () { return { observe: vi.fn(), unobserve: vi.fn(), disconnect: vi.fn(), }; }); }); it('renders each member type given', async () => { const allCategories = [...allCategoriesForPreciousPlastic, ...allCategoriesForProjectKamp]; const { findAllByTestId } = render( , ); const allItems = await findAllByTestId('CategoryHorizonalList-Item'); expect(allItems).toHaveLength(12); }); it('orders by _created with oldest first', async () => { const allCategories = [...allCategoriesForPreciousPlastic, ...allCategoriesForProjectKamp]; const { findAllByTestId } = render( , ); const allItems = await findAllByTestId('CategoryHorizonalList-Item'); expect(allItems[0].title).toEqual('Machines'); expect(allItems[11].title).toEqual('Guides'); }); it('renders default category glyph when specific glyph is missing', async () => { const noGlyphCategories = [ { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 13, modifiedAt: null, name: 'No Glphy A', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 14, modifiedAt: null, name: 'No Glphy B', type: 'questions' as ContentType, }, { createdAt: new Date('2022-12-03T18:03:51.313Z'), id: 15, modifiedAt: null, name: 'No Glphy C', type: 'questions' as ContentType, }, ]; const { findAllByTestId } = render( , ); const allItems = await findAllByTestId('category-icon'); expect(allItems).toHaveLength(3); }); it("doesn't render items when less than three at present", () => { const twoCategories = [allCategoriesForPreciousPlastic[0], allCategoriesForPreciousPlastic[1]]; const { getByTestId } = render( , ); expect(() => getByTestId('MemberTypeVerticalList-Item')).toThrow(); }); }); ================================================ FILE: packages/components/src/CategoryHorizonalList/CategoryHorizonalList.tsx ================================================ import type { Category } from 'oa-shared'; import { Text } from 'theme-ui'; import { CardButton } from '../CardButton/CardButton'; import { getGlyph, Icon } from '../Icon/Icon'; import type { availableGlyphs } from '../Icon/types'; import { VerticalList } from '../VerticalList/VerticalList.client'; export interface IProps { activeCategory: Category | null; allCategories: Category[]; setActiveCategory: (category: Category | null) => void; } export const CategoryHorizonalList = (props: IProps) => { const { activeCategory, allCategories, setActiveCategory } = props; if (!allCategories || !allCategories.length || allCategories.length < 3) { return null; } const orderedCategories = allCategories .slice() .sort((a, b) => (a.createdAt > b.createdAt ? 1 : -1)); const isCategorySelected = (category: Category) => { return category.id === activeCategory?.id; }; return ( {orderedCategories.map((category, index) => { const isSelected = isCategorySelected(category); const name = category.name; const glyph = name.toLowerCase() as availableGlyphs; const hasGlyph = getGlyph(glyph); return ( setActiveCategory(isSelected ? null : category)} extrastyles={{ alignItems: 'center', background: 'none', flexDirection: 'column', justifyContent: 'center', minWidth: ['80px', '100px', '130px'], marginX: 1, paddingY: 2, textAlign: 'center', width: ['80px', '100px', '130px'], ...(isSelected ? { borderColor: 'green', ':hover': { borderColor: 'green' }, } : { borderColor: 'background', ':hover': { borderColor: 'background' }, }), }} isSelected={isSelected} > {name} ); })} ); }; ================================================ FILE: packages/components/src/CharacterCount/CharacterCount.stories.tsx ================================================ import { CharacterCount } from './CharacterCount'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Components/CharacterCount', component: CharacterCount, } as Meta; const errorValues = [ { currentSize: 10, minSize: 50, maxSize: 200, }, { currentSize: 200, minSize: 0, maxSize: 100, }, ]; export const Default: StoryFn = () => ( ); export const WithValidState: StoryFn = () => ( ); export const WithError: StoryFn = () => ( <> {errorValues.map((state, index) => { return ( ); })} ); ================================================ FILE: packages/components/src/CharacterCount/CharacterCount.tsx ================================================ import { Text } from 'theme-ui'; export interface ICharacterCountProps { currentSize: number; minSize: number; maxSize: number; } export const CharacterCount = ({ currentSize, minSize, maxSize }: ICharacterCountProps) => { const percentageOfMax = currentSize / maxSize; const characterCountThresholds = [ { value: 1.0, color: 'red', font_weight: 'bold' }, { value: 0.95, color: 'subscribed', font_weight: 'bold' }, { value: 0.9, color: 'green', font_weight: 'bold' }, ]; let color = characterCountThresholds.find((threshold) => threshold.value <= percentageOfMax)?.color ?? 'green'; let fontWeight = characterCountThresholds.find((threshold) => threshold.value <= percentageOfMax)?.font_weight ?? 'normal'; if (currentSize < minSize) { color = characterCountThresholds[0].color; fontWeight = 'bold'; } return ( {currentSize} / {maxSize} ); }; ================================================ FILE: packages/components/src/CommentAvatar/CommentAvatar.tsx ================================================ import { Avatar, Image } from 'theme-ui'; import defaultBaloonUrl from '../../assets/images/author.svg'; import defaultProfileImage from '../../assets/images/default_member.svg'; type CommentAvatarProps = { displayName?: string; isCommentAuthor?: boolean; photo?: string | null; }; export const CommentAvatar = (props: CommentAvatarProps) => { const { displayName, isCommentAuthor = false, photo } = props; const alt = displayName ? `Avatar of ${displayName}` : 'Avatar of comment author'; return ( <> {isCommentAuthor && ( )} ); }; ================================================ FILE: packages/components/src/CommentBody/CommentBody.tsx ================================================ import { useEffect, useRef, useState } from 'react'; import { Text } from 'theme-ui'; import { LinkifyText } from '../LinkifyText/LinkifyText'; interface IProps { body: string; } const SHORT_COMMENT = 129; export const CommentBody = ({ body }: IProps) => { const textRef = useRef(null); const [textHeight, setTextHeight] = useState(0); const [isShowMore, setShowMore] = useState(false); useEffect(() => { if (textRef.current) { setTextHeight(textRef.current.scrollHeight); } }, [body]); const maxHeight = isShowMore ? 'max-content' : '128px'; return ( <> {body.trim()} {textHeight > SHORT_COMMENT && ( setShowMore((prev) => !prev)} sx={{ color: 'gray', cursor: 'pointer', fontSize: [3], }} > {isShowMore ? 'Show less' : 'Show more'} )} ); }; ================================================ FILE: packages/components/src/CommentDisplay/CommentDisplay.stories.tsx ================================================ import { fakeCommentSB } from '../utils'; import { CommentDisplay } from './CommentDisplay'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Commenting/CommentDisplay', component: CommentDisplay, } as Meta; const itemType = 'CommentItem'; const isEditable = false; const isLoggedIn = true; const votedUsefulCount = 0; const hasUserVotedUseful = false; const mockHandleUsefulClick = async (vote: 'add' | 'delete', eventCategory = 'Comment'): Promise => { console.log('handleUsefulClick called with:', { vote, eventCategory }); return new Promise((resolve) => setTimeout(() => resolve(), 300)); }; // Common useful button config const usefulButtonConfig = { votedUsefulCount, hasUserVotedUseful, isLoggedIn, onUsefulClick: mockHandleUsefulClick, }; export const Default: StoryFn = () => { const comment = fakeCommentSB(); return ( } /> ); }; export const Editable: StoryFn = () => { const comment = fakeCommentSB(); return } />; }; export const Edited: StoryFn = () => { const comment = fakeCommentSB({ modifiedAt: new Date() }); return ( } /> ); }; export const LongText: StoryFn = () => { const commentText = `Ut dignissim, odio a cursus pretium, erat ex dictum quam, a eleifend augue mauris vel metus. Suspendisse pellentesque, elit efficitur rutrum maximus, arcu enim congue ipsum, vel aliquam ipsum urna quis tellus. Mauris at imperdiet nisi. Integer at neque ex. Nullam vel ipsum sodales, porttitor nulla vitae, tincidunt est. Pellentesque vitae lectus arcu. Integer dapibus rutrum facilisis. Nullam tincidunt quam at arcu interdum, vitae egestas libero vehicula. Morbi metus tortor, dapibus id finibus ac, egestas quis leo. Phasellus scelerisque suscipit mauris sed rhoncus. In quis ultricies ipsum. Integer vitae iaculis risus, sit amet elementum augue. Pellentesque vitae sagittis erat, eget consectetur lorem.\n\nUt pharetra molestie quam id dictum. In molestie, arcu sit amet faucibus pulvinar, eros erat egestas leo, at molestie nunc velit a arcu. Aliquam erat volutpat. Vivamus vehicula mi sit amet nibh auctor efficitur. Duis fermentum sem et nibh facilisis, ut tincidunt sem commodo. Nullam ornare ex a elementum accumsan. Etiam a neque ut lacus suscipit blandit. Maecenas id tortor velit.\n\nInterdum et malesuada fames ac ante ipsum primis in faucibus. Nam ut commodo tellus. Maecenas at leo metus. Vivamus ullamcorper ex purus, volutpat auctor nunc lobortis a. Integer sit amet ornare nisi, sed ultrices enim. Pellentesque ut aliquam urna, eu fringilla ante. Nullam dui nibh, feugiat id vestibulum nec, efficitur a lorem. In vitae pellentesque tellus. Pellentesque sed odio iaculis, imperdiet turpis at, aliquam ex. Praesent iaculis bibendum nibh, vel egestas turpis ultrices ac. Praesent tincidunt libero sed gravida ornare. Aliquam vehicula risus ut molestie suscipit. Nunc erat odio, venenatis nec posuere in, placerat eget massa. Sed in ultrices ex, vel egestas quam. Integer lectus magna, ornare at nisl sed, convallis euismod enim. Cras pretium commodo arcu non bibendum.\n\nNullam dictum lectus felis. Duis vitae lacus vitae nisl aliquet faucibus. Integer neque lacus, dignissim sed mi et, dignissim luctus metus. Cras sollicitudin vestibulum leo, ac ultrices sapien bibendum ac. Phasellus lobortis aliquam libero eu volutpat. Donec vitae rutrum tellus. Fusce vel ante ipsum. Suspendisse mollis tempus porta. Sed a orci tempor, rhoncus tortor eu, sodales justo.`; const comment = fakeCommentSB({ comment: commentText }); return ( } /> ); }; export const ShortTextWithLink: StoryFn = () => { const comment = fakeCommentSB({ comment: `Ut dignissim, odio a cursus pretium. https://example.com`, }); return ( } /> ); }; export const UserVotedUseful: StoryFn = () => { const comment = fakeCommentSB(); const votedConfig = { ...usefulButtonConfig, hasUserVotedUseful: true, votedUsefulCount: 5, }; return } />; }; ================================================ FILE: packages/components/src/CommentDisplay/CommentDisplay.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { fireEvent } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { AuthorsContext } from '../providers/AuthorsContext'; import { render } from '../test/utils'; import { CommentDisplay } from './CommentDisplay'; import { UsefulConfig } from '../UsefulStatsButton/UsefulButtonLite'; vi.mock('../UsefulStatsButton/UsefulButtonLite', () => ({ UsefulButtonLite: ({ onUsefulClick, votedUsefulCount }: UsefulConfig) => ( ), })); vi.mock('../CommentAvatar/CommentAvatar', () => ({ CommentAvatar: ({ displayName }: any) =>
{displayName}
, })); vi.mock('../CommentBody/CommentBody', () => ({ CommentBody: ({ body }: any) =>
{body}
, })); describe('CommentDisplay', () => { const mockOnUsefulClick = vi.fn(async () => Promise.resolve()); const mockComment = { id: 1, comment: 'Test comment', createdAt: new Date('2023-01-01T00:00:00Z'), modifiedAt: new Date('2023-01-01T00:00:00Z'), deleted: false, highlighted: false, createdBy: { username: 'testuser', displayName: 'Test User', id: 0, isVerified: false, isSupporter: false, photo: null, }, sourceId: 1, sourceType: 'questions' as const, parentId: null, voteCount: 0, hasVoted: false, }; const mockUsefulButtonConfig = { hasUserVotedUseful: false, votedUsefulCount: 5, isLoggedIn: true, onUsefulClick: mockOnUsefulClick, }; const mockAuthorsContextValue = { authors: [0], }; const renderWithAuthorsContext = (ui: React.ReactElement) => { return render({ui}); }; beforeEach(() => { vi.clearAllMocks(); }); it('renders comment body and avatar', () => { const { getByTestId, getAllByTestId } = renderWithAuthorsContext( } usefulButtonConfig={mockUsefulButtonConfig} />, ); expect(getByTestId('comment-body')).toBeInTheDocument(); expect(getAllByTestId('comment-avatar').length).toBeGreaterThanOrEqual(1); expect(getByTestId('useful-button')).toBeInTheDocument(); }); it('calls onUsefulClick when useful button is clicked', () => { const { getByTestId } = render( } usefulButtonConfig={mockUsefulButtonConfig} />, ); fireEvent.click(getByTestId('useful-button')); expect(mockOnUsefulClick).toHaveBeenCalledWith('add'); }); it('increments useful count when useful button is clicked', () => { let count = 5; const handleUsefulClick = vi.fn(() => { count += 1; return Promise.resolve(); }); const { getByTestId, rerender } = render( } usefulButtonConfig={{ ...mockUsefulButtonConfig, votedUsefulCount: count, onUsefulClick: handleUsefulClick, }} />, ); const button = getByTestId('useful-button'); expect(button).toHaveTextContent('Useful 5'); fireEvent.click(button); expect(handleUsefulClick).toHaveBeenCalledWith('add'); rerender( } usefulButtonConfig={{ ...mockUsefulButtonConfig, votedUsefulCount: count, onUsefulClick: handleUsefulClick, }} />, ); expect(getByTestId('useful-button')).toHaveTextContent('Useful 6'); }); }); ================================================ FILE: packages/components/src/CommentDisplay/CommentDisplay.tsx ================================================ import type { Comment } from 'oa-shared'; import { useContext } from 'react'; import { Box, Flex, Text } from 'theme-ui'; import { CommentAvatar } from '../CommentAvatar/CommentAvatar'; import { CommentBody } from '../CommentBody/CommentBody'; import { DisplayDate } from '../DisplayDate/DisplayDate'; import { AuthorsContext } from '../providers/AuthorsContext'; import { UsefulButtonLite, UsefulConfig } from '../UsefulStatsButton/UsefulButtonLite'; import { Username } from '../Username/Username'; export interface IProps { comment: Comment; itemType: 'ReplyItem' | 'CommentItem'; isEditable: boolean | undefined; actions: React.ReactNode; usefulButtonConfig: UsefulConfig; } const DELETED_COMMENT = 'The original comment got deleted'; export const CommentDisplay = (props: IProps) => { const { comment, actions, usefulButtonConfig } = props; const { authors } = useContext(AuthorsContext); const border = `${comment.highlighted ? '2px dashed black' : 'none'}`; if (comment.deleted) { return ( [{DELETED_COMMENT}] ); } if (!comment.deleted) { return ( {comment.createdBy && } {actions && {actions}} ); } }; ================================================ FILE: packages/components/src/CommentsTitle/CommentsTitle.stories.tsx ================================================ import { CommentsTitle } from './CommentsTitle'; import type { Meta, StoryFn } from '@storybook/react-vite'; import type { Comment } from 'oa-shared'; export default { title: 'Commenting/CommentsTitle', component: CommentsTitle, } as Meta; export const NoComments: StoryFn = () => ; export const OneComment: StoryFn = () => { const comment = {} as Comment; return ; }; export const MultipleComments: StoryFn = () => { const comment = {} as Comment; return ; }; ================================================ FILE: packages/components/src/CommentsTitle/CommentsTitle.test.tsx ================================================ import '@testing-library/jest-dom/vitest'; import { describe, expect, it } from 'vitest'; import { render } from '../test/utils'; import { COMMENTS, CommentsTitle, NO_COMMENTS, ONE_COMMENT } from './CommentsTitle'; import type { Comment } from 'oa-shared'; describe('CommentsTitle', () => { it('renders correctly when there are zero comments', () => { const { getByText } = render(); expect(getByText(NO_COMMENTS)).toBeInTheDocument(); }); it('renders correctly when there is one comment', () => { const comment = {} as Comment; const { getByText } = render(); expect(getByText(ONE_COMMENT)).toBeInTheDocument(); }); it('renders correctly when there are multiple comments', () => { const comment = {} as Comment; const { getByText } = render(); expect(getByText(`3 ${COMMENTS}`)).toBeInTheDocument(); }); }); ================================================ FILE: packages/components/src/CommentsTitle/CommentsTitle.tsx ================================================ import type { Comment } from 'oa-shared'; import { useMemo } from 'react'; import { Heading } from 'theme-ui'; export const NO_COMMENTS = 'Start the discussion'; export const ONE_COMMENT = '1 Comment'; export const COMMENTS = 'Comments'; export interface IProps { comments: Comment[]; } export const CommentsTitle = ({ comments }: IProps) => { const title = useMemo(() => { const commentCount = comments.filter((x) => !x.deleted).length + comments.flatMap((x) => x.replies).filter((x) => !!x).length; if (commentCount === 0) { return NO_COMMENTS; } if (commentCount === 1) { return ONE_COMMENT; } return `${commentCount} ${COMMENTS}`; }, [comments]); return ( {title} ); }; ================================================ FILE: packages/components/src/ConfirmModal/ConfirmModal.stories.tsx ================================================ import { ConfirmModal } from './ConfirmModal'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/ConfirmModal', component: ConfirmModal, } as Meta; export const Default: StoryFn = () => ( null} handleConfirm={() => null} /> ); ================================================ FILE: packages/components/src/ConfirmModal/ConfirmModal.tsx ================================================ import { Flex, Text } from 'theme-ui'; import { Button } from '../Button/Button'; import { Modal } from '../Modal/Modal'; export interface Props { message: string; confirmButtonText: string; isOpen: boolean; handleCancel: () => void; handleConfirm: () => void; width?: number; cancelVariant?: 'outline' | 'destructive' | 'primary'; confirmVariant?: 'outline' | 'destructive' | 'primary'; } export const ConfirmModal = (props: Props) => { const { message, confirmButtonText, isOpen, width, confirmVariant = 'primary', cancelVariant = 'outline', } = props; return ( props?.handleCancel} isOpen={isOpen} width={width}> {message} ); }; ================================================ FILE: packages/components/src/ContentStatistics/ContentStatistics.stories.tsx ================================================ import { faker } from '@faker-js/faker'; import { ContentStatistics } from './ContentStatistics'; import type { Meta, StoryFn } from '@storybook/react-vite'; export default { title: 'Layout/ContentStatistics', component: ContentStatistics, } as Meta; export const Default: StoryFn = () => ( ); export const SingleCount: StoryFn = () => ( ); ================================================ FILE: packages/components/src/ContentStatistics/ContentStatistics.tsx ================================================ import React, { cloneElement, isValidElement, useCallback, useState } from 'react'; import { Flex, Text } from 'theme-ui'; import { Button } from '../Button/Button'; import { StatisticsList } from './ContentStatisticsList'; import type { IStatistic } from './types'; export interface IProps { statistics: IStatistic[]; alwaysShow?: boolean; } export const ContentStatistics = ({ statistics, alwaysShow }: IProps) => { const [showStats, setShowStats] = useState(false); const [activeModal, setActiveModal] = useState(null); const handleShowStats = () => { setShowStats(!showStats); }; const handleOpenModal = useCallback(async (stat: IStatistic) => { if (!stat.modalComponent) return; let data = undefined; if (stat.onOpen) { try { data = await stat.onOpen(); } catch (error) { console.error('Error loading modal data:', error); } } const modalElement = stat.modalComponent(data); if (isValidElement(modalElement)) { setActiveModal( cloneElement(modalElement, { onClose: () => setActiveModal(null), }), ); } else { setActiveModal(null); } }, []); const visible = showStats || alwaysShow === true; return ( {showStats ? '' : 'More Information'}