import VueSelect from 'vue-select'; import {Octokit} from '@octokit/rest'; import yaml from 'js-yaml' import {hydrateAppWithData} from "./hydration"; export const IssueLabel = { template: ` {{ name }} `, data() { return { labels: window.labels } }, props: { name: { type: String, required: true } }, computed: { /** * Get the name of the class to apply to the label based on the group to * which it belongs. Falls back to miscellaneous if the label does not * belong to a group or a class cannot be identified. * * @returns {string} the name of the class to apply to the label */ className() { return this.$root.categories[this.name] || `${this.name.toLocaleLowerCase()} miscellaneous` } } } export const IssueCard = { template: `

{{ issue.title }}

{{ issue.repo }}#{{ issue.number }}   opened on {{ dateCreated }}.

`, components: { IssueLabel }, props: { issue: { type: Object, required: true }, }, computed: { dateCreated() { const [dateComponent,] = this.issue.created_at.split("T") return dateComponent } } } export const App = { el: '#vue-app', template: `

Loading filters, please wait...

No results.

`, components: { VueSelect, IssueCard }, data() { return { options: { aims: [ {name: 'Contributing code', code: 'contribute'}, {name: 'Triaging issues', code: 'triage'}, {name: 'Labelling issues', code: 'label'} ], skills: [], experiences: [ {name: 'Yes, it is', code: 'beginner'}, {name: 'No, it isn\'t', code: 'experienced'} ] }, filters: { aim: 'contribute', skills: [], experience: 'experienced' }, categories: {}, issues: [], octokit: null } }, computed: { /** * Get a filtered list of issues matching the chosen skill labels. * * @returns {array} the array of filtered issues */ filteredIssues() { return this.issues.filter(issue => { // If aim is to triage issues if (this.filters.aim === 'triage' || this.filters.aim === 'label') { // Show all issues as they all have the label "🚦 status: awaiting triage" return true } // Check experience match if (this.filters.experience === 'beginner' && !issue.labels.includes('good first issue')) { return false } // Check skill set match const joinedLabels = issue.labels.join(',') return !(this.filters.skills.length && !this.filters.skills.some(skill => joinedLabels.includes(skill))); }).sort((a, b) => b.createdAt - a.createdAt) } }, watch: { 'filters.aim' (to, from) { if (to !== from) { this.loadIssues() } } }, methods: { loadIssues () { const q = ['org:creativecommons', 'is:open', 'is:issue'] if (this.filters.aim === 'contribute') { q.push('label:"help wanted"') } else if (this.filters.aim === 'triage') { q.push('label:"🚦 status: awaiting triage"') } else if (this.filters.aim === 'label') { q.push('label:"🏷 status: label work required"') } this.octokit.search.issuesAndPullRequests({ q: q.join(' '), per_page: 100, sort: 'created', order: 'desc' }).then(res => { this.issues = res.data.items this.issues.forEach(issue => { issue.labels = issue.labels.map(label => label.name) const repoUrl = issue.repository_url issue.repo = repoUrl.slice(repoUrl.lastIndexOf('/') + 1) }) }) } }, mounted() { const BASE_URL = 'https://raw.githubusercontent.com/creativecommons/ccos-scripts/main/ccos/norm' const FILE_URL = name => `${BASE_URL}/${name}.yml` this.octokit = new Octokit() this.loadIssues() Promise .all([ fetch(FILE_URL('skills')) .then(response => response.text()), fetch(FILE_URL('labels')) .then(response => response.text()) ]) .then(([skillResponse, labelResponse]) => { skillResponse = yaml.safeLoad(skillResponse) labelResponse = yaml.safeLoad(labelResponse) const [skills, categories] = hydrateAppWithData(skillResponse, labelResponse) this.categories = categories this.options.skills = skills }) .catch(err => console.error(err)) } }