Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
211 commits
Select commit Hold shift + click to select a range
b48b229
Bump vite from 7.3.1 to 7.3.2
dependabot[bot] Apr 7, 2026
bdaef7b
Update docs/sourcing-parts.md
Cybdo Apr 7, 2026
34a0575
Update docs/index.md
Cybdo Apr 7, 2026
c9904d6
Update docs/cost.md
Cybdo Apr 7, 2026
32797eb
Update docs/faq.md
Cybdo Apr 7, 2026
2a8c0dc
Update docs/sourcing-parts.md
Cybdo Apr 7, 2026
a866a34
Update docs/sourcing-parts.md
Cybdo Apr 7, 2026
cca2a4c
Apply PR review feedback to docs
Copilot Apr 7, 2026
522506b
Fix line endings in sourcing-parts.md
Copilot Apr 7, 2026
fbbd64b
remvoe deleted projects from pending count and view
Apr 7, 2026
1940ed4
Merge pull request #37 from hackclub/clarify-docs
Cybdo Apr 7, 2026
6475357
Merge pull request #39 from hackclub/remove-deleted-projects-from-mai…
Cybdo Apr 7, 2026
2d1d796
Render pitch text as markdown with Slack link conversion
The-UnknownHacker Apr 7, 2026
77356d2
Merge pull request #40 from hackclub/fix-pitch-markdown
The-UnknownHacker Apr 7, 2026
2e537e6
Add hidden toggle for projects
The-UnknownHacker Apr 7, 2026
0159685
Merge pull request #41 from hackclub/add-hidden-project-toggle
The-UnknownHacker Apr 7, 2026
a69e559
Revise funding details and project steps
Cybdo Apr 7, 2026
ea96a36
Merge pull request #42 from hackclub/fix-docs-again
Cybdo Apr 7, 2026
0d976aa
Show public-facing message for approved builds to non-owners
The-UnknownHacker Apr 7, 2026
6384d82
Merge pull request #43 from hackclub/add-hidden-project-toggle
The-UnknownHacker Apr 7, 2026
c6f12e7
Rework permissions with role-based defaults and add user role filter
The-UnknownHacker Apr 7, 2026
b228e49
Merge pull request #44 from hackclub/add-hidden-project-toggle
The-UnknownHacker Apr 7, 2026
7ee5411
Update favicon and fix Rack::Attack throttle crash
The-UnknownHacker Apr 7, 2026
607773d
Merge pull request #45 from hackclub/update-favicon-and-fix-rack-attack
The-UnknownHacker Apr 7, 2026
9f5b486
Bust favicon cache with version query param
The-UnknownHacker Apr 7, 2026
04e16cc
Merge pull request #46 from hackclub/bust-favicon-cache
The-UnknownHacker Apr 7, 2026
a575f93
Add Slack support bot with ticket tracking and admin page
The-UnknownHacker Apr 7, 2026
ad62f4a
Merge pull request #47 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
8511a40
Post resolved message to public thread when staff resolves from BTS
The-UnknownHacker Apr 7, 2026
1c7ab2d
Add support ticket detail page with reply, claim, and resolve from web
The-UnknownHacker Apr 7, 2026
2efe976
Merge pull request #48 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
a29a3b6
React with checkmark on original message when ticket resolved
The-UnknownHacker Apr 7, 2026
24ab1c1
Allow admins to permanently delete support tickets
The-UnknownHacker Apr 7, 2026
0b1d1b8
Merge pull request #49 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
0fb35c0
remove db execute route; sets transactions as read only from admin/da…
Apr 7, 2026
9fed7ef
Merge pull request #50 from hackclub/limit-db-write-access
Cybdo Apr 7, 2026
dd4aaed
Fix support bot: sync threads both ways, ping user on resolve, react …
The-UnknownHacker Apr 7, 2026
573e86e
Merge pull request #51 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
f3304e1
Prevent double resolve, use @mention for resolver in resolved message
The-UnknownHacker Apr 7, 2026
ea3d353
Sync web replies to BTS thread
The-UnknownHacker Apr 7, 2026
27a3620
Merge pull request #52 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
003a2eb
Make admin permissions explicit and individually revocable
The-UnknownHacker Apr 7, 2026
5ce5e01
Merge pull request #53 from hackclub/support-bot
The-UnknownHacker Apr 7, 2026
23443c4
Allow normal tier projects to use git journal mode
The-UnknownHacker Apr 7, 2026
30cd4bd
Merge pull request #54 from hackclub/allow-git-journal-normal-tier
The-UnknownHacker Apr 7, 2026
769fa32
Allow normal tier projects to use git journal mode
The-UnknownHacker Apr 7, 2026
ec69ade
Ping user and mention reviewer in pitch decision notifications
The-UnknownHacker Apr 7, 2026
50b098a
Merge pull request #55 from hackclub/allow-git-journal-normal-tier
The-UnknownHacker Apr 7, 2026
d8a0e47
Clean up dashboard and explore UI
The-UnknownHacker Apr 8, 2026
ad4b825
Merge pull request #56 from hackclub/ui-cleanup
The-UnknownHacker Apr 8, 2026
eac3bf8
Merge pull request #38 from hackclub/dependabot/npm_and_yarn/vite-7.3.2
The-UnknownHacker Apr 8, 2026
e225ebe
Update favicon to new design
The-UnknownHacker Apr 8, 2026
7dacfa0
Merge pull request #57 from hackclub/update-favicon-v2
The-UnknownHacker Apr 8, 2026
7e46d6b
Bump addressable from 2.8.8 to 2.9.0
dependabot[bot] Apr 8, 2026
c27778b
Add internal notes on user profiles
The-UnknownHacker Apr 8, 2026
62743ca
Merge pull request #60 from hackclub/user-internal-notes
The-UnknownHacker Apr 8, 2026
102966d
Add Hackatime trust level to user profiles, require ! prefix for BTS …
The-UnknownHacker Apr 8, 2026
ac5852f
Merge pull request #61 from hackclub/hackatime-and-relay-prefix
The-UnknownHacker Apr 8, 2026
d989570
UI tweaks, support commands, remove public mark answered button
The-UnknownHacker Apr 9, 2026
801e8a9
Merge pull request #62 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 9, 2026
4d6a3fa
Add news posts, redesign dashboard, misc UI fixes
The-UnknownHacker Apr 9, 2026
2576d9e
Merge pull request #63 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 9, 2026
91504fe
Add news post show page with markdown rendering
The-UnknownHacker Apr 10, 2026
6197bc5
Merge pull request #64 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 10, 2026
f4f666e
Add public profiles, kudos, staff picks, and superadmin
The-UnknownHacker Apr 10, 2026
b15c446
Merge pull request #65 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 10, 2026
65e0de1
Action-based audit log, reviewer hackatime default, misc
The-UnknownHacker Apr 10, 2026
9712f63
Merge pull request #66 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 10, 2026
4db5b98
Steel coin shop, 4 tiers, build proof, coin adjustments
The-UnknownHacker Apr 10, 2026
92890c0
Merge pull request #67 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 10, 2026
48f04d7
Coin history, tier swap, address fixes, README spinner
The-UnknownHacker Apr 11, 2026
b36d1ed
Merge pull request #68 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
1702b42
Misc shop and admin polish
The-UnknownHacker Apr 11, 2026
bb68771
Project kudos and shop quantity
The-UnknownHacker Apr 11, 2026
dad6bb4
Merge pull request #69 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
558072c
Manual shop access toggle on admin user page
The-UnknownHacker Apr 11, 2026
f69362c
new resources page layout, wip updating documentation files
Apr 11, 2026
10e9cd8
Merge pull request #70 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
20b9a77
Guard shop_unlocked check when column missing
The-UnknownHacker Apr 11, 2026
2f1e262
Merge pull request #71 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
faa3d6b
Lock role/perm edits to superadmins, add admin-page link on project show
The-UnknownHacker Apr 11, 2026
3f99605
Merge pull request #72 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
045b7ec
Auto-invite beta users to slack channel + manual sync button
The-UnknownHacker Apr 11, 2026
e61ab0f
Merge pull request #73 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
8ee5899
Post shop item updates to Slack + swallow missing_scope on beta invite
The-UnknownHacker Apr 11, 2026
dbf8648
Merge pull request #74 from hackclub/misc-fixes-and-ui
The-UnknownHacker Apr 11, 2026
ad0fe99
Merge pull request #59 from hackclub/dependabot/bundler/addressable-2…
The-UnknownHacker Apr 11, 2026
21053ba
Ping @here for new shop items, stay silent on price updates
The-UnknownHacker Apr 11, 2026
a57a9b5
Merge pull request #76 from hackclub/shop-slack-ping-rules
The-UnknownHacker Apr 11, 2026
33fcf18
Make site usable on mobile
The-UnknownHacker Apr 11, 2026
09a8ee5
Merge pull request #77 from hackclub/mobile-responsive
The-UnknownHacker Apr 11, 2026
39f5b2d
Add examples to project tier descriptions
The-UnknownHacker Apr 11, 2026
da8db6f
Merge pull request #78 from hackclub/tier-examples
The-UnknownHacker Apr 11, 2026
8464f88
Reorder tiers 4→1 and add dollar ranges
The-UnknownHacker Apr 11, 2026
d31640a
Merge pull request #79 from hackclub/tier-reorder
The-UnknownHacker Apr 11, 2026
02fa9f3
Use orph-building placeholder for projects without a cover image
The-UnknownHacker Apr 11, 2026
0cf2e05
Merge pull request #80 from hackclub/project-placeholder
The-UnknownHacker Apr 11, 2026
45f0dfb
docs functional for now
Apr 11, 2026
31d076e
Merge pull request #81 from hackclub/update-resouces-pages
Cybdo Apr 11, 2026
af84681
change .com to .dev in docs/index.md
Apr 11, 2026
9704f67
remove specifc fallout reference in how-to-journal.md
Apr 11, 2026
cd18e63
Add GitHub section, project-tagged kudos, Built stat on user profile
The-UnknownHacker Apr 11, 2026
3d77ce5
Merge pull request #82 from hackclub/profile-improvements
The-UnknownHacker Apr 11, 2026
cd9148c
remove fallout reference
Apr 11, 2026
16484af
Merge pull request #83 from hackclub:fix-docs-again
Cybdo Apr 11, 2026
162f6ff
Devlog edit + bare hours, drop HCB grant link from projects, use buil…
The-UnknownHacker Apr 11, 2026
4ad619b
Merge pull request #84 from hackclub/devlog-and-review-cleanup
The-UnknownHacker Apr 11, 2026
1ea23af
add new prices
The-UnknownHacker Apr 11, 2026
e830e66
Merge pull request #85 from hackclub/tier-price-update
The-UnknownHacker Apr 11, 2026
6f70f7a
Add referral system with prize pool and leaderboard
The-UnknownHacker Apr 11, 2026
4163c9c
Sort shop items by price
The-UnknownHacker Apr 11, 2026
c3cc059
add referral system wahoooo pr 86
The-UnknownHacker Apr 11, 2026
3456e46
Backfill referral codes for existing users
The-UnknownHacker Apr 11, 2026
ab33852
Merge pull request #87 from hackclub/backfill-user-referral-codes
The-UnknownHacker Apr 11, 2026
39f851b
Admin action to generate referral codes for users
The-UnknownHacker Apr 11, 2026
6a03806
Merge pull request #88 from hackclub/generate-referral-code
The-UnknownHacker Apr 11, 2026
421d53f
Count pending referrals on the leaderboard
The-UnknownHacker Apr 11, 2026
c03df2f
Merge pull request #89 from hackclub/leaderboard-count-pending
The-UnknownHacker Apr 11, 2026
fe33488
Fix airtable sync timing, shop card hover expand, news markdown rende…
The-UnknownHacker Apr 12, 2026
79bdb98
Merge pull request #90 from hackclub/misc-fixes-shop-news-airtable
The-UnknownHacker Apr 12, 2026
768c6b6
Add shop regions with per-region pricing, fulfillment assignment, and…
The-UnknownHacker Apr 12, 2026
d2d313a
Merge pull request #91 from hackclub/shop-regions
The-UnknownHacker Apr 12, 2026
2b048aa
Include regional pricing in Slack shop update notifications
The-UnknownHacker Apr 12, 2026
ec16745
Merge pull request #92 from hackclub/slack-regional-pricing-v2
The-UnknownHacker Apr 12, 2026
02496e6
Fix region pricing sort and Slack notification on region price change
The-UnknownHacker Apr 12, 2026
1ffa696
Merge pull request #93 from hackclub/fix-region-pricing-sort-slack
The-UnknownHacker Apr 12, 2026
4b1c716
Add image paste-to-upload in devlog editor with HC CDN integration
The-UnknownHacker Apr 12, 2026
f63655a
Merge pull request #94 from hackclub/devlog-image-paste
The-UnknownHacker Apr 12, 2026
82118a2
Direct file upload to HC CDN for devlog images
The-UnknownHacker Apr 12, 2026
5c7be9a
Merge pull request #95 from hackclub/devlog-image-direct-cdn
The-UnknownHacker Apr 12, 2026
d6d5ebc
Support multiple git providers on user profile
The-UnknownHacker Apr 12, 2026
43c2d9f
Load avatars from all git providers
The-UnknownHacker Apr 12, 2026
d1b999c
Merge pull request #97 from hackclub/git-provider-avatars
The-UnknownHacker Apr 12, 2026
6649b21
change #into-the-forge to #forgery, clarify on pitching
Apr 12, 2026
58956d4
Merge pull request #96 from hackclub/git-provider-profile
The-UnknownHacker Apr 12, 2026
c179fdc
Prep for launch
The-UnknownHacker Apr 13, 2026
29ae5d3
Merge pull request #98 from hackclub/launch-prep
The-UnknownHacker Apr 13, 2026
5ce3d1d
Streaks, settings page, admin metrics dashboard
The-UnknownHacker Apr 13, 2026
20784d7
Streaks, settings page, admin metrics dashboard
The-UnknownHacker Apr 13, 2026
268f945
Merge pull request #99 from hackclub/streaks-metrics
The-UnknownHacker Apr 13, 2026
fcfd7fc
Merge pull request #100 from hackclub/launch-prep
The-UnknownHacker Apr 13, 2026
317f1ba
Relabel metric tiles, add hours and streak leaderboards
The-UnknownHacker Apr 13, 2026
5a6aa8d
Merge pull request #101 from hackclub/leaderboard-metrics-labels
The-UnknownHacker Apr 13, 2026
808727f
Polish leaderboard rows
The-UnknownHacker Apr 13, 2026
dcbf90e
Merge pull request #102 from hackclub/leaderboard-polish
The-UnknownHacker Apr 13, 2026
7fe77f5
Fix sign out button by moving it out of the settings Link
The-UnknownHacker Apr 13, 2026
bde8c05
Merge pull request #103 from hackclub/fix-signout-button
The-UnknownHacker Apr 13, 2026
40deb30
Prevent duplicate pitch resubmissions
The-UnknownHacker Apr 14, 2026
bbabde1
Merge pull request #104 from hackclub/fix-resubmit-pitch-duplicates
The-UnknownHacker Apr 14, 2026
c8111a2
Admin metrics refresh, review double-submit guard, chart fix
The-UnknownHacker Apr 14, 2026
33983e9
Merge pull request #105 from hackclub/admin-metrics-and-review-fixes
The-UnknownHacker Apr 14, 2026
6013d47
Keep project name on pitch resubmit
The-UnknownHacker Apr 14, 2026
73de94f
Merge pull request #106 from hackclub/keep-name-on-resubmit
The-UnknownHacker Apr 14, 2026
36594c0
Read HCA addresses from identity.addresses[]
The-UnknownHacker Apr 14, 2026
16b7244
Merge pull request #107 from hackclub/fix-hca-address-sync
The-UnknownHacker Apr 14, 2026
9923e73
Treat edited forge-channel messages as pitches
The-UnknownHacker Apr 14, 2026
17df8e7
Merge pull request #108 from hackclub/handle-slack-message-edits
The-UnknownHacker Apr 14, 2026
d312335
Minor UI tweaks and cleanup
The-UnknownHacker Apr 16, 2026
a3c3044
Merge pull request #110 from hackclub/ui-tweaks-cleanup
The-UnknownHacker Apr 16, 2026
5466b6b
Allow unauthenticated access to project, shop, and user pages
The-UnknownHacker Apr 16, 2026
73bd31b
Merge pull request #111 from hackclub/public-project-shop-user-pages
The-UnknownHacker Apr 16, 2026
59f7486
Polish public project page
The-UnknownHacker Apr 16, 2026
abc2893
Merge pull request #112 from hackclub/polish-public-project-page
The-UnknownHacker Apr 16, 2026
e8ef2fa
Allow users and admins to change project tiers
The-UnknownHacker Apr 17, 2026
cf57cb6
Merge pull request #113 from hackclub/project-tier-changes
The-UnknownHacker Apr 17, 2026
4e2b3c5
Only earn coins after build is approved
The-UnknownHacker Apr 17, 2026
f771d2b
Merge pull request #114 from hackclub/coins-only-on-build-approved
The-UnknownHacker Apr 17, 2026
5c36054
Increase pitch text truncation limit from 4000 to 32000 chars
The-UnknownHacker Apr 17, 2026
9d93d42
Merge pull request #115 from hackclub/fix-pitch-truncation
The-UnknownHacker Apr 17, 2026
65e7fb0
clarify pitching, fix link to docs on project page
Apr 18, 2026
821f686
add HCA_COMMUNITY field in .env so that community_untrusted apps work…
Apr 18, 2026
d2ae28d
add internal comments on projects in admin view
Apr 18, 2026
9c87c3b
Merge pull request #116 from hackclub/add-internal-project-comments
Cybdo Apr 18, 2026
b225790
remove points reference, clarify pitch format
Cybdo Apr 18, 2026
4519ac5
Rework review flow: per-devlog reviews, pitch_approved status, finish…
The-UnknownHacker Apr 18, 2026
c40d14c
Merge pull request #117 from hackclub/devlog-review-flow
The-UnknownHacker Apr 18, 2026
fa49eaa
Public API with full project data and docs
The-UnknownHacker Apr 18, 2026
989b6ff
Merge pull request #118 from hackclub/public-api
The-UnknownHacker Apr 18, 2026
e51c187
Fix TypeScript errors from pitch_approved status
The-UnknownHacker Apr 18, 2026
41db870
Fix API index pagy call
The-UnknownHacker Apr 18, 2026
a753844
Drop pagy from API, use simple pagination
The-UnknownHacker Apr 18, 2026
8bb2782
Add shipped boolean to API project response
The-UnknownHacker Apr 18, 2026
dd4c1e8
Merge pull request #119 from hackclub/api-shipped-field
The-UnknownHacker Apr 18, 2026
e986951
Show devlogs for all active project statuses
The-UnknownHacker Apr 18, 2026
c06eaba
Fix project show page: hide confusing status badges, show devlogs for…
The-UnknownHacker Apr 18, 2026
7bc94a2
Merge pull request #120 from hackclub/fix-project-show-ui
The-UnknownHacker Apr 18, 2026
58d915d
Fix project flow: pending for normal tiers, devlog detail page
The-UnknownHacker Apr 18, 2026
171ad5f
Merge pull request #121 from hackclub/fix-project-flow
The-UnknownHacker Apr 18, 2026
8067175
add unique identifier to project based on message id and channel id t…
Apr 18, 2026
651ce1a
Merge pull request #122 from hackclub/fix-duplicate-pitches
Cybdo Apr 18, 2026
aff403c
Revert "add unique identifier to project based on message id and chan…
Cybdo Apr 18, 2026
a2a4ae0
Merge pull request #123 from hackclub/revert-122-fix-duplicate-pitches
Cybdo Apr 18, 2026
c632e9c
Show all logged hours on leaderboard, not just approved
The-UnknownHacker Apr 19, 2026
2637b0e
Merge pull request #124 from hackclub/fix-leaderboard-hours
The-UnknownHacker Apr 20, 2026
a527629
Fix hours parsing regex to handle all time formats
The-UnknownHacker Apr 20, 2026
2b63bc2
Merge pull request #125 from hackclub/fix-hours-regex
The-UnknownHacker Apr 20, 2026
44d98ae
New project flow: pitch_pending status, auto project status from devl…
The-UnknownHacker Apr 20, 2026
c03b728
Merge pull request #126 from hackclub/new-project-flow
The-UnknownHacker Apr 20, 2026
f558530
Remove old submit for review card from project page
The-UnknownHacker Apr 20, 2026
a728536
Merge pull request #127 from hackclub/remove-old-submit-review
The-UnknownHacker Apr 20, 2026
45902e7
Add review controls to git journal devlogs
The-UnknownHacker Apr 20, 2026
6c3ce71
Merge pull request #128 from hackclub/git-devlog-review-controls
The-UnknownHacker Apr 20, 2026
bd82f05
Add maintenance mode with staff bypass and per-user bypass toggle
The-UnknownHacker Apr 20, 2026
4ab47d3
Merge pull request #129 from hackclub/maintenance-mode
The-UnknownHacker Apr 20, 2026
3ad3f6c
Collapse pitch+build review into single pending/approved flow
The-UnknownHacker Apr 21, 2026
015fe58
Preserve Airtable-sent project as approved in migration
The-UnknownHacker Apr 21, 2026
37d4a1b
Merge pull request #130 from hackclub/unified-review-flow
The-UnknownHacker Apr 21, 2026
2cae91e
Add review timeline on project pages
The-UnknownHacker Apr 21, 2026
e7dff99
Add expected-payout card on project page
The-UnknownHacker Apr 21, 2026
0b959a1
Merge pull request #132 from hackclub/expected-payout-card
The-UnknownHacker Apr 21, 2026
44a62eb
Merge pull request #133 from hackclub/review-timeline
The-UnknownHacker Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ applyTo: "**"

