From b632928f4f75c7f01f8a3b8f8df8a61754164746 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Wed, 3 Jul 2024 20:51:38 -0400 Subject: [PATCH 01/19] @purejerome says to put this in eslint --- .eslintrc.json | 87 ++++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 6f824cb..1afb772 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,44 +1,53 @@ { - "env": { - "browser": true, - "es2021": true + "env": { + "browser": true, + "es2021": true + }, + "extends": [ + "plugin:react/recommended", + "airbnb", + "prettier" + ], + "parserOptions": { + "ecmaFeatures": { + "jsx": true }, - "extends": [ - "plugin:react/recommended", - "airbnb", - "prettier" + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": [ + "react" + ], + "rules": { + "react/jsx-filename-extension": [ + 1, + { + "extensions": [ + ".js", + ".jsx" + ] + } ], - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": [ - "react" + "no-console": "off", + "no-unused-vars": "off", + "react/react-in-jsx-scope": "off", + "indent": [ + "warn", + 2 ], - "rules": { - "react/jsx-filename-extension": [ - 1, - { - "extensions": [ - ".js", - ".jsx" - ] - } - ], - "no-console": "off", - "no-unused-vars": "off", - "react/react-in-jsx-scope": "off", - "indent": [ - "warn", - 2 - ], - "react/prop-types": "off", - "default-param-last": "off", - "default-case": "off", - "no-case-declarations": "off", - "jsx-a11y/anchor-is-valid": "off" - } + "react/prop-types": "off", + "default-param-last": "off", + "default-case": "off", + "no-case-declarations": "off", + "jsx-a11y/anchor-is-valid": "off", + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "tabWidth": 2, + "semi": true, + "endOfLine": "lf" + } + ] + } } \ No newline at end of file From 550f369f352fd919a7970ba7fd45cfe7efd10c44 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Wed, 3 Jul 2024 20:53:44 -0400 Subject: [PATCH 02/19] let's configure the project to use the eslint for the formatter and to format all the time. thanks @purejerome --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c3fce85..22450ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "editor.defaultFormatter": "esbenp.prettier-vscode", + "[javascript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, + "[javascriptreact]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, + "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.formatOnType": true From e766dbcaeb31b6aa692eaf457f95828fb5b06680 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Thu, 4 Jul 2024 21:18:46 -0400 Subject: [PATCH 03/19] update prettier version, require eslint plugin --- .eslintrc.json | 3 +- package-lock.json | 102 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 3 +- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 1afb772..231b8e9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,8 @@ "sourceType": "module" }, "plugins": [ - "react" + "react", + "eslint-plugin-prettier" ], "rules": { "react/jsx-filename-extension": [ diff --git a/package-lock.json b/package-lock.json index 1f5c4aa..58873a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "CPR-Music", + "name": "CPR-Music-hcientist", "lockfileVersion": 3, "requires": true, "packages": { @@ -31,9 +31,10 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", - "prettier": "2.5.1" + "prettier": "^3.0.0" }, "engines": { "node": "^22.2.0" @@ -431,6 +432,19 @@ "node": ">=14" } }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1857,6 +1871,37 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.34.1", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", @@ -2023,6 +2068,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -3658,15 +3710,32 @@ } }, "node_modules/prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", "dev": true, + "license": "MIT", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/pretty-format": { @@ -4426,6 +4495,23 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -4900,4 +4986,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 376aa77..445c50d 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,9 @@ "eslint-config-prettier": "^8.3.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", - "prettier": "2.5.1" + "prettier": "^3.0.0" } } From aa430481b7b876940f9011f683f07c895cbed823 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Thu, 4 Jul 2024 21:24:38 -0400 Subject: [PATCH 04/19] test standardizing format on a single file: api.js tested with npx eslint --config .eslintrc.json api.js seems like I only needed to do npx eslint api.js --- api.js | 92 +++++++++++++++++---------------- components/teacher/grade/rte.js | 2 +- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/api.js b/api.js index 765969a..6f893e6 100644 --- a/api.js +++ b/api.js @@ -11,11 +11,16 @@ function assertResponse(response) { async function getDjangoToken() { const session = await getSession(); - if (!session || !session.djangoToken) return; - return session.djangoToken + if (!session || !session.djangoToken) return null; + return session.djangoToken; } -async function makeRequest(endpoint, method="GET", body=null, headers={}) { +async function makeRequest( + endpoint, + method = 'GET', + body = null, + headers = {}, +) { const token = await getDjangoToken(); if (!token) return {}; @@ -23,16 +28,16 @@ async function makeRequest(endpoint, method="GET", body=null, headers={}) { ...headers, Authorization: `Token ${token}`, 'Content-Type': 'application/json', - } + }; const API = `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api`; - const url = `${API}/${endpoint}` + const url = `${API}/${endpoint}`; const response = await fetch(url, { method, headers: requestHeaders, - body: body ? JSON.stringify(body) : null - }) + body: body ? JSON.stringify(body) : null, + }); assertResponse(response); @@ -41,7 +46,7 @@ async function makeRequest(endpoint, method="GET", body=null, headers={}) { } export async function getEnrollments() { - const endpoint = "enrollments/"; + const endpoint = 'enrollments/'; const json = await makeRequest(endpoint); return json; } @@ -50,32 +55,32 @@ export function getStudentAssignments(slug) { return async () => { const endpoint = `courses/${slug}/assignments/`; const json = await makeRequest(endpoint); - return json - } + return json; + }; } export function getAllPieces(slug) { return async () => { const endpoint = `courses/${slug}/piece-plans/`; const json = await makeRequest(endpoint); - return json.map((r) => ({...r.piece, piece_plan_id: r.id})); - } + return json.map((r) => ({ ...r.piece, piece_plan_id: r.id })); + }; } export function mutateAssignPiece(slug) { return async (piecePlanId) => { - const endpoint = `courses/${slug}/assign_piece_plan/` - const json = await makeRequest(endpoint, 'POST', {piece_id: piecePlanId}) - return json - } + const endpoint = `courses/${slug}/assign_piece_plan/`; + const json = await makeRequest(endpoint, 'POST', { piece_id: piecePlanId }); + return json; + }; } export function mutateUnassignPiece(slug) { return async (piece) => { - const endpoint = `courses/${slug}/unassign/` - const json = await makeRequest(endpoint, 'POST', {piece_id: piece.id}); - return json - } + const endpoint = `courses/${slug}/unassign/`; + const json = await makeRequest(endpoint, 'POST', { piece_id: piece.id }); + return json; + }; } export function getRecentSubmissions({ slug, piece, partType }) { @@ -83,40 +88,40 @@ export function getRecentSubmissions({ slug, piece, partType }) { const endpoint = `courses/${slug}/submissions/recent/?piece_slug=${piece}&activity_name=${partType}`; const json = await makeRequest(endpoint); return json; - } + }; } export function mutateGradeSubmission(slug) { - return async ({ student_submission, rhythm, tone, expression, grader }) => { + return async ({ studentSubmission, rhythm, tone, expression, grader }) => { const endpoint = `courses/${slug}/grades/`; const body = { - student_submission: [student_submission], + student_submission: [studentSubmission], own_submission: [], rhythm, tone, expression, - grader + grader, }; - + const json = await makeRequest(endpoint, 'POST', body); - + return json; - } + }; } // should i make this mutator optionally have a recording or?? export function mutateCreateSubmission({ slug }) { return async (submission, assignmentId) => { - const endpoint = `courses/${slug}/assignments/${assignmentId}/submissions/` - - const json = await makeRequest(endpoint, 'POST', submission) - + const endpoint = `courses/${slug}/assignments/${assignmentId}/submissions/`; + + const json = await makeRequest(endpoint, 'POST', submission); + return json; - } + }; } export async function getMySubmissionsForAssignment({ slug, assignmentId }) { - const endpoint = `courses/${slug}/assignments/${assignmentId}/submissions/` + const endpoint = `courses/${slug}/assignments/${assignmentId}/submissions/`; const json = await makeRequest(endpoint); return json; } @@ -124,28 +129,28 @@ export async function getMySubmissionsForAssignment({ slug, assignmentId }) { export function mutateCourse(slug) { // expecting params to be any subset of name, start_date, end_date, slug return async (params) => { - const endpoint = `courses/${slug}/` - + const endpoint = `courses/${slug}/`; + const json = await makeRequest(endpoint, 'PATCH', params); - + return json; - } + }; } - + export async function mutateAssignmentInstrument(slug, pieceId, instrument) { const endpoint = `courses/${slug}/change_piece_instrument/`; - const body = {piece_id: pieceId, instrument_id: instrument.id}; - + const body = { piece_id: pieceId, instrument_id: instrument.id }; + const json = await makeRequest(endpoint, 'PATCH', body); - + return json; } export function getAssignedPieces(assignments) { return () => { const pieces = {}; - if (Object.values(assignments).length == 0) return pieces; - + if (Object.values(assignments).length === 0) return pieces; + for (const pieceKey of Object.keys(assignments)) { for (const pieceAssignment of assignments[pieceKey]) { if (!(pieceKey in pieces)) { @@ -167,4 +172,3 @@ export function getAssignedPieces(assignments) { return pieces; }; } - diff --git a/components/teacher/grade/rte.js b/components/teacher/grade/rte.js index 6d25288..b066081 100644 --- a/components/teacher/grade/rte.js +++ b/components/teacher/grade/rte.js @@ -67,7 +67,7 @@ export default function RTE({ submission, submitAction, autoFocus = false }) { }); const grade = ({ sub, r, t, e, grader }) => gradeMutation.mutate({ - student_submission: sub, + studentSubmission: sub, rhythm: r, tone: t, expression: e, From 5b49450fc6046930d3df88dbb8bf23f62881f784 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Thu, 4 Jul 2024 21:34:26 -0400 Subject: [PATCH 05/19] eslint and prettier disagree about indentation, let prettier win bc who cares --- .eslintrc.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 231b8e9..9046e75 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -33,8 +33,8 @@ "no-unused-vars": "off", "react/react-in-jsx-scope": "off", "indent": [ - "warn", - 2 + "off", + 0 ], "react/prop-types": "off", "default-param-last": "off", From 7c6c3078cbf9f34ba3a648642f6dab15de8bcd84 Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Thu, 4 Jul 2024 21:39:34 -0400 Subject: [PATCH 06/19] with these changes, top-level .js files all pass eslint with no warnings or errors --- actions.js | 247 +++++++++++++++++++++++++------------------------- pages/_app.js | 2 +- reducers.js | 32 +++---- store.js | 11 +-- 4 files changed, 142 insertions(+), 150 deletions(-) diff --git a/actions.js b/actions.js index cebd57b..b709408 100644 --- a/actions.js +++ b/actions.js @@ -2,7 +2,6 @@ import { signOut } from 'next-auth/react'; import * as types from './types'; - // https://allover.twodee.org/remote-state/fetching-memories/ function assertResponse(response) { if (response.status >= 200 && response.status < 300) { @@ -39,73 +38,69 @@ export function retrieveEnrollments(djangoToken) { export function fetchEnrollments() { return (dispatch, getState) => { - const { currentUser: {token} } = getState(); + const { + currentUser: { token }, + } = getState(); return token ? retrieveEnrollments(token) - .then((courses) => dispatch(gotEnrollments(courses))) - .catch((...rest) => { - console.log('catch rest'); - console.log(rest); - }) + .then((courses) => dispatch(gotEnrollments(courses))) + .catch((...rest) => { + console.log('catch rest'); + console.log(rest); + }) : null; }; } export const newCourse = - ({ - name, - startDate: start_date, - endDate: end_date, - slug = 'slug', - userId, - }) => - async(dispatch, getState) => { - const { - currentUser: { token } - } = getState(); - const params = { - name, - start_date, - end_date, - slug, - owner: userId, - }; - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Token ${token}`, - }, - body: JSON.stringify(params), - }; + ({ name, startDate: start_date, endDate: end_date, slug = 'slug', userId }) => + async (dispatch, getState) => { + const { + currentUser: { token }, + } = getState(); + const params = { + name, + start_date, + end_date, + slug, + owner: userId, + }; + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Token ${token}`, + }, + body: JSON.stringify(params), + }; - const enrollOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Token ${token}`, - }, - }; - var newSlug; - await fetch(`${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/`, options) - .then(assertResponse) - .then((response) => response.json()) - .then((data) => { - const enrollParams = { - user: userId, - role: 1, - course: data.id, - }; - newSlug = data.slug; - enrollOptions.body = JSON.stringify(enrollParams); - return fetch( - `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/enrollments/`, - enrollOptions - ); - }) - .then(() => dispatch(fetchEnrollments())); - return newSlug; + const enrollOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Token ${token}`, + }, }; + let newSlug; + await fetch(`${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/`, options) + .then(assertResponse) + .then((response) => response.json()) + .then((data) => { + const enrollParams = { + user: userId, + role: 1, + course: data.id, + }; + newSlug = data.slug; + enrollOptions.body = JSON.stringify(enrollParams); + return fetch( + `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/enrollments/`, + enrollOptions, + ); + }) + .then(() => dispatch(fetchEnrollments())); + return newSlug; + }; export function addedFromRoster(courseSlug, enrollments) { return { @@ -129,17 +124,17 @@ export function fetchRoster({ courseSlug }) { Authorization: `Token ${token}`, 'Content-Type': 'application/json', }, - } + }, ) .then((response) => response.json()) - .then((enrollments) => dispatch(gotRoster({enrollments, courseSlug}))); + .then((enrollments) => dispatch(gotRoster({ enrollments, courseSlug }))); }; } export function uploadRoster({ body, courseSlug }) { return (dispatch, getState) => { const { - currentUser: { token } + currentUser: { token }, } = getState(); fetch( `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/${courseSlug}/roster/`, @@ -149,17 +144,15 @@ export function uploadRoster({ body, courseSlug }) { }, method: 'POST', body, - } + }, ) .then(assertResponse) .then((response) => response.json()) .then((res) => { dispatch(addedFromRoster(courseSlug, res)); }) - .then(() => - dispatch(fetchRoster({ djangoToken: token, courseSlug })) - ); - } + .then(() => dispatch(fetchRoster({ djangoToken: token, courseSlug }))); + }; } export function gotInstruments(instruments) { @@ -182,16 +175,16 @@ export function fetchInstruments() { }) .then(assertResponse) .then((response) => response.json()) - .then((instruments) => + .then((instruments) => dispatch( - gotInstruments(instruments.sort((a, b) => (a.name < b.name ? -1 : 1))) - ) + gotInstruments( + instruments.sort((a, b) => (a.name < b.name ? -1 : 1)), + ), + ), ); - } + }; } - - export function enrollmentUpdated({ enrollment, instrument }) { return { type: types.Action.UpdatedEnrollmentInstrument, @@ -209,10 +202,7 @@ export function setInstrumentActivity(enrollmentId, activityState) { }; } -export function updateEnrollmentInstrument({ - enrollmentId, - instrument, -}) { +export function updateEnrollmentInstrument({ enrollmentId, instrument }) { return (dispatch, getState) => { const { currentUser: { token }, @@ -227,19 +217,19 @@ export function updateEnrollmentInstrument({ }, method: 'PATCH', body: JSON.stringify({ instrument: instrument.id }), - } + }, ) .then(assertResponse) .then((res) => res.json()) .then((enrollment) => { dispatch( - setInstrumentActivity(enrollmentId, types.ActivityState.Success) + setInstrumentActivity(enrollmentId, types.ActivityState.Success), ); dispatch(enrollmentUpdated({ enrollment, instrument })); }) .catch(() => { dispatch( - setInstrumentActivity(enrollmentId, types.ActivityState.Erroneous) + setInstrumentActivity(enrollmentId, types.ActivityState.Erroneous), ); }); }; @@ -264,11 +254,11 @@ export function fetchStudentAssignments({ slug }) { Authorization: `Token ${token}`, 'Content-Type': 'application/json', }, - } + }, ) .then((response) => response.json()) .then((assignments) => dispatch(gotAssignments(assignments))); - } + }; } export function loggedOut() { @@ -292,7 +282,7 @@ export function logoutUser() { .then(assertResponse) .then((res) => res.json()) .then(loggedOut); - } + }; } export function gotActivities({ activities, slug }) { @@ -310,22 +300,25 @@ export function fetchActivities({ slug }) { const { currentUser: { token }, } = getState(); - return token && fetch( - `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/${slug}/assignments/`, - { - headers: { - Authorization: `Token ${token}`, - 'Content-Type': 'application/json', + return ( + token && + fetch( + `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/${slug}/assignments/`, + { + headers: { + Authorization: `Token ${token}`, + 'Content-Type': 'application/json', + }, }, - } - ) - .then(assertResponse) - .then((response) => response.json()) - .then((activities) => dispatch(gotActivities({ activities, slug }))) - .catch((e) => { - console.error('caught', e) - }); - } + ) + .then(assertResponse) + .then((response) => response.json()) + .then((activities) => dispatch(gotActivities({ activities, slug }))) + .catch((e) => { + console.error('caught', e); + }) + ); + }; } export function gotPieces(pieces) { @@ -351,7 +344,7 @@ export function fetchPieces() { .then((pieces) => { dispatch(gotPieces(pieces.sort((a, b) => (a.name < b.name ? -1 : 1)))); }); - } + }; } export function assignedPiece({ piece, slug }) { @@ -385,12 +378,12 @@ export function assignPiece({ slug, piece }) { method: 'POST', body: JSON.stringify({ piece_id: piece.id }), // body: data, - } + }, ) .then(assertResponse) .then((response) => response.json()) .then((pieceResponse) => dispatch(assignedPiece({ piece, slug }))); - } + }; } export function setPieceChangeState({ piece, state }) { @@ -409,7 +402,7 @@ export function unassignPiece({ piece, slug }) { setPieceChangeState({ pieceId: piece.id, state: types.ActivityState.Active, - }) + }), ); // const data = new FormData(); // data.append("piece_id", piece); @@ -423,7 +416,7 @@ export function unassignPiece({ piece, slug }) { method: 'POST', body: JSON.stringify({ piece_id: piece.id }), // body: data, - } + }, ) .then(assertResponse) .then(() => dispatch(unassignedPiece({ piece, slug }))) @@ -432,15 +425,15 @@ export function unassignPiece({ piece, slug }) { setPieceChangeState({ pieceId: piece.id, state: types.ActivityState.Success, - }) - ) + }), + ), ) .catch((err) => { dispatch( setPieceChangeState({ pieceId: piece.id, state: types.ActivityState.Erroneous, - }) + }), ); }); }; @@ -482,7 +475,7 @@ export function getUserProfile() { signOut({ callbackUrl: '/' }); } }); - } + }; } export function selectEnrollment(enrollment) { @@ -502,7 +495,7 @@ export function selectAssignment(assignment) { export function beginUpload(id) { return { type: types.Action.BeginUpload, - payload: {id} + payload: { id }, }; } @@ -526,14 +519,13 @@ export function uploadFailed(id) { }; } - export function postRecording({ slug, assignmentId, audio, composition, submissionId, - index=0 + index = 0, }) { return (dispatch, getState) => { const { @@ -542,13 +534,13 @@ export function postRecording({ dispatch(beginUpload(submissionId)); // let body = '' - let bodyObj = {"content":"N/A for Perform submissions"}; + let bodyObj = { content: 'N/A for Perform submissions' }; if (composition) { bodyObj = { content: composition }; } - + bodyObj.index = index; - let body = JSON.stringify(bodyObj); + const body = JSON.stringify(bodyObj); return fetch( `${process.env.NEXT_PUBLIC_BACKEND_HOST}/api/courses/${slug}/assignments/${assignmentId}/submissions/`, { @@ -558,7 +550,7 @@ export function postRecording({ }, method: 'POST', body, - } + }, ) .then(assertResponse) .then((res) => res.json()) @@ -571,12 +563,11 @@ export function postRecording({ }, method: 'POST', body: audio, - } + }, ) .then(assertResponse) .then((response) => response.json()) - .then((res) => { - }); + .then((res) => {}); }) .then(() => { // success case @@ -601,8 +592,13 @@ export function postRespond({ slug, assignmentId, response }) { currentUser: { token }, } = getState(); if (!slug || !assignmentId || !response) { - console.error('missing requirements to submit', slug, assignmentId, response) - return; + console.error( + 'missing requirements to submit', + slug, + assignmentId, + response, + ); + return null; } dispatch(beginUpload(assignmentId)); const body = JSON.stringify({ content: JSON.stringify(response) }); @@ -615,7 +611,7 @@ export function postRespond({ slug, assignmentId, response }) { }, method: 'POST', body, - } + }, ) .then(assertResponse) .then((res) => res.json()) @@ -652,7 +648,7 @@ export function postConnect({ slug, assignmentId, response }) { }, method: 'POST', body, - } + }, ) .then(assertResponse) .then((res) => res.json()) @@ -673,7 +669,6 @@ export function postConnect({ slug, assignmentId, response }) { }; } - export function gotSingleStudentAssignment(assignment) { return { type: types.Action.GotSingleAssignment, @@ -684,7 +679,7 @@ export function gotSingleStudentAssignment(assignment) { export function fetchSingleStudentAssignment({ slug, assignmentId }) { if (!assignmentId) { console.error('assignmentId is required'); - return; + return null; } return (dispatch, getState) => { const { @@ -697,16 +692,16 @@ export function fetchSingleStudentAssignment({ slug, assignmentId }) { Authorization: `Token ${token}`, 'Content-Type': 'application/json', }, - } + }, ) .then(assertResponse) .then((response) => response.json()) .then((assignment) => dispatch(selectAssignment(assignment))); - } + }; } export function didInstrument() { return { type: types.Action.DidInstrument, }; -} \ No newline at end of file +} diff --git a/pages/_app.js b/pages/_app.js index 88a2dd9..278cc06 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -6,7 +6,7 @@ import '../styles/bootstrap.min-musiccpr.css'; import '../styles/global.css'; import { ReactQueryDevtools } from 'react-query/devtools'; import { QueryClient, QueryClientProvider } from 'react-query'; -import { wrapper } from '../store'; +import wrapper from '../store'; export function App({ Component, pageProps: { session, ...pageProps } }) { const queryClient = new QueryClient({ diff --git a/reducers.js b/reducers.js index ba58a98..b5844aa 100644 --- a/reducers.js +++ b/reducers.js @@ -30,21 +30,19 @@ const initialAssignedPieces = { // assignedPieces const assignedPiecesReducer = ( state = initialAssignedPieces, - { type, payload } + { type, payload }, ) => { switch (type) { case types.Action.GotActivities: // we have to make some changes here. the response is now already grouped. // the response will be an object with each key being a piece slug whose // value is an array of activities - const pieceSlugsKeys = Object.keys(payload.activities) - let pieces = pieceSlugsKeys.map((k) => { - return { - id: payload.activities[k][0].piece_id, - slug: k, - name: payload.activities[k][0].piece_name, - } - }) + const pieceSlugsKeys = Object.keys(payload.activities); + let pieces = pieceSlugsKeys.map((k) => ({ + id: payload.activities[k][0].piece_id, + slug: k, + name: payload.activities[k][0].piece_name, + })); // payload.activities.map( // (assignment) => ({ // id: assignment.piece_id, @@ -54,9 +52,9 @@ const assignedPiecesReducer = ( pieces.sort((a, b) => (a.id < b.id ? -1 : 1)); // FIXME how should the pieces be sorted? this assumes by the piece's id, but perhaps it should be by the order property of the piece_plan if it's available? // what does this next step do?? - // it looks like it says to only keep the first piece with a given id // thanks copilot for finishing my sentence + // it looks like it says to only keep the first piece with a given id // thanks copilot for finishing my sentence pieces = pieces.filter((piece, i, arr) => - i === 0 ? true : piece.id !== arr[i - 1].id + i === 0 ? true : piece.id !== arr[i - 1].id, ); // FIXME again, as above, how do we want pieces sorted? @@ -81,7 +79,7 @@ const assignedPiecesReducer = ( ...state.items, [payload.slug]: [ ...state.items[payload.slug].filter( - (pieceObj) => pieceObj.id !== payload.piece.id + (pieceObj) => pieceObj.id !== payload.piece.id, ), ], }, @@ -98,7 +96,7 @@ const initialActivities = { const activitiesReducer = (state = initialActivities, { type, payload }) => { switch (type) { case types.Action.GotActivities: - console.log('payload', payload) + console.log('payload', payload); return { loaded: true, items: payload.activities }; } return state; @@ -216,7 +214,7 @@ const selectedEnrollmentReducer = (state = {}, { type, payload }) => { const selectedAssignmentReducer = ( state = { uploadStatus: types.UploadStatusEnum.Inactive }, - { type, payload } + { type, payload }, ) => { switch (type) { case types.Action.SelectedAssignment: @@ -233,7 +231,7 @@ const selectedAssignmentReducer = ( const submitStatusReducer = ( state = { submissions: {} }, - { type, payload } + { type, payload }, ) => { switch (type) { case types.Action.BeginUpload: @@ -267,8 +265,8 @@ const submitStatusReducer = ( // ...Object.keys(state.submissions).filter((elem) => elem !== payload.id).reduce( (res, key) => (res[key] = state.submissions[key], res), {} ) ...Object.fromEntries( Object.entries(state.submissions).filter( - (elem) => elem !== payload.id - ) + (elem) => elem !== payload.id, + ), ), }, }; diff --git a/store.js b/store.js index fe21eff..3efea63 100644 --- a/store.js +++ b/store.js @@ -2,14 +2,13 @@ import { useMemo } from 'react'; import { createStore, applyMiddleware } from 'redux'; // import { composeWithDevTools } from 'redux-devtools-extension' import thunkMiddleware from 'redux-thunk'; -import reducers from './reducers'; - import { createWrapper } from 'next-redux-wrapper'; +import reducers from './reducers'; // create a makeStore function -const makeStore = (context) => { - return createStore(reducers, applyMiddleware(thunkMiddleware)); -}; +const makeStore = (context) => + createStore(reducers, applyMiddleware(thunkMiddleware)); // export an assembled wrapper -export const wrapper = createWrapper(makeStore, { debug: true }); +const wrapper = createWrapper(makeStore, { debug: true }); +export default wrapper; From cad351e194da233fdc04598bcdf65e5bd56cb0fc Mon Sep 17 00:00:00 2001 From: Michael Stewart Date: Thu, 4 Jul 2024 21:50:04 -0400 Subject: [PATCH 07/19] with these changes, top-level pages pass linter --- pages/_app.js | 8 +- pages/about.js | 215 +++++++++++++++++++++++++++++++++++++++---------- pages/index.js | 45 ++++++----- 3 files changed, 204 insertions(+), 64 deletions(-) diff --git a/pages/_app.js b/pages/_app.js index 278cc06..8f46ea6 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,4 +1,5 @@ 'use client'; + import { SessionProvider } from 'next-auth/react'; // Importing the Bootstrap CSS import 'bootstrap/dist/css/bootstrap.min.css'; @@ -15,11 +16,14 @@ export function App({ Component, pageProps: { session, ...pageProps } }) { refetchOnWindowFocus: false, }, }, - }) + }); return ( - + { + // eslint-disable-next-line react/jsx-props-no-spreading + + } diff --git a/pages/about.js b/pages/about.js index 621e0e4..3d7f80b 100644 --- a/pages/about.js +++ b/pages/about.js @@ -1,4 +1,5 @@ 'use client'; + import { Button, Card, Col, Row } from 'react-bootstrap'; import Layout from '../components/layout'; @@ -31,16 +32,28 @@ function About() { MusicCPR is a free web-based platform to promote standards-based instrumental music education. MusicCPR aligns with four artistic processes found in{' '} - + National Standards for Music Education : create, perform, respond, and connect and their manifestations in - state standards for instrumental music education (e.g., New York's{' '} - + state standards for instrumental music education (e.g., New York's{' '} + Arts Learning Standards - , Virginia's{' '} - + , Virginia's{' '} + Music Standards of Learning ). @@ -49,59 +62,121 @@ function About() {

We provide teachers with research-based and standards-aligned tools for - facilitating and assessing individual students' music learning. These - tools connect with established repertoire on state music education + facilitating and assessing individual students' music learning. + These tools connect with established repertoire on state music education association lists, as well as newly commissioned repertoire that highlights underrepresented musics and composers.

MusicCPR is housed at James Madison University, in a collaboration between the{' '} - Department of Computer Science and the{' '} - + + Department of Computer Science + {' '} + and the{' '} + Office of Creative Propulsion {' '} with collaborators at{' '} - University of Rochester's{' '} - Eastman School of Music and - in University of Delaware's{' '} - School of Music. MusicCPR's - development has been supported by these institutions, as well as{' '} - 4-VA Collaborative and{' '} + + University of Rochester + + 's{' '} + + Eastman School of Music + {' '} + and in{' '} + + University of Delaware + + 's{' '} + + School of Music + + . MusicCPR's development has been supported by these institutions, + as well as{' '} + + 4-VA Collaborative + {' '} + and{' '} National Association for Music Education .

- If you're interested in trying MusicCPR, or have a question for the - team, don't hesitate to drop us a line at{' '} - feedback@musiccpr.org + If you're interested in trying MusicCPR, or have a question for the + team, don't hesitate to drop us a line at{' '} + + feedback@musiccpr.org +

- +

Investigators

  • - + Lisa R. Caravan, DMA {' '} (Assistant Professor, Department of Music Teaching and Learning, Eastman School of Music, University of Rochester)
  • - + Alden H. Snell, II, Ph.D. {' '} (Associate Professor, Department of Music Teaching and Learning, Eastman School of Music, University of Rochester)
  • - Michael C. Stewart, Ph.D.{' '} + + Michael C. Stewart, Ph.D. + {' '} (Assistant Professor of Computer Science, James Madison University)
  • - + David A. Stringham, Ph.D. {' '} (Professor of Music; Executive Director, Office of Creative @@ -114,35 +189,60 @@ function About() { Abdullah Mohammed Ali (Undergraduate Student, James Madison University)
  • -
  • Jerome Donfack (Undergraduate Student, James Madison University)
  • - Alex Dumouchelle{' '} + Jerome Donfack (Undergraduate Student, James Madison University) +
  • +
  • + + Alex Dumouchelle + {' '} (Undergraduate Student, James Madison University)
  • Zoey Fox (Consultant)
  • Jonah Giblin (Undergraduate Student, James Madison University)
  • - Benjamin Guerrero, MM (Preparing - Future Faculty Fellow, James Madison University; Ph.D. Candidate, - University of Rochester) -
  • -
  • - Luke Hennessy (Undergraduate, James Madison University) + + Benjamin Guerrero, MM + {' '} + (Preparing Future Faculty Fellow, James Madison University; Ph.D. + Candidate, University of Rochester)
  • +
  • Luke Hennessy (Undergraduate, James Madison University)
  • - Matt Wolffe - (Undergraduate, James Madison University) + + Matt Wolffe + + (Undergraduate, James Madison University)
  • Thomas Hassett (Undergraduate Student Alumnus, School of Music; Innovation Leader, Center for Inclusive Music Engagement; James Madison University)
  • -
  • - Chris Hopkins {' '}(Undergraduate Student, James Madison University)
  • - William Jedrzejczak (Undergraduate Student, - James Madison University) + + Chris Hopkins{' '} + {' '} + (Undergraduate Student, James Madison University) +
  • +
  • + William Jedrzejczak (Undergraduate Student, James Madison University)
  • Heidi Lucas, DMA (Visiting Assistant Professor of Brass and Music @@ -158,7 +258,11 @@ function About() {
  • Zamua Nasrawt (Consulting Musician and Web Developer)
  • - + Liem Nguyen (Undergraduate Student, James Madison University)
  • @@ -166,7 +270,11 @@ function About() { Meara Patterson (Undergraduate Student, James Madison University)
  • - + Phil Riley {' '} (Lecturer in Computer Science, James Madison University) @@ -175,17 +283,38 @@ function About() {
  • Eliza Samuels (Undergraduate Student, James Madison University)
  • Nathan Self (Consulting Musician and Web Developer)
  • - Paweł W. Woźniak, Ph.D.{' '} + + Paweł W. Woźniak, Ph.D. + {' '} (Associate Professor, Interaction Design and Software Engineering division, Department of Computer Science and Engineering, Chalmers University)
  • - Lauren Yu (Web - Developer) + + Lauren Yu + {' '} + (Web Developer)
  • -
  • Joshua Hairston (Undergraduate Student, James Madison University)
  • +
  • + + Joshua Hairston + {' '} + (Undergraduate Student, James Madison University) +
); diff --git a/pages/index.js b/pages/index.js index 506eeee..409efaa 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,16 +1,22 @@ 'use client'; + import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import Link from 'next/link'; import Card from 'react-bootstrap/Card'; import CardGroup from 'react-bootstrap/CardGroup'; -import Layout from '../components/layout'; import Row from 'react-bootstrap/Row'; import Col from 'react-bootstrap/Col'; import { Container } from 'react-bootstrap'; -import { FaComment, FaLaughBeam, FaMicrophone, FaMicrophoneAlt } from 'react-icons/fa'; -import { FaTools } from 'react-icons/fa'; -import { FaRulerCombined } from 'react-icons/fa'; +import { + FaComment, + FaLaughBeam, + FaMicrophone, + FaMicrophoneAlt, + FaTools, + FaRulerCombined, +} from 'react-icons/fa'; +import Layout from '../components/layout'; function Index() { return ( @@ -19,10 +25,10 @@ function Index() {

- MusicCPR is a free platform that facilitates music - teachers' collection of individual student achievement data that - aligns with ensemble repertoire and artistic processes (create, - perform, respond, connect) described in National Standards for Arts + MusicCPR is a free platform that facilitates music teachers' + collection of individual student achievement data that aligns with + ensemble repertoire and artistic processes (create, perform, + respond, connect) described in National Standards for Arts Education.

@@ -63,7 +69,7 @@ function Index() {
  • Web-based
  • Assign activities
  • - Grade students' work + Grade students' work