// Final file url reference: "https://browndash.com/files/images/upload_cb31bc0fda59c96ca14193ec494f80cf_o.jpg" /> import { Octokit } from '@octokit/core'; import { Networking } from '../../Network'; import { Issue } from './reportManagerSchema'; import { Upload } from '../../../server/SharedMediaTypes'; // enums and interfaces export enum ViewState { VIEW, CREATE, } export enum Priority { HIGH = 'priority-high', MEDIUM = 'priority-medium', LOW = 'priority-low', } export enum BugType { BUG = 'bug', COSMETIC = 'cosmetic', DOCUMENTATION = 'documentation', ENHANCEMENT = 'enhancement', } export interface FileData { _id: string; file: File; } export interface ReportForm { title: string; description: string; type: BugType; priority: Priority; mediaFiles: FileData[]; } export type ReportFormKey = keyof ReportForm; export const emptyReportForm = { title: '', description: '', type: BugType.BUG, priority: Priority.MEDIUM, mediaFiles: [], }; // interfacing with Github /** * Fetches issues from Github. * @returns array of all issues */ export const getAllIssues = async (octokit: Octokit): Promise => { const res = await octokit.request('GET /repos/{owner}/{repo}/issues', { owner: 'brown-dash', repo: 'Dash-Web', per_page: 80, }); // 200 status means success if (res.status === 200) { return res.data; } throw new Error('Error getting issues'); }; /** * Formats issue title. * * @param title title of issue * @param userEmail email of issue submitter * @returns formatted title */ export const formatTitle = (title: string, userEmail: string): string => `${title} - ${userEmail.replace('@brown.edu', '')}`; // uploading // turns an upload link -> server link // ex: // C: /Users/dash/Documents/GitHub/Dash-Web/src/server/public/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2.png // -> https://browndash.com/files/images/upload_8008dbc4b6424fbff14da7345bb32eb2_l.png export const fileLinktoServerLink = (fileLink: string): string => { const serverUrl = window.location.href.includes('browndash') ? 'https://browndash.com/' : 'http://localhost:1050/'; const regex = 'public'; const publicIndex = fileLink.indexOf(regex) + regex.length; let finalUrl: string = ''; if (fileLink.includes('.png') || fileLink.includes('.jpg') || fileLink.includes('.jpeg') || fileLink.includes('.gif')) { finalUrl = `${serverUrl}${fileLink.substring(publicIndex + 1).replace('.', '_l.')}`; } else { finalUrl = `${serverUrl}${fileLink.substring(publicIndex + 1)}`; } return finalUrl; }; /** * Gets the server file path. * * @param link response from file upload * @returns server file path */ export const getServerPath = (link: Upload.FileResponse): string => { if (link.result instanceof Error) return ''; return link.result.accessPaths.agnostic.server; }; /** * Uploads media files to the server. * @returns the server paths or undefined on error */ export const uploadFilesToServer = async (mediaFiles: FileData[]): Promise => { try { // need to always upload to browndash const links = await Networking.UploadFilesToServer(mediaFiles.map(file => ({ file: file.file }))); return (links ?? []).map(getServerPath).map(fileLinktoServerLink); } catch (result) { if (result instanceof Error) { alert(result.message); } else { alert(result); } } return undefined; }; // helper functions /** * Returns when the issue passes the current filters. * * @param issue issue to check * @returns boolean indicating whether the issue passes the current filters */ export const passesTagFilter = (issue: Issue, priorityFilter: string | null, bugFilter: string | null) => { let passesPriority = true; let passesBug = true; if (priorityFilter) { passesPriority = issue.labels.some(label => { if (typeof label === 'string') { return label === priorityFilter; } return label.name === priorityFilter; }); } if (bugFilter) { passesBug = issue.labels.some(label => { if (typeof label === 'string') { return label === bugFilter; } return label.name === bugFilter; }); } return passesPriority && passesBug; }; // sets and lists export const prioritySet = new Set(Object.values(Priority)); export const bugSet = new Set(Object.values(BugType)); export const priorityDropdownItems = [ { text: 'Low', val: Priority.LOW, }, { text: 'Medium', val: Priority.MEDIUM, }, { text: 'High', val: Priority.HIGH, }, ]; export const bugDropdownItems = [ { text: 'Bug', val: BugType.BUG, }, { text: 'Poor Design or Cosmetic', val: BugType.COSMETIC, }, { text: 'Documentation', val: BugType.DOCUMENTATION, }, { text: 'New feature or request', val: BugType.ENHANCEMENT, }, ]; // colors // [bgColor, color] export const priorityColors: { [key: string]: string[] } = { 'priority-low': ['#d4e0ff', '#000000'], 'priority-medium': ['#6a91f6', '#ffffff'], 'priority-high': ['#003cd5', '#ffffff'], }; // [bgColor, color] export const bugColors: { [key: string]: string[] } = { bug: ['#fe6d6d', '#ffffff'], cosmetic: ['#c650f4', '#ffffff'], documentation: ['#36acf0', '#ffffff'], enhancement: ['#36d4f0', '#ffffff'], }; export const getLabelColors = (label: string): string[] => { if (prioritySet.has(label as Priority)) { return priorityColors[label]; } if (bugSet.has(label as BugType)) { return bugColors[label]; } return ['#0f73f6', '#ffffff']; }; const hexToRgb = (hex: string) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16), } : { r: 0, g: 0, b: 0, }; }; // function that returns whether text should be light on the given bg color export const isDarkMode = (bgHex: string): boolean => { const { r, g, b } = hexToRgb(bgHex); return r * 0.299 + g * 0.587 + b * 0.114 <= 186; }; export const lightColors = { text: '#000000', textGrey: '#5c5c5c', border: '#b8b8b8', }; export const darkColors = { text: '#ffffff', textGrey: '#d6d6d6', border: '#717171', }; export const dashBlue = '#4476f7';