Keep your changes as low impact as possible. You do not need to give me a summary of changes. You do not need to test the changes. Try to reference other parts of the codebase to ensure your changes are consistent with the existing code style and practices. Keep your responses concise and focused.

Never add "Co-Authored-By" lines to commits. PR descriptions should be a simple description of the changes — nothing more. No test plans, no checklists, no headings, no marketing copy. One short paragraph (or a couple of plain sentences) is enough. Do not push code or create PRs unless explicitly asked. Do not add co-authorship attribution of any kind.

Read all context and instructions carefully before making changes. Code may be manually modified between messages. Do not suggest code that has been deleted or is no longer relevant.

This project uses ruby 3.4.4, rails 8.1.2 with React 19 and tailwind 4.1.18 through inertia-rails. Make sure to only suggest changes that are applicable to those versions. When possible, prefer to use the cli to generate boilerplate rather than editing files manually. You can always modify boilerplate generated from the cli.
Expand All @@ -27,10 +29,11 @@ Do not add comments unless they are absolutely necessary for clarity. Your code
Forge is a Hack Club program where teen builders (ages 13-18) get funded for hardware projects. Users pitch projects in Slack, admins review them, and approved builders document their progress via devlogs.

### Key Models
- **User** — has roles (user/admin/reviewer), permissions array, ban status, slack_id for Slack integration
- **Project** — belongs to user, has status enum (draft/pending/approved/returned/rejected), slack_channel_id/slack_message_ts for Slack thread tracking, pitch_text (cleaned user pitch), description (AI-generated admin summary)
- **User** — has roles (user/admin/reviewer/support/fulfillment), permissions array, ban status, slack_id for Slack integration
- **Project** — belongs to user, has status enum (draft/pending/approved/returned/rejected/pitch_pending/pitch_approved), slack_channel_id/slack_message_ts for Slack thread tracking, pitch_text (cleaned user pitch), description (AI-generated admin summary), hidden flag
- **Ship** — belongs to project, represents a submission of work for review
- **Devlog** — belongs to project, markdown entries documenting build progress (title, content, time_spent)
- **SupportTicket** — tracks support questions from Slack with status (open/claimed/resolved), two-way thread sync between public and BTS channels
- **FeatureFlag** — simple name/enabled toggle for app-wide feature flags
- All models use `has_paper_trail` for audit logging

