From b2bbc7f81bd036a1091754aaae5bf050553bed31 Mon Sep 17 00:00:00 2001
From: Aarushi Patidar <206544456+aarushi-patidar@users.noreply.github.com>
Date: Sun, 31 Aug 2025 18:32:56 +0530
Subject: [PATCH 1/6] fix: move select and download buttons to catalogue
---
src/components/CatalogueContent.tsx | 24 +++++++++++++++++++++
src/components/SideBar.tsx | 33 +++++------------------------
2 files changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/components/CatalogueContent.tsx b/src/components/CatalogueContent.tsx
index c7acfe6..fc3e635 100644
--- a/src/components/CatalogueContent.tsx
+++ b/src/components/CatalogueContent.tsx
@@ -417,6 +417,30 @@ const CatalogueContent = () => {
+
+
+ {/* Select/Deselect/Download All Buttons */}
+
Date: Sun, 31 Aug 2025 18:43:54 +0530
Subject: [PATCH 2/6] fix: move select and download buttons to catalogue
---
src/components/SideBar.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx
index be8070a..2fb3e05 100644
--- a/src/components/SideBar.tsx
+++ b/src/components/SideBar.tsx
@@ -18,6 +18,7 @@ function SideBar({
selectedSemesters,
selectedAnswerKeyIncluded,
filterOptions,
+ filtersNotPulled,
handleApplyFilters,
handleSelectAll,
handleDeselectAll,
From da8ac1b545c533dc74b6033c2b38809d87bcf175 Mon Sep 17 00:00:00 2001
From: Aarushi Patidar <206544456+aarushi-patidar@users.noreply.github.com>
Date: Sun, 31 Aug 2025 19:44:09 +0530
Subject: [PATCH 3/6] Merge branch 'prod' into aarushi/catalogue
---
README.md | 2 +-
package.json | 6 +
pnpm-lock.yaml | 315 +++++++++++++++
src/app/api/upload/route.ts | 2 +-
src/app/upload/page.tsx | 638 +++++++++++++++++++++---------
src/components/Footer.tsx | 8 +
src/components/SideBar.tsx | 115 ++----
src/components/SidebarButton.tsx | 23 ++
src/components/SidebarSection.tsx | 55 +++
src/components/screens/Faq.tsx | 12 +-
src/db/papers.ts | 1 +
src/interface.ts | 1 +
src/styles/globals.css | 15 +-
13 files changed, 920 insertions(+), 273 deletions(-)
create mode 100644 src/components/SidebarButton.tsx
create mode 100644 src/components/SidebarSection.tsx
diff --git a/README.md b/README.md
index 50b5419..a73eac1 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
.
Papers
diff --git a/package.json b/package.json
index 8c4aaf9..2a80e9f 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,10 @@
"start": "next start"
},
"dependencies": {
+ "@dnd-kit/core": "^6.3.1",
+ "@dnd-kit/sortable": "^10.0.0",
+ "@dnd-kit/utilities": "^3.2.2",
+ "@google/genai": "^0.7.0",
"@radix-ui/react-accordion": "^1.2.4",
"@radix-ui/react-dialog": "^1.1.7",
"@radix-ui/react-dropdown-menu": "^2.1.16",
@@ -51,6 +55,7 @@
"prettier-plugin-tailwindcss": "^0.6.11",
"raw-loader": "^4.0.2",
"react": "^18.3.1",
+ "react-beautiful-dnd": "^13.1.1",
"react-dom": "^18.3.1",
"react-dropzone": "^14.3.8",
"react-hot-toast": "^2.5.2",
@@ -63,6 +68,7 @@
"devDependencies": {
"@types/eslint": "8.56.12",
"@types/react": "^18.3.20",
+ "@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18.3.6",
"@typescript-eslint/eslint-plugin": "^8.30.1",
"@typescript-eslint/parser": "^8.30.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3168b3b..191ef5d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,18 @@ importers:
.:
dependencies:
+ '@dnd-kit/core':
+ specifier: ^6.3.1
+ version: 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@dnd-kit/sortable':
+ specifier: ^10.0.0
+ version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
+ '@dnd-kit/utilities':
+ specifier: ^3.2.2
+ version: 3.2.2(react@18.3.1)
+ '@google/genai':
+ specifier: ^0.7.0
+ version: 0.7.0
'@radix-ui/react-accordion':
specifier: ^1.2.4
version: 1.2.4(@types/react-dom@18.3.6(@types/react@18.3.20))(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -131,6 +143,9 @@ importers:
react:
specifier: ^18.3.1
version: 18.3.1
+ react-beautiful-dnd:
+ specifier: ^13.1.1
+ version: 13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-dom:
specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
@@ -162,6 +177,9 @@ importers:
'@types/react':
specifier: ^18.3.20
version: 18.3.20
+ '@types/react-beautiful-dnd':
+ specifier: ^13.1.8
+ version: 13.1.8
'@types/react-dom':
specifier: ^18.3.6
version: 18.3.6(@types/react@18.3.20)
@@ -194,6 +212,28 @@ packages:
resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==}
engines: {node: '>=6.9.0'}
+ '@dnd-kit/accessibility@3.1.1':
+ resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==}
+ peerDependencies:
+ react: '>=16.8.0'
+
+ '@dnd-kit/core@6.3.1':
+ resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@dnd-kit/sortable@10.0.0':
+ resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==}
+ peerDependencies:
+ '@dnd-kit/core': ^6.3.0
+ react: '>=16.8.0'
+
+ '@dnd-kit/utilities@3.2.2':
+ resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==}
+ peerDependencies:
+ react: '>=16.8.0'
+
'@emnapi/core@1.4.3':
resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==}
@@ -236,6 +276,10 @@ packages:
'@floating-ui/utils@0.2.9':
resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
+ '@google/genai@0.7.0':
+ resolution: {integrity: sha512-r+Fwj/emnXZN5R+4JCxDXboY4AGTmTn7+Wnori5dgyJiStP0P82f9YYL0CVsCnDIumNY2i0UIcZ1zGZdtHJ34w==}
+ engines: {node: '>=18.0.0'}
+
'@humanwhocodes/config-array@0.13.0':
resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==}
engines: {node: '>=10.10.0'}
@@ -1096,6 +1140,11 @@ packages:
'@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
+ '@types/hoist-non-react-statics@3.3.7':
+ resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==}
+ peerDependencies:
+ '@types/react': '*'
+
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@@ -1112,11 +1161,17 @@ packages:
'@types/prop-types@15.7.14':
resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==}
+ '@types/react-beautiful-dnd@13.1.8':
+ resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
+
'@types/react-dom@18.3.6':
resolution: {integrity: sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw==}
peerDependencies:
'@types/react': ^18.0.0
+ '@types/react-redux@7.1.34':
+ resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==}
+
'@types/react@18.3.20':
resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==}
@@ -1613,6 +1668,9 @@ packages:
crypto-js@4.2.0:
resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==}
+ css-box-model@1.2.1:
+ resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==}
+
cssesc@3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
engines: {node: '>=4'}
@@ -2046,10 +2104,18 @@ packages:
resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==}
engines: {node: '>=10'}
+ gaxios@6.7.1:
+ resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==}
+ engines: {node: '>=14'}
+
gaxios@7.1.1:
resolution: {integrity: sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==}
engines: {node: '>=18'}
+ gcp-metadata@6.1.1:
+ resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==}
+ engines: {node: '>=14'}
+
gcp-metadata@7.0.1:
resolution: {integrity: sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==}
engines: {node: '>=18'}
@@ -2126,6 +2192,14 @@ packages:
resolution: {integrity: sha512-HMxFl2NfeHYnaL1HoRIN1XgorKS+6CDaM+z9LSSN+i/nKDDL4KFFEWogMXu7jV4HZQy2MsxpY+wA5XIf3w410A==}
engines: {node: '>=18'}
+ google-auth-library@9.15.1:
+ resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==}
+ engines: {node: '>=14'}
+
+ google-logging-utils@0.0.2:
+ resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==}
+ engines: {node: '>=14'}
+
google-logging-utils@1.1.1:
resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==}
engines: {node: '>=14'}
@@ -2148,6 +2222,10 @@ packages:
graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
+ gtoken@7.1.0:
+ resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==}
+ engines: {node: '>=14.0.0'}
+
gtoken@8.0.0:
resolution: {integrity: sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==}
engines: {node: '>=18'}
@@ -2179,6 +2257,9 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ hoist-non-react-statics@3.3.2:
+ resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
+
https-proxy-agent@7.0.6:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
@@ -2302,6 +2383,10 @@ packages:
resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
engines: {node: '>= 0.4'}
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
is-string@1.1.1:
resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
engines: {node: '>= 0.4'}
@@ -2476,6 +2561,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+
memory-pager@1.5.0:
resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==}
@@ -2631,6 +2719,15 @@ packages:
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
+ node-fetch@2.7.0:
+ resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+
node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -2926,6 +3023,9 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ raf-schd@4.0.3:
+ resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
+
randombytes@2.1.0:
resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
@@ -2939,6 +3039,13 @@ packages:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
+ react-beautiful-dnd@13.1.1:
+ resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
+ deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672'
+ peerDependencies:
+ react: ^16.8.5 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
+
react-dom@18.3.1:
resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
peerDependencies:
@@ -2965,6 +3072,9 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
react-pdf@9.2.1:
resolution: {integrity: sha512-AJt0lAIkItWEZRA5d/mO+Om4nPCuTiQ0saA+qItO967DTjmGjnhmF+Bi2tL286mOTfBlF5CyLzJ35KTMaDoH+A==}
peerDependencies:
@@ -2975,6 +3085,18 @@ packages:
'@types/react':
optional: true
+ react-redux@7.2.9:
+ resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
+ peerDependencies:
+ react: ^16.8.3 || ^17 || ^18
+ react-dom: '*'
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@@ -3033,6 +3155,9 @@ packages:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
+ redux@4.2.1:
+ resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
+
reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -3353,6 +3478,9 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+
tr46@5.1.1:
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
engines: {node: '>=18'}
@@ -3439,6 +3567,11 @@ packages:
'@types/react':
optional: true
+ use-memo-one@1.1.3:
+ resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+
use-sidecar@1.1.3:
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
engines: {node: '>=10'}
@@ -3452,6 +3585,10 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+ uuid@9.0.1:
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+ hasBin: true
+
warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
@@ -3463,6 +3600,9 @@ packages:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+
webidl-conversions@7.0.0:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
@@ -3485,6 +3625,9 @@ packages:
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
engines: {node: '>=18'}
+ whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+
which-boxed-primitive@1.1.1:
resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
engines: {node: '>= 0.4'}
@@ -3528,6 +3671,18 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
@@ -3559,6 +3714,31 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
+ '@dnd-kit/accessibility@3.1.1(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ tslib: 2.8.1
+
+ '@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@dnd-kit/accessibility': 3.1.1(react@18.3.1)
+ '@dnd-kit/utilities': 3.2.2(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ tslib: 2.8.1
+
+ '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@dnd-kit/core': 6.3.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@dnd-kit/utilities': 3.2.2(react@18.3.1)
+ react: 18.3.1
+ tslib: 2.8.1
+
+ '@dnd-kit/utilities@3.2.2(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ tslib: 2.8.1
+
'@emnapi/core@1.4.3':
dependencies:
'@emnapi/wasi-threads': 1.0.2
@@ -3615,6 +3795,16 @@ snapshots:
'@floating-ui/utils@0.2.9': {}
+ '@google/genai@0.7.0':
+ dependencies:
+ google-auth-library: 9.15.1
+ ws: 8.18.3
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
+
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
@@ -4429,6 +4619,11 @@ snapshots:
'@types/estree@1.0.7': {}
+ '@types/hoist-non-react-statics@3.3.7(@types/react@18.3.20)':
+ dependencies:
+ '@types/react': 18.3.20
+ hoist-non-react-statics: 3.3.2
+
'@types/json-schema@7.0.15': {}
'@types/json5@0.0.29': {}
@@ -4452,10 +4647,21 @@ snapshots:
'@types/prop-types@15.7.14': {}
+ '@types/react-beautiful-dnd@13.1.8':
+ dependencies:
+ '@types/react': 18.3.20
+
'@types/react-dom@18.3.6(@types/react@18.3.20)':
dependencies:
'@types/react': 18.3.20
+ '@types/react-redux@7.1.34':
+ dependencies:
+ '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.20)
+ '@types/react': 18.3.20
+ hoist-non-react-statics: 3.3.2
+ redux: 4.2.1
+
'@types/react@18.3.20':
dependencies:
'@types/prop-types': 15.7.14
@@ -5003,6 +5209,10 @@ snapshots:
crypto-js@4.2.0: {}
+ css-box-model@1.2.1:
+ dependencies:
+ tiny-invariant: 1.3.3
+
cssesc@3.0.0: {}
csstype@3.1.3: {}
@@ -5546,6 +5756,17 @@ snapshots:
fuse.js@7.1.0: {}
+ gaxios@6.7.1:
+ dependencies:
+ extend: 3.0.2
+ https-proxy-agent: 7.0.6
+ is-stream: 2.0.1
+ node-fetch: 2.7.0
+ uuid: 9.0.1
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
gaxios@7.1.1:
dependencies:
extend: 3.0.2
@@ -5554,6 +5775,15 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ gcp-metadata@6.1.1:
+ dependencies:
+ gaxios: 6.7.1
+ google-logging-utils: 0.0.2
+ json-bigint: 1.0.0
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
gcp-metadata@7.0.1:
dependencies:
gaxios: 7.1.1
@@ -5661,6 +5891,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ google-auth-library@9.15.1:
+ dependencies:
+ base64-js: 1.5.1
+ ecdsa-sig-formatter: 1.0.11
+ gaxios: 6.7.1
+ gcp-metadata: 6.1.1
+ gtoken: 7.1.0
+ jws: 4.0.0
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ google-logging-utils@0.0.2: {}
+
google-logging-utils@1.1.1: {}
googleapis-common@8.0.0:
@@ -5686,6 +5930,14 @@ snapshots:
graphemer@1.4.0: {}
+ gtoken@7.1.0:
+ dependencies:
+ gaxios: 6.7.1
+ jws: 4.0.0
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
gtoken@8.0.0:
dependencies:
gaxios: 7.1.1
@@ -5715,6 +5967,10 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ hoist-non-react-statics@3.3.2:
+ dependencies:
+ react-is: 16.13.1
+
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
@@ -5841,6 +6097,8 @@ snapshots:
dependencies:
call-bound: 1.0.4
+ is-stream@2.0.1: {}
+
is-string@1.1.1:
dependencies:
call-bound: 1.0.4
@@ -6016,6 +6274,8 @@ snapshots:
math-intrinsics@1.1.0: {}
+ memoize-one@5.2.1: {}
+
memory-pager@1.5.0: {}
merge-refs@1.3.0(@types/react@18.3.20):
@@ -6150,6 +6410,10 @@ snapshots:
node-domexception@1.0.0: {}
+ node-fetch@2.7.0:
+ dependencies:
+ whatwg-url: 5.0.0
+
node-fetch@3.3.2:
dependencies:
data-uri-to-buffer: 4.0.1
@@ -6388,6 +6652,8 @@ snapshots:
queue-microtask@1.2.3: {}
+ raf-schd@4.0.3: {}
+
randombytes@2.1.0:
dependencies:
safe-buffer: 5.2.1
@@ -6405,6 +6671,20 @@ snapshots:
minimist: 1.2.8
strip-json-comments: 2.0.1
+ react-beautiful-dnd@13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@babel/runtime': 7.27.0
+ css-box-model: 1.2.1
+ memoize-one: 5.2.1
+ raf-schd: 4.0.3
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ redux: 4.2.1
+ use-memo-one: 1.1.3(react@18.3.1)
+ transitivePeerDependencies:
+ - react-native
+
react-dom@18.3.1(react@18.3.1):
dependencies:
loose-envify: 1.4.0
@@ -6431,6 +6711,8 @@ snapshots:
react-is@16.13.1: {}
+ react-is@17.0.2: {}
+
react-pdf@9.2.1(@types/react@18.3.20)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
clsx: 2.1.1
@@ -6446,6 +6728,18 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.20
+ react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@babel/runtime': 7.27.0
+ '@types/react-redux': 7.1.34
+ hoist-non-react-statics: 3.3.2
+ loose-envify: 1.4.0
+ prop-types: 15.8.1
+ react: 18.3.1
+ react-is: 17.0.2
+ optionalDependencies:
+ react-dom: 18.3.1(react@18.3.1)
+
react-remove-scroll-bar@2.3.8(@types/react@18.3.20)(react@18.3.1):
dependencies:
react: 18.3.1
@@ -6512,6 +6806,10 @@ snapshots:
dependencies:
picomatch: 2.3.1
+ redux@4.2.1:
+ dependencies:
+ '@babel/runtime': 7.27.0
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -6899,6 +7197,8 @@ snapshots:
dependencies:
is-number: 7.0.0
+ tr46@0.0.3: {}
+
tr46@5.1.1:
dependencies:
punycode: 2.3.1
@@ -7012,6 +7312,10 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.20
+ use-memo-one@1.1.3(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+
use-sidecar@1.1.3(@types/react@18.3.20)(react@18.3.1):
dependencies:
detect-node-es: 1.1.0
@@ -7022,6 +7326,8 @@ snapshots:
util-deprecate@1.0.2: {}
+ uuid@9.0.1: {}
+
warning@4.0.3:
dependencies:
loose-envify: 1.4.0
@@ -7033,6 +7339,8 @@ snapshots:
web-streams-polyfill@3.3.3: {}
+ webidl-conversions@3.0.1: {}
+
webidl-conversions@7.0.0: {}
webpack-sources@3.2.3: {}
@@ -7072,6 +7380,11 @@ snapshots:
tr46: 5.1.1
webidl-conversions: 7.0.0
+ whatwg-url@5.0.0:
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+
which-boxed-primitive@1.1.1:
dependencies:
is-bigint: 1.1.0
@@ -7141,6 +7454,8 @@ snapshots:
wrappy@1.0.2: {}
+ ws@8.18.3: {}
+
y18n@4.0.3: {}
yaml@2.7.1: {}
diff --git a/src/app/api/upload/route.ts b/src/app/api/upload/route.ts
index dd36940..63ec9d9 100644
--- a/src/app/api/upload/route.ts
+++ b/src/app/api/upload/route.ts
@@ -92,7 +92,6 @@ export async function POST(req: Request) {
);
}
-
const paper = new PaperAdmin({
cloudinary_index: configIndex,
@@ -105,6 +104,7 @@ export async function POST(req: Request) {
exam: null,
semester: null,
campus: null,
+ ambiguous_tags: [],
});
await paper.save();
diff --git a/src/app/upload/page.tsx b/src/app/upload/page.tsx
index 0d35748..bf769b3 100644
--- a/src/app/upload/page.tsx
+++ b/src/app/upload/page.tsx
@@ -1,129 +1,260 @@
"use client";
-import React, { useState, useEffect } from "react";
-import axios, { AxiosError } from "axios";
+
+import { useCallback, useEffect, useState } from "react";
import toast from "react-hot-toast";
-import { handleAPIError } from "../../util/error";
import { Button } from "@/components/ui/button";
-
-import { type APIResponse } from "@/interface";
+import axios, { AxiosError } from "axios";
+import { FiTrash, FiPlus } from "react-icons/fi";
+import Image from "next/image";
+import {
+ DndContext,
+ closestCenter,
+ PointerSensor,
+ TouchSensor,
+ useSensor,
+ useSensors,
+ DragEndEvent,
+} from "@dnd-kit/core";
+import {
+ arrayMove,
+ SortableContext,
+ useSortable,
+ horizontalListSortingStrategy,
+} from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
import Dropzone from "react-dropzone";
-import { Upload } from "lucide-react";
+import { Upload, XIcon } from "lucide-react";
+
+interface APIResponse {
+ status: string;
+ message?: string;
+}
-const Page = () => {
- const [campus, setCampus] = useState("Vellore");
+export default function Page() {
+ const campus = "Vellore";
const [files, setFiles] = useState
([]);
+ const [previews, setPreviews] = useState<
+ { id: string; file: File; preview: string }[]
+ >([]);
const [isUploading, setIsUploading] = useState(false);
- const [, setResetSearch] = useState(false);
- const [isDragging, setIsDragging] = useState(false);
const [isGlobalDragging, setIsGlobalDragging] = useState(false);
+ const [zoomIndex, setZoomIndex] = useState(null);
useEffect(() => {
- const handleDragEnter = (e: DragEvent) => {
- e.preventDefault();
- e.stopPropagation();
- setIsGlobalDragging(true);
+ const onDragEnter = () => setIsGlobalDragging(true);
+ const onDragLeave = () => setIsGlobalDragging(false);
+ const onDrop = () => setIsGlobalDragging(false);
+
+ document.addEventListener("dragenter", onDragEnter);
+ document.addEventListener("dragleave", onDragLeave);
+ document.addEventListener("drop", onDrop);
+
+ return () => {
+ document.removeEventListener("dragenter", onDragEnter);
+ document.removeEventListener("dragleave", onDragLeave);
+ document.removeEventListener("drop", onDrop);
};
+ }, []);
- const handleDragOver = (e: DragEvent) => {
- e.preventDefault();
- e.stopPropagation();
+ // Cleanup URLs when component unmounts
+ useEffect(() => {
+ return () => {
+ previews.forEach((item) => {
+ try {
+ URL.revokeObjectURL(item.preview);
+ } catch (e) {
+ // Ignore errors
+ }
+ });
};
+ }, []); // Only run on unmount
- const handleDragLeave = (e: DragEvent) => {
- e.preventDefault();
- e.stopPropagation();
+ const fileCheckAndSelect = useCallback(
+ (acceptedFiles: File[]) => {
+ const maxFileSize = 5 * 1024 * 1024;
+ const allowedFileTypes = [
+ "application/pdf",
+ "image/jpeg",
+ "image/png",
+ "image/gif",
+ ];
- if (
- !e.relatedTarget ||
- (e.currentTarget !== e.relatedTarget &&
- !(e.currentTarget as Element)?.contains(e.relatedTarget as Node))
- ) {
- setIsGlobalDragging(false);
+ const toastId = toast.loading("Adding your files...");
+ if (!acceptedFiles || acceptedFiles.length === 0) {
+ toast.error("No files selected", { id: toastId });
+ return;
}
- };
- const handleDrop = (e: DragEvent) => {
- e.preventDefault();
- e.stopPropagation();
- setIsGlobalDragging(false);
- };
+ const isNewPdf = acceptedFiles.some(
+ (file) => file.type === "application/pdf",
+ );
- document.addEventListener("dragenter", handleDragEnter);
- document.addEventListener("dragover", handleDragOver);
- document.addEventListener("dragleave", handleDragLeave);
- document.addEventListener("drop", handleDrop);
+ const hasExistingImages = files.some((file) =>
+ file.type.startsWith("image/"),
+ );
- return () => {
- document.removeEventListener("dragenter", handleDragEnter);
- document.removeEventListener("dragover", handleDragOver);
- document.removeEventListener("dragleave", handleDragLeave);
- document.removeEventListener("drop", handleDrop);
- };
- }, []);
+ const hasExistingPdf = files.some(
+ (file) => file.type === "application/pdf",
+ );
- function fileCheckAndSelect(acceptedFiles: T[]) {
- const maxFileSize = 5 * 1024 * 1024;
- const allowedFileTypes = [
- "application/pdf",
- "image/jpeg",
- "image/png",
- "image/gif",
- ];
-
- const toastId = toast.loading("uploading your files");
- if (!acceptedFiles || acceptedFiles.length === 0) {
- toast.error("No files selected", {
- id: toastId,
- });
- return;
- }
+ if (isNewPdf && acceptedFiles.length > 1) {
+ toast.error("Only one PDF can be uploaded at a time.", {
+ id: toastId,
+ });
+ return;
+ }
- if (acceptedFiles.length > 5) {
- toast.error("More than 5 files selected", {
- id: toastId,
- });
- return;
- }
+ if (isNewPdf && hasExistingImages) {
+ toast.error("PDFs cannot be uploaded together with images.", {
+ id: toastId,
+ });
+ return;
+ }
- const invalidFiles = acceptedFiles.filter(
- (file) =>
- file.size > maxFileSize || !allowedFileTypes.includes(file.type),
- );
- if (invalidFiles.length > 0) {
- toast.error(
- `Some files are invalid. Ensure each file is below 5MB and of an allowed type (PDF, JPEG, PNG, GIF).`,
- {
+ if (isNewPdf && hasExistingPdf) {
+ toast.error("Only one PDF is allowed. You’ve already uploaded a PDF.", {
id: toastId,
- },
+ });
+ return;
+ }
+
+ if (!isNewPdf && hasExistingPdf) {
+ toast.error(
+ "Images cannot be uploaded after a PDF. Upload them separately.",
+ {
+ id: toastId,
+ },
+ );
+ return;
+ }
+
+ const allFiles = [...files, ...acceptedFiles];
+ if (allFiles.length > 5) {
+ toast.error("You can upload up to 5 files only", { id: toastId });
+ return;
+ }
+
+ const invalidFiles = acceptedFiles.filter(
+ (file) =>
+ file.size > maxFileSize || !allowedFileTypes.includes(file.type),
);
- return;
- }
- const isPdf = acceptedFiles.reduce(
- (reducer, file) => file.type === "application/pdf" || reducer,
- false,
+ if (invalidFiles.length > 0) {
+ toast.error(
+ "Some files are invalid. Make sure each is under 5MB and of allowed types (PDF, JPEG, PNG, GIF).",
+ { id: toastId },
+ );
+ return;
+ }
+
+ const newPreviews = acceptedFiles.map((file, idx) => ({
+ id: `${file.name}-${file.lastModified}-${Date.now()}-${files.length + idx}`,
+ file,
+ preview: URL.createObjectURL(file),
+ }));
+
+ setFiles((prev) => [...prev, ...acceptedFiles]);
+ setPreviews((prev) => [...prev, ...newPreviews]);
+
+ toast.success(`${acceptedFiles.length} file(s) added!`, { id: toastId });
+ },
+ [files],
+ );
+
+ const onDrop = useCallback(
+ (acceptedFiles: File[]) => {
+ fileCheckAndSelect(acceptedFiles);
+ },
+ [fileCheckAndSelect],
+ );
+
+ const sensors = useSensors(
+ useSensor(PointerSensor, {
+ activationConstraint: { distance: 5 },
+ }),
+ useSensor(TouchSensor, {
+ activationConstraint: {
+ delay: 200,
+ tolerance: 10,
+ },
+ }),
+ );
+
+ function SortablePreview({
+ id,
+ children,
+ }: {
+ id: string;
+ children: React.ReactNode;
+ }) {
+ const {
+ attributes,
+ listeners,
+ setNodeRef,
+ transform,
+ transition,
+ isDragging,
+ } = useSortable({ id });
+
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ zIndex: isDragging ? 1000 : 1,
+ touchAction: "none", // Prevent scrolling during touch drag
+ };
+
+ return (
+
+ {children}
+
);
- if (isPdf && acceptedFiles.length > 1) {
- toast.error("PDFs must be uploaded separately", {
- id: toastId,
- });
- return;
+ }
+
+ const handleDndKitDragEnd = (event: DragEndEvent) => {
+ const { active, over } = event;
+ if (over && active.id !== over.id) {
+ const oldIndex = previews.findIndex((item) => item.id === active.id);
+ const newIndex = previews.findIndex((item) => item.id === over.id);
+
+ const newPreviews = arrayMove(previews, oldIndex, newIndex);
+ const newFiles = arrayMove(files, oldIndex, newIndex);
+
+ setFiles(newFiles);
+ setPreviews(newPreviews);
}
+ };
- const orderedFiles = acceptedFiles.sort((a, b) => {
- return a.lastModified - b.lastModified;
- });
- setFiles(orderedFiles);
- toast.success(`${orderedFiles.length} files selected!`, {
- id: toastId,
+ const handleDelete = (index: number) => {
+ const deletedPreview = previews[index];
+ if (deletedPreview) {
+ URL.revokeObjectURL(deletedPreview.preview);
+ }
+
+ const remainingFiles = files.filter((_, i) => i !== index);
+ const remainingPreviews = previews.filter((_, i) => i !== index);
+
+ setFiles(remainingFiles);
+ setPreviews(remainingPreviews);
+ };
+
+ const clearAllFiles = useCallback(() => {
+ previews.forEach((item) => {
+ try {
+ URL.revokeObjectURL(item.preview);
+ } catch (e) {}
});
- }
- const handlePrint = async () => {
- if (!campus) {
- setCampus("Vellore");
- }
+ setFiles([]);
+ setPreviews([]);
+ }, [previews]);
+ const handlePrint = async () => {
const isPdf = files.length === 1 && files[0]?.type === "application/pdf";
const formData = new FormData();
@@ -146,7 +277,7 @@ const Page = () => {
if (error instanceof AxiosError && error.response?.data) {
const errorData = error.response.data as APIResponse;
const errorMessage =
- errorData.message || "Failed to upload papers";
+ errorData.message ?? "Failed to upload papers";
throw new Error(errorMessage);
}
throw new Error("Failed to upload papers");
@@ -161,98 +292,249 @@ const Page = () => {
},
);
- setFiles([]);
- setResetSearch(true);
- setTimeout(() => setResetSearch(false), 100);
+ clearAllFiles();
} catch (error) {
- handleAPIError(error);
} finally {
setIsUploading(false);
}
};
- const isCurrentlyDragging = isDragging || isGlobalDragging;
-
return (
-
-
-
+
+
Our Projects
+ Papers
+ Contactify
+ FFCS-iniator
+
+
{/* Projects */}
@@ -121,6 +128,7 @@ export default function Footer() {
codechefvit@gmail.com
+
Subscribe For Updates:
diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx
index 2fb3e05..368854e 100644
--- a/src/components/SideBar.tsx
+++ b/src/components/SideBar.tsx
@@ -3,12 +3,8 @@
import React from "react";
import { Filter } from "lucide-react";
import { type Filters, type IPaper } from "@/interface";
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from "@/components/ui/accordion";
+import SidebarButton from "./SidebarButton";
+import SidebarSection from "./SidebarSection";
function SideBar({
selectedExams,
@@ -43,27 +39,18 @@ function SideBar({
years: string[],
campus: string[],
semester: string[],
- anskey: boolean,
+ anskey: boolean
) => void;
handleSelectAll: () => void;
handleDeselectAll: () => void;
handleDownloadSelected: () => void;
}) {
const exams =
- filterOptions?.unique_exams.map((exam) => ({
- label: exam,
- value: exam,
- })) ?? [];
+ filterOptions?.unique_exams.map((exam) => ({ label: exam, value: exam })) ?? [];
const slots =
- filterOptions?.unique_slots.map((slot) => ({
- label: slot,
- value: slot,
- })) ?? [];
+ filterOptions?.unique_slots.map((slot) => ({ label: slot, value: slot })) ?? [];
const years =
- filterOptions?.unique_years.map((year) => ({
- label: year,
- value: year,
- })) ?? [];
+ filterOptions?.unique_years.map((year) => ({ label: year, value: year })) ?? [];
const semesters =
filterOptions?.unique_semesters.map((semester) => ({
label: semester,
@@ -82,7 +69,7 @@ function SideBar({
selectedYears,
selectedCampuses,
selectedSemesters,
- selectedAnswerKeyIncluded,
+ selectedAnswerKeyIncluded
),
},
{
@@ -96,7 +83,7 @@ function SideBar({
selectedYears,
selectedCampuses,
selectedSemesters,
- selectedAnswerKeyIncluded,
+ selectedAnswerKeyIncluded
),
},
{
@@ -110,7 +97,7 @@ function SideBar({
newVal,
selectedCampuses,
selectedSemesters,
- selectedAnswerKeyIncluded,
+ selectedAnswerKeyIncluded
),
},
{
@@ -124,7 +111,7 @@ function SideBar({
selectedYears,
selectedCampuses,
newVal,
- selectedAnswerKeyIncluded,
+ selectedAnswerKeyIncluded
),
},
];
@@ -136,78 +123,42 @@ function SideBar({
Filters
-
-
{
- handleApplyFilters([], [], [], [], [], false);
- }}
- >
- Reset Filters
-
-
+
handleApplyFilters([], [], [], [], [], false)}>
+ Reset Filters
+
-
{
+
handleApplyFilters(
selectedExams,
selectedSlots,
selectedYears,
selectedCampuses,
selectedSemesters,
- !selectedAnswerKeyIncluded,
- );
- }}
- className={`flex cursor-pointer rounded-full border-2 border-black px-2 py-1 font-play text-xs font-semibold hover:bg-slate-800 hover:text-white ${
- selectedAnswerKeyIncluded
- ? "border-[#B2B8FF] bg-[#B2B8FF] hover:border-black hover:bg-[#B2B8FF] dark:border-[#434dba] dark:bg-[#434dba] dark:hover:border-[white] dark:hover:bg-[#434dba]"
- : "bg-none hover:bg-[#B2B8FF] dark:border-white dark:hover:border-[#434dba]"
- }`}
+ !selectedAnswerKeyIncluded
+ )
+ }
>
Answer Key Available
-
-
+
+
+