From f7731a4846c82b28ecd1253520ca404b15a279c2 Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Wed, 9 Jul 2025 16:33:38 +0530 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20Added=20star=20toggle=20but=20i?= =?UTF-8?q?t=20doesn't=20toggle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Table/index.js | 60 +++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 39125efa..35408a7d 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -50,11 +50,11 @@ const Table = () => { new Array(questions.length).fill(''); /* If the user has previously visited the website, then an array in - LocalStorage would exist of a certain length which corresponds to which - questions they have/have not completed. In the event that we add new questions - to the list, then we would need to resize and copy the existing 'checked' - array before updating it in LocalStorage in order to transfer their saved - progress. */ + LocalStorage would exist of a certain length which corresponds to which + questions they have/have not completed. In the event that we add new questions + to the list, then we would need to resize and copy the existing 'checked' + array before updating it in LocalStorage in order to transfer their saved + progress. */ if (checkedList.length !== questions.length) { const resizedCheckedList = new Array(questions.length).fill(false); @@ -114,6 +114,15 @@ const Table = () => { JSON.parse(localStorage.getItem('showPatterns')) || new Array(1).fill(true), ); + const [important, setImportant] = useState( + JSON.parse(localStorage.getItem('importantProblems')) || + new Array(questions.length).fill(false), + ); + + useEffect(() => { + localStorage.setItem('importantProblems', JSON.stringify(important)); + }, [important]); + useEffect(() => { window.localStorage.setItem('checked', JSON.stringify(checked)); }, [checked]); @@ -171,7 +180,7 @@ const Table = () => { totalValue={totalDifficultyCount.Total} label={() => `${difficultyCount.Total} / - ${totalDifficultyCount.Total}` + ${totalDifficultyCount.Total}` } labelPosition={0} labelStyle={{ @@ -481,7 +490,46 @@ const Table = () => { }, Filter: SelectColumnFilter, }, + /* eslint-disable react/prop-types */ { + Header: '⭐', + accessor: 'important', + disableSortBy: true, + disableFilters: true, + Cell: ({ row }) => { + const id = Number(row?.original?.id); + if (Number.isNaN(id)) return '❌'; + + const handleKeyPress = e => { + if (e.key === 'Enter' || e.key === ' ') { + const updatedImportant = [...important]; + updatedImportant[id] = !updatedImportant[id]; + setImportant(updatedImportant); + console.log('Toggled important:', updatedImportant); + } + }; + + return ( + { + console.log('Star clicked!', id); // add this + const updatedImportant = [...important]; + updatedImportant[id] = !updatedImportant[id]; + setImportant(updatedImportant); + }} + onKeyDown={handleKeyPress} + aria-label="Mark as important for revision" + data-tip="Mark as important for revision" + > + {important[id] ? '⭐' : '☆'} + + ); + }, + }, // Optional + /* eslint-enable react/prop-types */ { Header: 'Last Solved On', accessor: 'LastSolvedOn', disableSortBy: true, From 0cc695f5b1084de6cd8f182ecd3dab914a26ad17 Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Wed, 9 Jul 2025 22:38:02 +0530 Subject: [PATCH 2/6] star toggle is working (not storing yet) --- src/components/Table/index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 35408a7d..4573d9e6 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -41,6 +41,7 @@ const iconPath = `${process.env.PUBLIC_URL}/static/icons/`; const Table = () => { const [resetCount, setResetCount] = useState(0); + let checkedList = JSON.parse(localStorage.getItem('checked')) || new Array(questions.length).fill(false); @@ -114,11 +115,12 @@ const Table = () => { JSON.parse(localStorage.getItem('showPatterns')) || new Array(1).fill(true), ); + const savedImportant = JSON.parse(localStorage.getItem('importantProblems')); const [important, setImportant] = useState( - JSON.parse(localStorage.getItem('importantProblems')) || - new Array(questions.length).fill(false), + savedImportant && savedImportant.length === questions.length + ? savedImportant + : new Array(questions.length).fill(false), ); - useEffect(() => { localStorage.setItem('importantProblems', JSON.stringify(important)); }, [important]); @@ -498,6 +500,8 @@ const Table = () => { disableFilters: true, Cell: ({ row }) => { const id = Number(row?.original?.id); + console.log('⭐ Row ID:', id, row?.original); + if (Number.isNaN(id)) return '❌'; const handleKeyPress = e => { @@ -546,7 +550,7 @@ const Table = () => { }, ], // eslint-disable-next-line - [resetCount], + [resetCount, important], ); const { From d96c27d533bede33fa011d3dc5fcbccbdbf82fb9 Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Thu, 10 Jul 2025 11:20:19 +0530 Subject: [PATCH 3/6] add star feature, saves starred question to view later --- src/components/Table/index.js | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 4573d9e6..cc45feae 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -121,6 +121,11 @@ const Table = () => { ? savedImportant : new Array(questions.length).fill(false), ); + + // Returns an array of question objects that are starred + const getStarredQuestions = () => { + return questions.filter((q, idx) => important[idx]); + }; useEffect(() => { localStorage.setItem('importantProblems', JSON.stringify(important)); }, [important]); @@ -591,10 +596,48 @@ const Table = () => { useSortBy, ); + const [showOnlyStarred, setShowOnlyStarred] = useState(false); + + useEffect(() => { + if (showOnlyStarred) { + setData(getStarredQuestions()); + } else { + setData(filteredByCheckbox()); + } + // eslint-disable-next-line + }, [showOnlyStarred, important, checked, resetCount]); + return ( + + {/* Minimal Show Only Starred Button */} +
+ +
+ {headerGroups.map(headerGroup => ( From 927e964cd5df84c7f2e138026e503bcfa096871d Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Thu, 10 Jul 2025 11:29:29 +0530 Subject: [PATCH 4/6] add a toast --- package-lock.json | 36 +++++++++++++++++++++++++++++++++++ package.json | 1 + src/components/Table/index.js | 30 ++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5427d0b..26698306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "react-scroll": "^1.8.0", "react-table": "^7.6.3", "react-test-renderer": "^16.14.0", + "react-toastify": "^8.1.0", "react-toggle": "^4.1.1", "react-tooltip": "^3.11.2", "reactstrap": "^8.8.1", @@ -5517,6 +5518,15 @@ "node": ">=6" } }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -18670,6 +18680,19 @@ "scheduler": "^0.19.1" } }, + "node_modules/react-toastify": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz", + "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==", + "license": "MIT", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-toggle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.1.tgz", @@ -28206,6 +28229,11 @@ "shallow-clone": "^3.0.0" } }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -38755,6 +38783,14 @@ "scheduler": "^0.19.1" } }, + "react-toastify": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.0.tgz", + "integrity": "sha512-M+Q3rTmEw/53Csr7NsV/YnldJe4c7uERcY7Tma9mvLU98QT2VhIkKwjBzzxZkJRk/oBKyUAtkyMjMgO00hx6gQ==", + "requires": { + "clsx": "^1.1.1" + } + }, "react-toggle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.1.tgz", diff --git a/package.json b/package.json index 0f3e6a60..af81a817 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "react-scroll": "^1.8.0", "react-table": "^7.6.3", "react-test-renderer": "^16.14.0", + "react-toastify": "^8.1.0", "react-toggle": "^4.1.1", "react-tooltip": "^3.11.2", "reactstrap": "^8.8.1", diff --git a/src/components/Table/index.js b/src/components/Table/index.js index cc45feae..346feda3 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -23,6 +23,8 @@ import { FaRandom, FaQuestionCircle, } from 'react-icons/fa'; +import { ToastContainer, toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; import { DefaultColumnFilter, SelectDifficultyColumnFilter, @@ -30,7 +32,6 @@ import { SelectCheckedColumnFilter, } from './filters'; import { Event } from '../Shared/Tracking'; - import questions, { updated } from '../../data'; import 'react-toggle/style.css'; @@ -505,8 +506,6 @@ const Table = () => { disableFilters: true, Cell: ({ row }) => { const id = Number(row?.original?.id); - console.log('⭐ Row ID:', id, row?.original); - if (Number.isNaN(id)) return '❌'; const handleKeyPress = e => { @@ -514,7 +513,17 @@ const Table = () => { const updatedImportant = [...important]; updatedImportant[id] = !updatedImportant[id]; setImportant(updatedImportant); - console.log('Toggled important:', updatedImportant); + toast( + updatedImportant[id] + ? 'Marked as Important' + : 'Removed from Important', + { + type: updatedImportant[id] ? 'success' : 'info', + autoClose: 1200, + hideProgressBar: true, + position: 'bottom-center', + }, + ); } }; @@ -524,10 +533,20 @@ const Table = () => { tabIndex={0} style={{ cursor: 'pointer', fontSize: '1.2em' }} onClick={() => { - console.log('Star clicked!', id); // add this const updatedImportant = [...important]; updatedImportant[id] = !updatedImportant[id]; setImportant(updatedImportant); + toast( + updatedImportant[id] + ? 'Marked as Important' + : 'Removed from Important', + { + type: updatedImportant[id] ? 'success' : 'info', + autoClose: 1200, + hideProgressBar: true, + position: 'bottom-center', + }, + ); }} onKeyDown={handleKeyPress} aria-label="Mark as important for revision" @@ -609,6 +628,7 @@ const Table = () => { return ( + From ca299f5d25ced5486bc5dc9cb579b3c60b38835c Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Thu, 10 Jul 2025 11:35:43 +0530 Subject: [PATCH 5/6] animation to star --- src/components/Table/index.js | 63 ++++++++++++++++---------------- src/components/Table/styles.scss | 22 +++++++++++ 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 346feda3..643a7938 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -115,13 +115,13 @@ const Table = () => { const [showPatterns, setShowPatterns] = useState( JSON.parse(localStorage.getItem('showPatterns')) || new Array(1).fill(true), ); - const savedImportant = JSON.parse(localStorage.getItem('importantProblems')); const [important, setImportant] = useState( savedImportant && savedImportant.length === questions.length ? savedImportant : new Array(questions.length).fill(false), ); + const [starAnimation, setStarAnimation] = useState({}); // Returns an array of question objects that are starred const getStarredQuestions = () => { @@ -508,22 +508,31 @@ const Table = () => { const id = Number(row?.original?.id); if (Number.isNaN(id)) return '❌'; + const handleToggle = () => { + const updatedImportant = [...important]; + updatedImportant[id] = !updatedImportant[id]; + setImportant(updatedImportant); + toast( + updatedImportant[id] + ? 'Marked as Important' + : 'Removed from Important', + { + type: updatedImportant[id] ? 'success' : 'info', + autoClose: 1200, + hideProgressBar: true, + position: 'bottom-center', + }, + ); + // Trigger animation + setStarAnimation(prev => ({ ...prev, [id]: true })); + setTimeout(() => { + setStarAnimation(prev => ({ ...prev, [id]: false })); + }, 400); + }; + const handleKeyPress = e => { if (e.key === 'Enter' || e.key === ' ') { - const updatedImportant = [...important]; - updatedImportant[id] = !updatedImportant[id]; - setImportant(updatedImportant); - toast( - updatedImportant[id] - ? 'Marked as Important' - : 'Removed from Important', - { - type: updatedImportant[id] ? 'success' : 'info', - autoClose: 1200, - hideProgressBar: true, - position: 'bottom-center', - }, - ); + handleToggle(); } }; @@ -531,23 +540,15 @@ const Table = () => { { - const updatedImportant = [...important]; - updatedImportant[id] = !updatedImportant[id]; - setImportant(updatedImportant); - toast( - updatedImportant[id] - ? 'Marked as Important' - : 'Removed from Important', - { - type: updatedImportant[id] ? 'success' : 'info', - autoClose: 1200, - hideProgressBar: true, - position: 'bottom-center', - }, - ); + style={{ + cursor: 'pointer', + fontSize: '1.2em', + transition: 'color 0.2s', }} + className={ + important[id] && starAnimation[id] ? 'star-animate' : '' + } + onClick={handleToggle} onKeyDown={handleKeyPress} aria-label="Mark as important for revision" data-tip="Mark as important for revision" diff --git a/src/components/Table/styles.scss b/src/components/Table/styles.scss index 5c566b27..ad5327bf 100644 --- a/src/components/Table/styles.scss +++ b/src/components/Table/styles.scss @@ -123,3 +123,25 @@ body.dark-mode .modal-content { text-wrap: nowrap; } } + +.star-animate { + animation: pop-star 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both; +} + +@keyframes pop-star { + 0% { + transform: scale(1) rotate(0deg); + color: #ffd700; + filter: drop-shadow(0 0 0 #ffd700); + } + 60% { + transform: scale(1.5) rotate(-15deg); + color: #ffd700; + filter: drop-shadow(0 0 8px #ffd700); + } + 100% { + transform: scale(1) rotate(0deg); + color: #ffd700; + filter: drop-shadow(0 0 0 #ffd700); + } +} From 86d381de671ca7cde9883c9657757ab7ced07caf Mon Sep 17 00:00:00 2001 From: Anjali Kamath Date: Thu, 10 Jul 2025 18:12:02 +0530 Subject: [PATCH 6/6] persist starred questions after refreshing page --- src/components/Table/index.js | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 643a7938..ecddea86 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -79,6 +79,22 @@ const Table = () => { window.localStorage.setItem('checkedAt', JSON.stringify(checkedAtList)); } + let importantList = + JSON.parse(localStorage.getItem('importantProblems')) || + new Array(questions.length).fill(false); + + if (importantList.length !== questions.length) { + const resizedImportantList = new Array(questions.length).fill(false); + for (let i = 0; i < importantList.length; i += 1) { + resizedImportantList[i] = importantList[i]; + } + importantList = resizedImportantList; + window.localStorage.setItem( + 'importantProblems', + JSON.stringify(importantList), + ); + } + const filteredByCheckbox = () => { const checkbox = localStorage.getItem('checkbox') || ''; return questions.filter(question => { @@ -123,14 +139,6 @@ const Table = () => { ); const [starAnimation, setStarAnimation] = useState({}); - // Returns an array of question objects that are starred - const getStarredQuestions = () => { - return questions.filter((q, idx) => important[idx]); - }; - useEffect(() => { - localStorage.setItem('importantProblems', JSON.stringify(important)); - }, [important]); - useEffect(() => { window.localStorage.setItem('checked', JSON.stringify(checked)); }, [checked]); @@ -143,6 +151,10 @@ const Table = () => { window.localStorage.setItem('showPatterns', JSON.stringify(showPatterns)); }, [showPatterns]); + useEffect(() => { + window.localStorage.setItem('importantProblems', JSON.stringify(important)); + }, [important]); + const defaultColumn = React.useMemo( () => ({ Filter: DefaultColumnFilter, @@ -619,11 +631,12 @@ const Table = () => { const [showOnlyStarred, setShowOnlyStarred] = useState(false); useEffect(() => { + // Always start from the full questions list + let filtered = filteredByCheckbox(); if (showOnlyStarred) { - setData(getStarredQuestions()); - } else { - setData(filteredByCheckbox()); + filtered = filtered.filter(q => important[q.id]); } + setData(filtered); // eslint-disable-next-line }, [showOnlyStarred, important, checked, resetCount]);