Expand All @@ -43,28 +46,38 @@ Forge is a Hack Club program where teen builders (ages 13-18) get funded for har
6. Once approved, user can add devlogs on the project page or sync from JOURNAL.md in their GitHub repo

### Slack Integration
- **Webhook endpoint:** `POST /slack/events` — receives Slack events, filters for messages in the forge channel
- **Webhook endpoint:** `POST /slack/events` — receives Slack events, routes to pitch or support handlers
- **Interactivity endpoint:** `POST /slack/interactivity` — handles Block Kit button clicks (support ticket claim/resolve)
- **SlackPitchJob** — processes pitches, creates projects, replies in thread, adds :eyes: reaction
- **SlackNotifyJob** — posts review decisions back to Slack threads
- Bot ignores its own messages (`bot_id`), thread replies (`thread_ts`), and messages with subtypes
- Required env vars: `SLACK_SIGNING_SECRET`, `SLACK_FORGE_CHANNEL_ID`, `SLACK_BOT_TOKEN`
- Required Slack app scopes: `channels:history`, `groups:history`, `chat:write`, `reactions:write`
- **SupportTicketJob** — processes support questions, auto-responds, posts to BTS channel with buttons
- **SupportRelayJob** — relays BTS thread replies to public thread with staff name/avatar
- **SupportForwardJob** — forwards public thread replies to BTS thread with user name/avatar
- Bot ignores its own messages (`bot_id`) and messages with subtypes
- Events controller routes by channel: forge channel → pitches, support channel → tickets, BTS channel threads → relay
- Required env vars: `SLACK_SIGNING_SECRET`, `SLACK_FORGE_CHANNEL_ID`, `SLACK_BOT_TOKEN`, `SLACK_SUPPORT_CHANNEL_ID`, `SLACK_BTS_CHANNEL_ID`
- Required Slack app scopes: `channels:history`, `groups:history`, `chat:write`, `chat:write.customize`, `reactions:write`
- Event subscription: `message.channels` and `message.groups`

