From 742b527a6726291c80fb37c70a9c613d39f9c622 Mon Sep 17 00:00:00 2001 From: Emma Walker Date: Wed, 25 Feb 2026 16:00:19 -0800 Subject: [PATCH 01/17] backend blur --- frontend/app/api/notes/route.ts | 10 +- frontend/app/api/upload/helpers/preview.ts | 22 + frontend/app/api/upload/route.ts | 80 +- .../app/dashboard/course/[classId]/page.tsx | 14 +- frontend/app/globals.css | 14 +- frontend/next.config.ts | 10 +- frontend/package-lock.json | 68 +- frontend/package.json | 6 +- frontend/untitled.1.png | Bin 0 -> 62369 bytes package-lock.json | 681 +++++++++++++++++- package.json | 4 + 11 files changed, 871 insertions(+), 38 deletions(-) create mode 100644 frontend/app/api/upload/helpers/preview.ts create mode 100644 frontend/untitled.1.png diff --git a/frontend/app/api/notes/route.ts b/frontend/app/api/notes/route.ts index ac5b8af..d800c1e 100644 --- a/frontend/app/api/notes/route.ts +++ b/frontend/app/api/notes/route.ts @@ -203,15 +203,19 @@ export async function GET(req: Request) { download_cost: row.download_cost ?? 0, downloaded: downloadedIds.has(row.id), }; + + const previewPath = + row.preview_key && !row.preview_key.toLowerCase().endsWith(".pdf") + ? row.preview_key + : null; - const path = row.preview_key ?? row.file_key; - if (!path) { + if (!previewPath) { return { ...base, previewUrl: null }; } let previewUrl: string | null = null; try { - previewUrl = await generateSignedUrl("resources", path); + previewUrl = await generateSignedUrl("resources", previewPath); } catch { previewUrl = null; } diff --git a/frontend/app/api/upload/helpers/preview.ts b/frontend/app/api/upload/helpers/preview.ts new file mode 100644 index 0000000..52a0101 --- /dev/null +++ b/frontend/app/api/upload/helpers/preview.ts @@ -0,0 +1,22 @@ +//**Purpose:** Given a path to a PDF file on disk, render the first page to an image, blur it, and return a JPEG +//buffer. + +import sharp from "sharp"; +import { fromPath } from "pdf2pic"; + +const PREVIEW_WIDTH = 400; +const BLUR_SIGMA = 6; + +export async function generateBlurredFirstPageBuffer(pdfPath: string): Promise { + const convert = fromPath(pdfPath, { density: 150 }); + const result = await convert(1, { format: "png" }); + if (!result?.path) throw new Error("Failed to render PDF first page"); + + const blurred = await sharp(result.path) + .resize(PREVIEW_WIDTH) + .blur(BLUR_SIGMA) + .jpeg({ quality: 80 }) + .toBuffer(); + + return blurred; +} \ No newline at end of file diff --git a/frontend/app/api/upload/route.ts b/frontend/app/api/upload/route.ts index 33b6ee9..ecc9d2b 100644 --- a/frontend/app/api/upload/route.ts +++ b/frontend/app/api/upload/route.ts @@ -2,8 +2,14 @@ import { randomUUID } from "node:crypto"; import { NextRequest, NextResponse } from "next/server"; import { createClient } from "@supabase/supabase-js"; +import fs from "node:fs"; +import path from "node:path"; +import os from "node:os"; +import { generateBlurredFirstPageBuffer } from "./helpers/preview"; + const MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024; const PDF_MIME_TYPES = new Set(["application/pdf"]); +const STORAGE_ALLOWED_MIME_TYPES = new Set(["application/pdf", "image/jpeg"]); const STORAGE_BUCKET = "resources"; const RESOURCE_TYPES = new Set([ "lecture_notes", @@ -33,23 +39,53 @@ const buildFilePath = (userId: string, originalName: string) => { return `${userId}/${suffix}-${normalizedBase}.pdf`; }; +function buildPreviewPath(pdfFilePath: string): string { + const parts = pdfFilePath.split("/"); + const fileName = parts.pop() ?? ""; + const userId = parts[0] ?? "unknown"; + const baseName = fileName.replace(/\.pdf$/i, ""); + const uuidMatch = baseName.match( + /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i + ); + const id = uuidMatch ? uuidMatch[1] : baseName || randomUUID(); + return `previews/${userId}/${id}.jpg`; +} + const ensureResourcesBucket = async (adminClient: ReturnType) => { const { data: buckets, error: listError } = await adminClient.storage.listBuckets(); if (listError) { throw listError; } - const exists = buckets?.some((bucket) => bucket.name === STORAGE_BUCKET); - if (exists) return; + const existing = buckets?.find((bucket) => bucket.name === STORAGE_BUCKET); + const desiredMimeTypes = Array.from(STORAGE_ALLOWED_MIME_TYPES); + + if (!existing) { + const { error: createError } = await adminClient.storage.createBucket(STORAGE_BUCKET, { + public: false, + fileSizeLimit: `${MAX_FILE_SIZE_BYTES}`, + allowedMimeTypes: desiredMimeTypes, + }); - const { error: createError } = await adminClient.storage.createBucket(STORAGE_BUCKET, { + if (createError && !createError.message.toLowerCase().includes("already exists")) { + throw createError; + } + return; + } + + const existingMimeTypes = existing.allowed_mime_types ?? []; + const hasAllMimeTypes = desiredMimeTypes.every((mime) => existingMimeTypes.includes(mime)); + if (hasAllMimeTypes) { + return; + } + + const { error: updateError } = await adminClient.storage.updateBucket(STORAGE_BUCKET, { public: false, fileSizeLimit: `${MAX_FILE_SIZE_BYTES}`, - allowedMimeTypes: Array.from(PDF_MIME_TYPES), + allowedMimeTypes: desiredMimeTypes, }); - - if (createError && !createError.message.toLowerCase().includes("already exists")) { - throw createError; + if (updateError) { + throw updateError; } }; @@ -171,7 +207,33 @@ export async function POST(req: NextRequest) { }, }) : adminClient; - + + let previewKey: string | null = null; + const tmpPdf = path.join(os.tmpdir(), `upload-${randomUUID()}.pdf`); + try { + fs.writeFileSync(tmpPdf, fileBuffer); + const blurredBuffer = await generateBlurredFirstPageBuffer(tmpPdf); + const previewPath = buildPreviewPath(filePath); + const { error: previewUploadError } = await adminClient.storage + .from(STORAGE_BUCKET) + .upload(previewPath, blurredBuffer, { + cacheControl: "3600", + contentType: "image/jpeg", + upsert: false, + }); + if (previewUploadError) { + console.error("Preview upload failed", previewUploadError); + } else { + previewKey = previewPath; + } + } catch (previewErr) { + console.error("Preview generation failed", previewErr); + } finally { + if (fs.existsSync(tmpPdf)) { + fs.unlinkSync(tmpPdf); + } + } + const { data: resource, error: insertError } = await supabase .from("resources") .insert({ @@ -181,7 +243,7 @@ export async function POST(req: NextRequest) { resource_type: resourceType, description: description || null, file_key: filePath, - preview_key: filePath, + preview_key: previewKey, }) .select() .single(); diff --git a/frontend/app/dashboard/course/[classId]/page.tsx b/frontend/app/dashboard/course/[classId]/page.tsx index 25eefc2..77ae680 100644 --- a/frontend/app/dashboard/course/[classId]/page.tsx +++ b/frontend/app/dashboard/course/[classId]/page.tsx @@ -10,13 +10,14 @@ import { import { useParams, useRouter } from "next/navigation"; import Link from "next/link"; import { getSessionWithRecovery, supabase } from "@/lib/supabaseClient"; -import PDFThumbnail from "@/app/components/pdf/PDFThumbnail"; import { DesignNav } from "@/app/components/DesignNav"; import ProfileIcons from "../../profile-icon"; import { getCourseSubline } from "../../course-name-utils"; import "../../dashboard.css"; import "../../browse.css"; import "../../course-detail.css"; +import Image from "next/image"; + type CourseOption = { id: string; @@ -751,15 +752,18 @@ export default function CourseDetailPage() {
{selectedNote.previewUrl ? ( - ) : (
No preview available
- )} + )}

diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 51023a6..ab6805e 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -968,13 +968,17 @@ body { overflow: hidden; border: 1px solid #e2e8f0; background: #f1f5f9; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; } -.note-modal-preview > div, -.note-modal-preview > div > div, -.note-modal-preview canvas { - filter: blur(5px) !important; - transform: scale(1.05) !important; +.note-modal-preview img { + max-width: 100%; + height: 100%; + object-fit: cover; + display: block; } .note-modal-no-preview { diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa30..19df44b 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,15 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "**.supabase.co", + pathname: "/storage/v1/object/sign/**", + }, + ], + }, }; export default nextConfig; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 412f48a..ce4117a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,10 +10,12 @@ "dependencies": { "@supabase/supabase-js": "^2.83.0", "next": "16.0.3", + "pdf2pic": "^3.2.0", "pdfjs-dist": "4.8.69", "react": "19.2.0", "react-dom": "19.2.0", - "react-pdf": "9.1.0" + "react-pdf": "9.1.0", + "sharp": "^0.34.5" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -772,7 +774,6 @@ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", - "optional": true, "engines": { "node": ">=18" } @@ -3328,6 +3329,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-parallel": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", + "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==", + "license": "MIT" + }, + "node_modules/array-series": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", + "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==", + "license": "MIT" + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -4100,7 +4113,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -4311,7 +4323,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -5595,6 +5606,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gm": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/gm/-/gm-1.25.1.tgz", + "integrity": "sha512-jgcs2vKir9hFogGhXIfs0ODhJTfIrbECCehg38tqFgHm8zqXx7kAJyCYAFK4jTjx71AxrkFtkJBawbAxYUPX9A==", + "deprecated": "The gm module has been sunset. Please migrate to an alternative. https://github.com/aheckmann/gm?tab=readme-ov-file#2025-02-24-this-project-is-not-maintained", + "license": "MIT", + "dependencies": { + "array-parallel": "~0.1.3", + "array-series": "~0.1.5", + "cross-spawn": "^7.0.5", + "debug": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gm/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -6356,7 +6392,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -7798,7 +7833,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "devOptional": true, "license": "MIT" }, "node_modules/nan": { @@ -8340,7 +8374,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8363,6 +8396,22 @@ "node": ">=6" } }, + "node_modules/pdf2pic": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pdf2pic/-/pdf2pic-3.2.0.tgz", + "integrity": "sha512-p0bp+Mp4iJy2hqSCLvJ521rDaZkzBvDFT9O9Y0BUID3I04/eDaebAFM5t8hoWeo2BCf42cDijLCGJWTOtkJVpA==", + "license": "MIT", + "dependencies": { + "gm": "^1.25.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "paypal", + "url": "https://www.paypal.me/yakovmeister" + } + }, "node_modules/pdfjs-dist": { "version": "4.8.69", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.8.69.tgz", @@ -9185,7 +9234,6 @@ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", - "optional": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -9229,7 +9277,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", - "optional": true, "bin": { "semver": "bin/semver.js" }, @@ -9241,7 +9288,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -9254,7 +9300,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10298,7 +10343,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" diff --git a/frontend/package.json b/frontend/package.json index 09a1f87..ed2d627 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,10 +15,12 @@ "dependencies": { "@supabase/supabase-js": "^2.83.0", "next": "16.0.3", + "pdf2pic": "^3.2.0", "pdfjs-dist": "4.8.69", - "react-pdf": "9.1.0", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "react-pdf": "9.1.0", + "sharp": "^0.34.5" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/frontend/untitled.1.png b/frontend/untitled.1.png new file mode 100644 index 0000000000000000000000000000000000000000..91bc525cafc7108228c7baef7ca89964806c1503 GIT binary patch literal 62369 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" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/array-parallel": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz", + "integrity": "sha512-TDPTwSWW5E4oiFiKmz6RGJ/a80Y91GuLgUYuLd49+XBS75tYo8PNgaT2K/OxuQYqkoI852MDGBorg9OcUSTQ8w==", + "license": "MIT" + }, + "node_modules/array-series": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/array-series/-/array-series-0.1.5.tgz", + "integrity": "sha512-L0XlBwfx9QetHOsbLDrE/vh2t018w9462HM3iaFfxRiK83aJjAt/Ja3NMkOW7FICwWTlQBa3ZbL5FKhuQWkDrg==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/gm": { + "version": "1.25.1", + "resolved": "https://registry.npmjs.org/gm/-/gm-1.25.1.tgz", + "integrity": "sha512-jgcs2vKir9hFogGhXIfs0ODhJTfIrbECCehg38tqFgHm8zqXx7kAJyCYAFK4jTjx71AxrkFtkJBawbAxYUPX9A==", + "deprecated": "The gm module has been sunset. Please migrate to an alternative. https://github.com/aheckmann/gm?tab=readme-ov-file#2025-02-24-this-project-is-not-maintained", + "license": "MIT", + "dependencies": { + "array-parallel": "~0.1.3", + "array-series": "~0.1.5", + "cross-spawn": "^7.0.5", + "debug": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pdf2pic": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pdf2pic/-/pdf2pic-3.2.0.tgz", + "integrity": "sha512-p0bp+Mp4iJy2hqSCLvJ521rDaZkzBvDFT9O9Y0BUID3I04/eDaebAFM5t8hoWeo2BCf42cDijLCGJWTOtkJVpA==", + "license": "MIT", + "dependencies": { + "gm": "^1.25.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "paypal", + "url": "https://www.paypal.me/yakovmeister" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + } + } } diff --git a/package.json b/package.json index 0448bff..925eba5 100644 --- a/package.json +++ b/package.json @@ -8,5 +8,9 @@ "test": "npm --prefix frontend run test", "format": "npm --prefix frontend run format", "gitcheck": "node setup.mjs --skip-supabase --skip-env --no-start && npm --prefix frontend run lint && npm --prefix frontend run test" + }, + "dependencies": { + "pdf2pic": "^3.2.0", + "sharp": "^0.34.5" } } From 4508294e0f4a56ba6b1e16f19a9d1fbdbe72dec0 Mon Sep 17 00:00:00 2001 From: Noah Gullo Date: Wed, 25 Feb 2026 19:39:00 -0800 Subject: [PATCH 02/17] Department UI --- frontend/app/api/credits/route.ts | 24 +++++ frontend/app/dashboard/browse.css | 49 +++++++++++ frontend/app/dashboard/page.tsx | 87 ++++++++++++++++++- .../app/dashboard/profile-dashboard/page.tsx | 13 ++- .../202411100910_note_sharer_base_schema.sql | 14 +++ 5 files changed, 184 insertions(+), 3 deletions(-) diff --git a/frontend/app/api/credits/route.ts b/frontend/app/api/credits/route.ts index 8c8a38b..3a0dd71 100644 --- a/frontend/app/api/credits/route.ts +++ b/frontend/app/api/credits/route.ts @@ -48,10 +48,34 @@ export async function GET() { return NextResponse.json({ error: voucherError.message }, { status: 500 }); } + // count uploads rewarded to this profile + const { count: uploadCount, error: uploadError } = await supabase + .from("credits_ledger") + .select("id", { count: "exact", head: true }) + .eq("profile_id", user.id) + .eq("metadata->>reason", "upload_reward"); + + if (uploadError) { + return NextResponse.json({ error: uploadError.message }, { status: 500 }); + } + + // count upvotes cast by this profile + const { count: upvoteCount, error: upvoteError } = await supabase + .from("votes") + .select("id", { count: "exact", head: true }) + .eq("profile_id", user.id) + .eq("value", 1); + + if (upvoteError) { + return NextResponse.json({ error: upvoteError.message }, { status: 500 }); + } + return NextResponse.json( { credits: creditData?.credit_score ?? 0, freeDownloads: voucherCount ?? 0, + uploadCount: uploadCount ?? 0, + upvoteCount: upvoteCount ?? 0, }, { status: 200 }, ); diff --git a/frontend/app/dashboard/browse.css b/frontend/app/dashboard/browse.css index 326b876..8c5ab45 100644 --- a/frontend/app/dashboard/browse.css +++ b/frontend/app/dashboard/browse.css @@ -199,6 +199,55 @@ box-shadow: 0 0 0 2px rgba(109, 190, 139, 0.15); } +/* department request link & form */ +.browse-dept-request-link { + font-size: 14px; + color: var(--poly-accent); + text-decoration: underline; + cursor: pointer; +} + +.browse-department-request-form { + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 8px; +} + +.browse-department-request-form input { + height: 32px; + padding: 0 8px; + outline: none; + border: 1px solid rgb(86, 85, 85); + border-radius: 4px; + font-size: 14px; +} + +.browse-department-search input:focus { + border: 1px solid var(--poly-sage); + box-shadow: 0 0 0 2px rgba(109, 190, 139, 0.15); +} + +.browse-department-request-form button { + align-self: flex-start; + padding: 6px 12px; + background: var(--poly-sage); + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; +} + +.browse-request-success { + color: var(--poly-sage); + font-size: 14px; +} + +.browse-request-error { + color: var(--poly-danger, #e53e3e); + font-size: 14px; +} + .browse-filter-options { display: flex; flex-direction: column; diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index f060e58..0a53a4b 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -63,6 +63,14 @@ export default function DashboardPage() { const [freeDownloads, setFreeDownloads] = useState(null); /** Number of course cards to render (paginated for performance). */ const [visibleCourseCount, setVisibleCourseCount] = useState(80); + + // department submission UI state + const [showDeptRequestForm, setShowDeptRequestForm] = useState(false); + const [deptRequestName, setDeptRequestName] = useState(""); + const [deptRequestNumber, setDeptRequestNumber] = useState(""); + const [deptRequestStatus, setDeptRequestStatus] = useState< + "idle" | "loading" | "success" | "error" + >("idle"); /** When no department selected, whether the API has more courses to fetch. */ const [hasMoreFromApi, setHasMoreFromApi] = useState(false); /** Page enter animation (leaderboard-style). */ @@ -99,6 +107,41 @@ export default function DashboardPage() { const INITIAL_PAGE_SIZE = 200; const DEPARTMENT_PAGE_SIZE = 1000; + const submitDeptRequest = useCallback(async () => { + // simple validation + if (!deptRequestName.trim() || !deptRequestNumber.trim()) { + setDeptRequestStatus("error"); + return; + } + + setDeptRequestStatus("loading"); + try { + const { + data: { user }, + error: userErr, + } = await supabase.auth.getUser(); + if (userErr || !user) throw userErr || new Error("no user"); + + const { error } = await supabase.from("department_submissions").insert({ + submitter_id: user.id, + full_name: deptRequestName.trim(), + department_number: deptRequestNumber.trim(), + }); + + if (error) { + console.error("failed to insert department request", error); + setDeptRequestStatus("error"); + } else { + setDeptRequestStatus("success"); + setDeptRequestName(""); + setDeptRequestNumber(""); + } + } catch (err) { + console.error(err); + setDeptRequestStatus("error"); + } + }, [deptRequestName, deptRequestNumber]); + const fetchCoursesPage = useCallback( async ( token: string, @@ -354,7 +397,20 @@ export default function DashboardPage() { >

Filters

- Department + + Department{' '} + { + e.preventDefault(); + setShowDeptRequestForm((prev) => !prev); + setDeptRequestStatus("idle"); + }} + > + Don't see your department? Request it + +
+ {showDeptRequestForm && ( +
+ setDeptRequestName(e.target.value)} + /> + setDeptRequestNumber(e.target.value)} + /> + + {deptRequestStatus === "success" && ( + Request submitted! + )} + {deptRequestStatus === "error" && ( + Failed to submit + )} +
+ )}
{filteredDepartments.map((dept) => ( +
@@ -876,6 +965,101 @@ export default function DashboardPage() {
, document.body )} + + {isDepartmentRequestOpen && + typeof document !== "undefined" && + createPortal( +
+
event.stopPropagation()} + > +
+

+ Request a new department +

+ +
+
+ + +