feat: add scouting portal with fellowship applications, fellowships, and reports#76
Merged
Conversation
7c49475 to
32d0029
Compare
65003e1 to
f6e91f1
Compare
Replace repeated `name || discordGlobalName || discordUserName` fallback chains with a single `displayName` getter on the User entity. Also expose displayName in the leaderboard response DTO. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add received, accepted, and rejected email notifications for fellowship applications with HTML and plain text templates following existing patterns. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add entities for fellowship application tracking (type, proposal, status, reviewer remarks) and fellowship records (post-acceptance onboarding data). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add endpoints for submitting, listing, and reviewing fellowship applications. Includes admin review with accept/reject, reviewer remarks, and email notifications on status changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add endpoints for completing fellowship onboarding after application acceptance, and listing/viewing fellowships for users and admins. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Schedule reminder emails on the 20th, 25th, and 28th of each month for active fellows who haven't submitted their monthly reports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lication The folder hosts the unsigned contract and is where the accepted fellow uploads their W-8BEN form. The URL is captured at review time, persisted on the Fellowship, and surfaced via the fellowship API. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restructure the accepted email to mirror the manual onboarding flow: contract signing, W-8BEN submission with India-specific guidance, Skydo payment setup, and the in-app onboarding form. Includes the Drive folder URL captured at review time and CCs fellowship@bitshala.org. Adds CC support to the mail core/service helpers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds CHANGES_REQUESTED status so reviewers can ask applicants to revise their proposal with feedback instead of only accepting or rejecting. Applicants edit the proposal and resubmit via the same submit endpoint, which clears the previous reviewer trail and loops back to SUBMITTED. Renames submitDraft/updateDraft to submitApplication/updateApplication since the same endpoints now also serve changes-requested applications. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- startContract rejects end dates on or before the start date (was accepted and produced inverted report periods) - proposal capped at 20k chars (client caps the two long-form sections at 3k each; this bounds the whole serialized payload) - driveFolderUrl must be an actual Drive folder URL (drive.google.com/drive/folders/..., incl. /u/N/), not just any drive.google.com link Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET /fellowship-applications/github/:username returns
{ exists: true | false | null } — null when GitHub could not be
reached (rate limit, network), which clients must treat as unknown,
never as missing. Reuses the existing authenticated Octokit via a new
userExists() on GitHubClassroomClient (404 -> false, other errors
rethrown and mapped to null with a warning log).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a574ab6 to
0b0fad6
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oints - Validated query DTOs for the three admin list endpoints (status/type filters, plus month/year/fellowshipId and parent-fellowship type for reports) - Whitelisted sortBy/sortOrder params (createdAt, updatedAt, contract dates, amount, reporting period) with NULLS LAST and stable id tiebreaker - Case-insensitive ILIKE search across name-like fields (applicant or fellow identity, project name, mentor contact, GitHub profile, location) with LIKE-wildcard escaping - Invalid enum/number query params now fail with 400 instead of a Postgres enum-cast 500 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Start-contract now accepts periodMonths (1-24) instead of an explicit end date and computes the end date from the start date. Adds an addMonths helper that clamps month-end overflow and works in UTC. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the single free-form markdown `proposal` blob with first-class, individually-validated columns: title, problemStatement, plan, mentorName, mentorContact, mentorTestimonial, github and links. - Entity: drop `proposal`, add the new columns (links as text[]) - DTOs: shared ProposalFieldsDto enforcing upper bounds only, so drafts can auto-save partial values; create adds immutable `type`, update omits it - Submit: full ruleset (required fields, title required, conditional github for DEVELOPER, link caps/format/dedup) validated in the service - GET /:id/proposal now returns the structured shape; list/detail stay lean - GitHub existence check remains advisory and never blocks submission - Migration 1775546228053 updated in place; seed migration emits structured dummy data Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Onboarding is now collected during the fellowship application instead of as a separate post-acceptance step. Fellowship becomes a pure contract record. - Move onboarding fields onto FellowshipApplication (projectName, projectGithubLink, academicBackground, graduationYear, professionalExperience, domains, codingLanguages, educationInterests, bitcoinContributions, bitcoinMotivation, bitcoinOssGoal, additionalInfo, questionsForBitshala) with draft-friendly validation. - Required at submit for all tracks except additionalInfo/questionsForBitshala; projectName, projectGithubLink and codingLanguages are developer-only. - Write profile fields (location, github) through to the user profile from create/update rather than duplicating them on the application. - Drop the onboarding columns from Fellowship, remove CompleteFellowshipOnboardingDto, the PATCH /fellowships/:id/onboarding endpoint and completeOnboarding service method; slim FellowshipResponseDto. - Repoint fellowship and fellowship-report list search off the removed columns. - Update the initial schema and seed migrations in place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reports now include a nested `fellowship` field identical field-for-field to a GET /fellowships list item, reusing FellowshipResponseDto. Eager-load the fellowship application alongside the user in all report finders and select it in the list query builder so the serializer has the data. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GET /fellowship-reports/me threw "Cannot read properties of undefined (reading 'mentorContact')" because the query did not eager-load the fellowship.application relation that FellowshipResponseDto.fromEntity depends on. Add application: true to the fellowship relation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Onboarding was merged into the application in af667a6, so the separate "Complete Onboarding" step in the acceptance email is no longer needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the single markdown `content` blob with structured fields: `summary` (markdown) and `links` (GitHub PR/issue URLs), plus four optional reflective questions (challengingWork, keyLearning, reviewerFeedback, growthGoal). Drafts are lenient (length caps + GitHub link format only); submit stays strict on `summary` (non-empty, <=3500 chars) and an ACTIVE fellowship. Reflective questions are optional and never gate submission. The content endpoint and create/update DTOs carry the new shape; list/metadata and review payloads are unchanged. Existing migrations updated in place (data is disposable) — no new migration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add @googleapis/drive and a single GoogleDriveService wrapper (mirroring the GitHubClassroomClient pattern) as the only code that touches Drive. Every call passes supportsAllDrives: true; no permission/sharing calls exist. The client factory base64-decodes a service-account key. Add the googleDrive config block (serviceAccountKey, sharedDriveId, optional rootFolderId), validated like the rest, with placeholders in the example config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the FellowshipDocument entity (one current row per application+type), the FellowshipApplication.driveFolderId column, the FellowshipDocumentType and FellowshipDocumentStatus enums, and the document-phase FellowshipStatus values (AWAITING_DOCUMENTS, DOCUMENTS_IN_REVIEW, DOCUMENTS_APPROVED). All schema changes are folded into this PR's existing migration rather than a new file, per the design. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mail Rewrite the fellowship-application-accepted templates to link into the app (via app.frontEndUrl) instead of a Drive folder, and change sendFellowshipApplicationAcceptedEmail to take the fellowship id and build that deep link. Add sendFellowshipDocumentRejectedEmail with its html/text templates and a TemplateContextMap entry, sent to the fellow with the rejection reason and a re-upload link. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the single service that proxies all document handling by our own document UUID: list, download (stream Drive -> backend), upload/re-upload (new Drive revision), and admin approve/reject. Ownership is enforced per request and a document must belong to the addressed fellowship, so Drive IDs never cross an API boundary. Provisioning, upload and review run in DbTransactionService transactions; accept-time folder creation is guarded by a pessimistic lock + driveFolderId re-check so concurrent accepts can't orphan a folder, and Drive writes are best-effort rolled back on DB failure. Uploads are validated by %PDF- magic bytes and the stored filename is sanitized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make PATCH /fellowship-applications/:id/review multipart: accepting now carries the Bitshala-signed unsigned-contract PDF (replacing the driveFolderUrl field). The accept branch creates the per-application Drive folder, uploads the contract, and creates the Fellowship (AWAITING_DOCUMENTS) plus the three document rows in one transaction, then emails the fellow a link into the app. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…proval Add the proxied document routes on the fellowships controller — list, download (StreamableFile with attachment + nosniff), upload by document id (15 MB PDF FileInterceptor), and admin approve/reject. Gate start-contract on DOCUMENTS_APPROVED instead of PENDING, and drop driveFolderUrl from FellowshipResponseDto. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…reports Require the four report reflection prompts (challenging work, key learning, reviewer feedback, growth goal) at submit. For applications, require location on all tracks, make mentor name/contact/testimonial and project name required for developers and designers (optional for educators), and keep the project GitHub link developer-only. Update request DTO comments to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Shared, admin-only internal notes on a fellowship application. The table is folded into the undeployed baseline fellowship migration rather than added as a new one. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Create, list, edit and delete with application-scoped lookups; edits and deletes are restricted to the note's author. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the notes controller and service into the fellowship applications module. Every route is admin-only via @roles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the follow-up migration (nullable applicationId on fellowship_document/fellowship_application_note + regenerated FK/UQ names) into the base migration, using TypeORM's generated constraint names so the schema no longer drifts from the entity metadata. Deletes the redundant follow-up migration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
🤖 Generated with Claude Code