### Admin Panel
- Dashboard at `/admin` with links to all sections
- Dashboard at `/admin` with links filtered by user permissions
- **Projects** (`/admin/projects`) — table view, status filters, review panel with approve/return/reject/draft actions
- **Users** (`/admin/users`) — role management, permission toggles, ban/unban with reasons, soft/hard delete, restore
- **Users** (`/admin/users`) — role management, permission toggles, ban/unban with reasons, soft/hard delete, restore, role filter
- **Support** (`/admin/support`) — support ticket list with stats, leaderboard, status filters. Detail page with Slack thread view, reply from web, claim/resolve/delete
- **Ships** (`/admin/reviews`) — review and manage ship submissions
- **Feature Flags** (`/admin/feature_flags`) — create/toggle/delete feature flags, use `FeatureFlag.enabled?("name")` to check
- **Audit Log** (`/admin/audit_log`) — PaperTrail versions with human-readable descriptions, filterable by type/event, clickable detail view
- **Jobs** (`/admin/jobs`) — MissionControl::Jobs engine with dark theme override
- Route constraints: `StaffConstraint` (admin or reviewer) for dashboard/ships, `AdminConstraint` (admin only) for everything else
- **Database** (`/admin/database`) — raw SQL query/execute, admin-only
- Route constraints: `StaffConstraint` for all admin routes, individual controllers enforce permissions

