-
Notifications
You must be signed in to change notification settings - Fork 96
feat: implement maintainer dashboard actions for multi-winner milestone bounties #265
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4f220cb
e96087c
55684a6
5e53aa8
b6b6494
01c8fe2
5ef6ef3
601a0c0
c00beee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,10 +9,11 @@ on: | |
| jobs: | ||
| build-and-lint: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 15 | ||
|
|
||
| strategy: | ||
| matrix: | ||
| node-version: [24.x] | ||
| node-version: [22.x] | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
|
|
@@ -55,15 +56,16 @@ jobs: | |
| test-e2e: | ||
| runs-on: ubuntu-latest | ||
| needs: build-and-lint | ||
| timeout-minutes: 15 | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== File: $FILE =="
ls -l "$FILE"
echo
echo "== Relevant uses lines (with numbers) =="
nl -ba "$FILE" | sed -n '40,140p'
echo
echo "== rg for uses: actions/ =="
rg -n "uses:\s*actions/|uses:\s*pnpm/" "$FILE" || trueRepository: boundlessfi/bounties Length of output: 279 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== File: $FILE =="
ls -l "$FILE"
echo
echo "== Relevant uses lines (with numbers) =="
nl -ba "$FILE" | sed -n '40,140p'
echo
echo "== rg for uses: actions/ =="
rg -n "uses:\s*actions/|uses:\s*pnpm/" "$FILE" || trueRepository: boundlessfi/bounties Length of output: 279 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== File: $FILE =="
ls -l "$FILE"
echo
echo "== Relevant uses lines (with numbers) =="
nl -ba "$FILE" | sed -n '40,140p'
echo
echo "== rg for uses: actions/ =="
rg -n "uses:\s*actions/|uses:\s*pnpm/" "$FILE" || trueRepository: boundlessfi/bounties Length of output: 279 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== File: $FILE =="
nl -ba "$FILE" | sed -n '1,200p'Repository: boundlessfi/bounties Length of output: 162 🏁 Script executed: #!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== File: $FILE =="
ls -l "$FILE"
echo
echo "== Line-numbered excerpt (40-140) =="
awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '40,140p'
echo
echo "== rg uses: actions/ or pnpm/ =="
rg -n "uses:\s*actions/|uses:\s*pnpm/" "$FILE" || true
echo
echo "== rg for persist-credentials in checkout steps =="
rg -n "persist-credentials" "$FILE" || trueRepository: boundlessfi/bounties Length of output: 3938 🌐 Web query:
💡 Result: For actions/checkout, the input persist-credentials has default value true, and it controls whether the auth token / SSH key is configured in the local git config. Default value: true [1] Description: “Whether to configure the token or SSH key with the local git config” [1] Additional context from the action README: the auth token is persisted in the local git config so scripts can run authenticated git commands, and the token is removed during post-job cleanup; you can opt out with persist-credentials: false. [2] Citations:
🌐 Web query:
💡 Result: For actions/checkout, the persist-credentials input defaults to true (i.e., the token is persisted locally) unless you explicitly set persist-credentials: false.[1][2] The action documentation states that “The auth token is persisted in the local git config… Set persist-credentials: false to opt-out.”[1] Security discussions in the actions/checkout issue tracker also describe the parameter’s default as true and that you must explicitly pass persist-credentials: false to prevent persistence.[2] Citations:
🌐 Web query:
💡 Result: Yes, the persist-credentials input for the actions/checkout GitHub Action defaults to true [1][2][3]. When this setting is enabled (the default), the action configures the local Git environment with the authentication token (such as the GITHUB_TOKEN) used to fetch the repository [1][2][3]. This allows subsequent steps in the same job to execute authenticated Git commands—such as git push or git pull—without requiring further manual configuration [1][3]. The token is removed from the local Git configuration during the post-job cleanup phase [1][2][3]. Security practitioners often recommend setting persist-credentials to false in workflows that do not require Git write access (e.g., build or test jobs) to adhere to the principle of least privilege [4][5][6]. This prevents the authentication token from being available to other steps in the job, which can reduce the impact if a subsequent step is compromised [4][6]. To disable this behavior, use the following syntax in your workflow file [5]: - uses: actions/checkout@v4 with: persist-credentials: false Citations:
🌐 Web query:
💡 Result: In the actions/checkout@v4 action, the default value for the persist-credentials input is true [1][2][3]. When this setting is enabled (the default), the action configures the repository's local git configuration to include the authentication token or SSH key used for the checkout [1][4]. This allows subsequent steps in your workflow to run authenticated git commands (such as git push) without needing to manually re-configure credentials [1][5]. The token is automatically removed during the post-job cleanup phase [1][2][4]. If you wish to prevent this behavior—for example, to enhance security by ensuring the credentials are not available to subsequent steps in the workflow—you must explicitly set persist-credentials to false in your workflow YAML [1][5]: steps: - uses: actions/checkout@v4 with: persist-credentials: false Citations:
Pin GitHub Actions to commit SHAs and set checkout token persistence explicitly. In Also, 🧰 Tools🪛 zizmor (1.25.2)[warning] 62-63: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false (artipacked) [error] 63-63: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Setup Node.js 24.x | ||
| - name: Setup Node.js 22.x | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 24.x | ||
| node-version: 22.x | ||
|
|
||
| - name: Setup pnpm | ||
| uses: pnpm/action-setup@v4 | ||
|
|
@@ -92,7 +94,7 @@ jobs: | |
| NEXT_PUBLIC_NATIVE_TOKEN_CONTRACT: ${{ secrets.NEXT_PUBLIC_NATIVE_TOKEN_CONTRACT || 'CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC' }} | ||
|
|
||
| - name: Install Playwright Chromium | ||
| run: pnpm exec playwright install chromium --with-deps | ||
| run: pnpm exec playwright install chromium | ||
|
|
||
| - name: Run E2E tests | ||
| run: pnpm run test:e2e | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,22 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TooltipProvider, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TooltipTrigger, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@/components/ui/tooltip"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Dialog, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DialogContent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DialogDescription, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DialogFooter, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DialogHeader, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DialogTitle, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@/components/ui/dialog"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Textarea } from "@/components/ui/textarea"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { toast } from "sonner"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useReleasePayment, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useAdvanceContributor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useRemoveContributor, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useSendMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@/hooks/use-bounty-application"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ChevronRight, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UserMinus, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,25 +39,86 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "lucide-react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface Model4MaintainerDashboardProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bountyId: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| milestones: Milestone[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributors: ContributorProgress[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxSlots?: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function Model4MaintainerDashboard({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bountyId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| milestones, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributors: initialContributors, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxSlots = 5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: Model4MaintainerDashboardProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [loadingAction, setLoadingAction] = React.useState<string | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const releasePayment = useReleasePayment(bountyId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const advanceContributor = useAdvanceContributor(bountyId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const removeContributor = useRemoveContributor(bountyId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sendMessage = useSendMessage(bountyId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [selectedContributor, setSelectedContributor] = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| React.useState<ContributorProgress | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isSubmissionsOpen, setIsSubmissionsOpen] = React.useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isMessageOpen, setIsMessageOpen] = React.useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [messageText, setMessageText] = React.useState(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleReleasePayment = (contributor: ContributorProgress) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| releasePayment.mutate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributorId: contributor.userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| milestoneId: contributor.currentMilestoneId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.success(`Payment released for ${contributor.userName}`), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+67
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing The Add onError callbacks to show error toasts const handleReleasePayment = (contributor: ContributorProgress) => {
releasePayment.mutate(
{
contributorId: contributor.userId,
milestoneId: contributor.currentMilestoneId,
},
{
onSuccess: () =>
toast.success(`Payment released for ${contributor.userName}`),
+ onError: () =>
+ toast.error(`Failed to release payment for ${contributor.userName}`),
},
);
};
const handleAdvance = (contributor: ContributorProgress) => {
advanceContributor.mutate(
{ contributorId: contributor.userId },
{
onSuccess: () =>
toast.success(`${contributor.userName} advanced to next milestone`),
+ onError: () =>
+ toast.error(`Failed to advance ${contributor.userName}`),
},
);
};
const handleRemove = (contributor: ContributorProgress) => {
removeContributor.mutate(
{ contributorId: contributor.userId },
{
onSuccess: () =>
toast.success(`${contributor.userName} removed from bounty`),
+ onError: () =>
+ toast.error(`Failed to remove ${contributor.userName}`),
},
);
};Also applies to: 73-81, 83-91 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleAdvance = (contributor: ContributorProgress) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| advanceContributor.mutate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { contributorId: contributor.userId }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.success(`${contributor.userName} advanced to next milestone`), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleRemove = (contributor: ContributorProgress) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeContributor.mutate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { contributorId: contributor.userId }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: () => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.success(`${contributor.userName} removed from bounty`), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleOpenSubmissions = (contributor: ContributorProgress) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedContributor(contributor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsSubmissionsOpen(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleOpenMessage = (contributor: ContributorProgress) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setSelectedContributor(contributor); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setMessageText(""); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsMessageOpen(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleAction = async (action: string, userName: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLoadingAction(`${action}-${userName}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`[Coming soon] ${action} for ${userName}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await new Promise((r) => setTimeout(r, 1000)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setLoadingAction(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleSendMessage = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!selectedContributor || !messageText.trim()) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sendMessage.mutate( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { contributorId: selectedContributor.userId, message: messageText }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccess: () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toast.success(`Message sent to ${selectedContributor.userName}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsMessageOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+111
to
122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing Similar to other handlers, Add onError callback const handleSendMessage = () => {
if (!selectedContributor || !messageText.trim()) return;
sendMessage.mutate(
{ contributorId: selectedContributor.userId, message: messageText },
{
onSuccess: () => {
toast.success(`Message sent to ${selectedContributor.userName}`);
setIsMessageOpen(false);
},
+ onError: () => {
+ toast.error(`Failed to send message to ${selectedContributor.userName}`);
+ },
},
);
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -135,18 +212,12 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant="ghost" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="icon-sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="text-gray-400 hover:text-white" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| aria-label="Send message" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAction("Message", contributor.userName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={loadingAction !== null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => handleOpenMessage(contributor)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <MessageSquare className="size-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Send Message [Coming soon] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent>Send Message</TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Tooltip> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Tooltip> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -155,15 +226,9 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant="outline" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-8 text-xs border-gray-700 hover:bg-gray-800" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAction( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "View Submissions", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={loadingAction !== null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => handleOpenSubmissions(contributor)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| View Submissions [Coming soon] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| View Submissions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent>Review work</TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -174,21 +239,21 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-8 text-xs bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 border border-emerald-500/20 font-bold" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAction( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Release Payment", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => handleReleasePayment(contributor)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| releasePayment.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| releasePayment.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={loadingAction !== null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {loadingAction === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Release Payment-${contributor.userName}` ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {releasePayment.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| releasePayment.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Loader2 className="size-3 mr-1.5 animate-spin" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Coins className="size-3 mr-1.5" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Release Payment [Coming soon] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Release Payment | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent>Pay for milestone</TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -200,18 +265,20 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant="secondary" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="h-8 text-xs font-bold" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAction("Advance", contributor.userName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => handleAdvance(contributor)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| advanceContributor.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| advanceContributor.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={loadingAction !== null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {loadingAction === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Advance-${contributor.userName}` ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {advanceContributor.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| advanceContributor.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Loader2 className="size-3 mr-1.5 animate-spin" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Advance [Coming soon]{" "} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <ArrowRight className="size-3 ml-1.5" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Advance <ArrowRight className="size-3 ml-1.5" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+275
to
283
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Advance button label missing during loading state. When Keep label visible during loading >
- {advanceContributor.isPending &&
- advanceContributor.variables?.contributorId ===
- contributor.userId ? (
+ {advanceContributor.isPending &&
+ advanceContributor.variables?.contributorId ===
+ contributor.userId && (
<Loader2 className="size-3 mr-1.5 animate-spin" />
- ) : (
- <>
- Advance <ArrowRight className="size-3 ml-1.5" />
- </>
)}
+ Advance <ArrowRight className="size-3 ml-1.5" />
</Button>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -225,18 +292,23 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| variant="ghost" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| size="icon-sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="text-red-400/50 hover:text-red-400 hover:bg-red-400/10" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| aria-label="Remove from slot" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleAction("Remove", contributor.userName) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={() => handleRemove(contributor)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeContributor.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeContributor.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={loadingAction !== null} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <UserMinus className="size-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {removeContributor.isPending && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeContributor.variables?.contributorId === | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contributor.userId ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Loader2 className="size-4 animate-spin" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <UserMinus className="size-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipTrigger> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Remove from slot [Coming soon] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <TooltipContent>Remove from slot</TooltipContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Tooltip> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipProvider> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -273,6 +345,60 @@ export function Model4MaintainerDashboard({ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Tooltip> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </TooltipProvider> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Modals */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dialog open={isSubmissionsOpen} onOpenChange={setIsSubmissionsOpen}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Submissions for {selectedContributor?.userName} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Review the submitted work from this contributor. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="py-6 text-center text-gray-400"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| No submissions found for this contributor. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button onClick={() => setIsSubmissionsOpen(false)}>Close</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Dialog> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Dialog open={isMessageOpen} onOpenChange={setIsMessageOpen}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogTitle>Message {selectedContributor?.userName}</DialogTitle> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Send a message directly to this contributor regarding their | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| application. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogDescription> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="py-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Textarea | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| placeholder="Type your message here..." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value={messageText} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onChange={(e) => setMessageText(e.target.value)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| className="min-h-[100px]" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button variant="outline" onClick={() => setIsMessageOpen(false)}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Cancel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClick={handleSendMessage} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disabled={sendMessage.isPending || !messageText.trim()} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {sendMessage.isPending && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Loader2 className="size-3 mr-1.5 animate-spin" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Send Message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogFooter> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </DialogContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Dialog> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </CardContent> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </Card> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: boundlessfi/bounties
Length of output: 108
🏁 Script executed:
Repository: boundlessfi/bounties
Length of output: 1026
🏁 Script executed:
Repository: boundlessfi/bounties
Length of output: 1286
Disable persisted checkout credentials in CI (
persist-credentials: false).Both
actions/checkout@v4steps (lines 20 and 63) lackpersist-credentials: false, which leaves the default token configured for later steps.🛡️ Suggested fix
- name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false📝 Committable suggestion
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 62-63: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 63-63: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents