Itinerary new item dropdown cut off when using small screen #257
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
| name: Sync Project Status | |
| on: | |
| issues: | |
| types: [labeled] | |
| jobs: | |
| update-project: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: write | |
| repository-projects: write | |
| steps: | |
| - name: Update project status from label | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PROJECT_AUTOMATION_TOKEN != '' && secrets.PROJECT_AUTOMATION_TOKEN || github.token }} | |
| script: | | |
| const labelMap = { | |
| "backlog": "Backlog", | |
| "needs discussion": "Needs discussion", | |
| "approved": "Approved", | |
| "ready": "Ready", | |
| "in progress": "In progress", | |
| "in review": "In review", | |
| "done": "Done" | |
| }; | |
| const label = context.payload.label.name.toLowerCase(); | |
| const targetOptionName = labelMap[label]; | |
| if (!targetOptionName) return; | |
| const issueNodeId = context.payload.issue.node_id; | |
| const configuredProjectId = "PVT_kwHOBeIeKs4AfmUO"; | |
| const fieldId = "PVTSSF_lAHOBeIeKs4AfmUOzgU5pCI"; | |
| const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | |
| async function fetchIssueProjectItems() { | |
| const result = await github.graphql(` | |
| query($issueId: ID!) { | |
| node(id: $issueId) { | |
| ... on Issue { | |
| projectItems(first: 100) { | |
| nodes { | |
| id | |
| project { | |
| id | |
| title | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| `, { | |
| issueId: issueNodeId | |
| }); | |
| return result.node?.projectItems?.nodes ?? []; | |
| } | |
| // Retry a few times in case project membership is still syncing. | |
| let issueProjectItems = []; | |
| for (let attempt = 1; attempt <= 4; attempt++) { | |
| issueProjectItems = await fetchIssueProjectItems(); | |
| if (issueProjectItems.length > 0) break; | |
| if (attempt < 4) await sleep(3000); | |
| } | |
| let exactMatch = issueProjectItems.find( | |
| (node) => node.project?.id === configuredProjectId | |
| ); | |
| let itemId = exactMatch?.id; | |
| let projectId = exactMatch?.project?.id; | |
| // If issue is not in the configured project yet, try to add it. | |
| if (!itemId) { | |
| try { | |
| const added = await github.graphql(` | |
| mutation($projectId: ID!, $contentId: ID!) { | |
| addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) { | |
| item { | |
| id | |
| } | |
| } | |
| } | |
| `, { | |
| projectId: configuredProjectId, | |
| contentId: issueNodeId | |
| }); | |
| itemId = added.addProjectV2ItemById?.item?.id; | |
| projectId = configuredProjectId; | |
| } catch (error) { | |
| const availableProjects = issueProjectItems | |
| .map((node) => `${node.project?.title ?? "(untitled)"} [${node.project?.id ?? "no-id"}]`) | |
| .join(", "); | |
| console.log(`Issue not in configured project ${configuredProjectId}. Available: ${availableProjects || "none"}`); | |
| console.log(`Failed to auto-add issue to configured project: ${error.message}`); | |
| return; | |
| } | |
| } | |
| if (!itemId || !projectId) { | |
| const availableProjects = issueProjectItems | |
| .map((node) => `${node.project?.title ?? "(untitled)"} [${node.project?.id ?? "no-id"}]`) | |
| .join(", "); | |
| console.log(`Issue not in configured project ${configuredProjectId}. Available: ${availableProjects || "none"}`); | |
| return; | |
| } | |
| const fieldResult = await github.graphql(` | |
| query($fieldId: ID!) { | |
| node(id: $fieldId) { | |
| ... on ProjectV2SingleSelectField { | |
| id | |
| options { | |
| id | |
| name | |
| } | |
| } | |
| } | |
| } | |
| `, { | |
| fieldId: fieldId | |
| }); | |
| const options = fieldResult.node?.options ?? []; | |
| const selectedOption = options.find( | |
| (option) => option.name?.toLowerCase() === targetOptionName.toLowerCase() | |
| ); | |
| if (!selectedOption?.id) { | |
| const availableOptions = options.map((option) => option.name).join(", "); | |
| console.log(`Could not find option '${targetOptionName}' for field ${fieldId}. Available options: ${availableOptions || "none"}`); | |
| return; | |
| } | |
| // update status field | |
| await github.graphql(` | |
| mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { | |
| updateProjectV2ItemFieldValue( | |
| input: { | |
| projectId: $projectId | |
| itemId: $itemId | |
| fieldId: $fieldId | |
| value: { singleSelectOptionId: $optionId } | |
| } | |
| ) { | |
| projectV2Item { | |
| id | |
| } | |
| } | |
| } | |
| `, { | |
| projectId: projectId, | |
| itemId: itemId, | |
| fieldId: fieldId, | |
| optionId: selectedOption.id | |
| }); |