### Permissions System
- **Roles** (array on User): `user`, `admin`, `reviewer`. Admin has all permissions by default.
- **Permissions** (array on User): `pending_reviews`, `projects`, `users`, `ships`, `feature_flags`, `audit_log`, `jobs`, `third_party` — for fine-grained access control on non-admin staff
- Check with `user.has_permission?("permission_name")` — returns true for admins automatically
- **Roles** (array on User): `user`, `admin`, `reviewer`, `support`, `fulfillment`
- **Permissions** (array on User): `pending_reviews`, `projects`, `users`, `ships`, `feature_flags`, `audit_log`, `jobs`, `third_party`, `support`
- Permissions are explicit for all roles including admin — `has_permission?` checks the permissions array only
- Assigning a role auto-grants default permissions (see `ROLE_DEFAULT_PERMISSIONS`), but individual permissions can be revoked afterwards
- Default permissions: admin gets all, reviewer gets pending_reviews/projects/ships, support gets projects/users/support, fulfillment gets projects/ships
- `staff?` returns true for admin, reviewer, support, or fulfillment roles

### AI Integration
- Uses Hack Club AI (`https://ai.hackclub.com/proxy/v1/chat/completions`) with `qwen/qwen3-32b` model
Expand All @@ -87,6 +100,8 @@ Forge is a Hack Club program where teen builders (ages 13-18) get funded for har
- `SLACK_SIGNING_SECRET` — Slack request verification
- `SLACK_FORGE_CHANNEL_ID` — Channel ID for `#into-the-forge`
- `HCA_CLIENT_ID` / `HCA_CLIENT_SECRET` — HCA OAuth credentials
- `SLACK_SUPPORT_CHANNEL_ID` — Channel ID for support questions
- `SLACK_BTS_CHANNEL_ID` — Channel ID for behind-the-scenes support staff
- `APP_URL` — Base URL for links in Slack messages (defaults to `https://forge.hackclub.com`)

### Deployment
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ GEM
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
uri (>= 0.13.1)
addressable (2.8.8)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
ahoy_matey (5.4.1)
activesupport (>= 7.1)
Expand Down Expand Up @@ -312,7 +312,7 @@ GEM
psych (5.3.1)
date
stringio
public_suffix (7.0.2)
public_suffix (7.0.5)
puma (7.2.0)
nio4r (~> 2.0)
pundit (2.5.2)
Expand Down
48 changes: 26 additions & 22 deletions app/assets/tailwind/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

.markdown-content {
max-width: 48rem;
margin: 2rem auto;
padding: 0 1rem;
margin: 0;
padding: 0;
line-height: 1.75;
color: #1a202c;
color: #a8a29e;
}

.markdown-content h1 {
Expand All @@ -14,6 +14,7 @@
margin-top: 0;
margin-bottom: 1rem;
line-height: 1.2;
color: #e5e2e1;
}

.markdown-content h2 {
Expand All @@ -22,35 +23,38 @@
margin-top: 2rem;
margin-bottom: 0.75rem;
line-height: 1.3;
border-bottom: 1px solid #e2e8f0;
border-bottom: 1px solid rgba(168, 138, 126, 0.2);
padding-bottom: 0.25rem;
color: #e5e2e1;
}

.markdown-content h3 {
font-size: 1.25rem;
font-weight: 600;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
color: #e5e2e1;
}

.markdown-content h4 {
font-size: 1.125rem;
font-weight: 600;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
color: #e5e2e1;
}

.markdown-content p {
margin-bottom: 1rem;
}

.markdown-content a {
color: #2563eb;
color: #ffb595;
text-decoration: underline;
}

.markdown-content a:hover {
color: #1d4ed8;
color: #ffd4be;
}

.markdown-content ul,
Expand Down Expand Up @@ -78,26 +82,27 @@
}

.markdown-content blockquote {
border-left: 4px solid #e2e8f0;
border-left: 4px solid rgba(238, 103, 28, 0.4);
padding: 0.5rem 1rem;
margin: 1rem 0;
color: #4a5568;
color: #d6d3d1;
background-color: rgba(42, 42, 42, 0.7);
}

.markdown-content code {
background-color: #f1f5f9;
background-color: #0e0e0e;
padding: 0.125rem 0.375rem;
border-radius: 0.25rem;
font-size: 0.875em;
color: #f5f5f4;
}

.markdown-content pre {
background-color: #1e293b;
color: #e2e8f0;
background-color: #0e0e0e;
color: #e7e5e4;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
margin-bottom: 1rem;
border: 1px solid rgba(168, 138, 126, 0.2);
}

.markdown-content pre code {
Expand All @@ -115,40 +120,39 @@
}

.markdown-content th {
background-color: #f8fafc;
border: 1px solid #e2e8f0;
background-color: #2a2a2a;
border: 1px solid rgba(168, 138, 126, 0.25);
padding: 0.5rem 0.75rem;
text-align: left;
font-weight: 600;
color: #e7e5e4;
}

.markdown-content td {
border: 1px solid #e2e8f0;
border: 1px solid rgba(168, 138, 126, 0.2);
padding: 0.5rem 0.75rem;
}

.markdown-content hr {
border: none;
border-top: 1px solid #e2e8f0;
border-top: 1px solid rgba(168, 138, 126, 0.2);
margin: 2rem 0;
}

.markdown-content img {
max-width: 100%;
height: auto;
border-radius: 0.5rem;
}

.markdown-content input[type="checkbox"] {
margin-right: 0.5rem;
}

.markdown-content aside {
background-color: #eff6ff;
border-left: 4px solid #3b82f6;
background-color: rgba(42, 42, 42, 0.7);
border-left: 4px solid rgba(238, 103, 28, 0.5);
padding: 1rem;
margin: 1rem 0;
border-radius: 0 0.25rem 0.25rem 0;
}

.markdown-content sup {
Expand All @@ -158,6 +162,6 @@
.markdown-content .footnotes {
margin-top: 2rem;
padding-top: 1rem;
border-top: 1px solid #e2e8f0;
border-top: 1px solid rgba(168, 138, 126, 0.2);
font-size: 0.875rem;
}
85 changes: 85 additions & 0 deletions app/controllers/admin/airtable_queue_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
class Admin::AirtableQueueController < Admin::ApplicationController
before_action :require_superadmin!

def index
scope = AirtableQueueItem.includes(:project, :enqueued_by, :sent_by).recent
scope = scope.where(status: params[:status_filter]) if params[:status_filter].present?
@pagy, @items = pagy(scope, limit: 50)

render inertia: "Admin/AirtableQueue/Index", props: {
items: @items.map { |i| serialize(i) },
pagy: pagy_props(@pagy),
filters: { status: params[:status_filter].to_s },
counts: {
pending: AirtableQueueItem.pending.count,
sent: AirtableQueueItem.sent.count,
failed: AirtableQueueItem.failed.count,
cancelled: AirtableQueueItem.cancelled.count
},
airtable_enabled: AirtableService.enabled?
}
end

def show
item = AirtableQueueItem.includes(:project, :enqueued_by, :sent_by).find(params[:id])

render inertia: "Admin/AirtableQueue/Show", props: {
item: serialize(item, full: true),
airtable_enabled: AirtableService.enabled?
}
end

def send_to_airtable
item = AirtableQueueItem.find(params[:id])

unless AirtableService.enabled?
redirect_to admin_airtable_queue_path(item), alert: "Airtable is not configured."
return
end

if item.send_to_airtable!(actor: current_user)
audit!("airtable.sent", target: item.project, metadata: { queue_item_id: item.id, airtable_record_id: item.airtable_record_id, table: item.table_name })
redirect_to admin_airtable_queue_path(item), notice: "Sent to Airtable."
else
redirect_to admin_airtable_queue_path(item), alert: "Airtable send failed: #{item.error}"
end
end

def cancel
item = AirtableQueueItem.find(params[:id])

unless item.pending?
redirect_to admin_airtable_queue_path(item), alert: "Only pending items can be cancelled."
return
end

item.update!(status: :cancelled)
audit!("airtable.cancelled", target: item.project, metadata: { queue_item_id: item.id })
redirect_to admin_airtable_queue_index_path, notice: "Item cancelled."
end

private

def require_superadmin!
raise ActionController::RoutingError, "Not Found" unless current_user&.superadmin?
end

def serialize(item, full: false)
data = {
id: item.id,
status: item.status,
table_name: item.table_name,
forge_id: item.forge_id,
project_id: item.project_id,
project_name: item.project&.name,
enqueued_by: item.enqueued_by&.display_name,
sent_by: item.sent_by&.display_name,
sent_at: item.sent_at&.strftime("%b %d, %Y %H:%M"),
airtable_record_id: item.airtable_record_id,
error: item.error,
created_at: item.created_at.strftime("%b %d, %Y %H:%M")
}
data[:payload] = item.payload if full
data
end
end
4 changes: 4 additions & 0 deletions app/controllers/admin/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ def require_staff!
def require_admin!
raise ActionController::RoutingError, "Not Found" unless current_user&.admin?
end

def require_permission!(perm)
raise ActionController::RoutingError, "Not Found" unless current_user&.has_permission?(perm)
end
end
Loading
Loading