diff --git a/app/student/profile/page.tsx b/app/student/profile/page.tsx index 9a38f13b..3e526852 100644 --- a/app/student/profile/page.tsx +++ b/app/student/profile/page.tsx @@ -364,7 +364,6 @@ export default function ProfilePage() { ref={profileEditorRef} initialTab="Student Profile" initialOpenSections={openProfileSections} - actionSlot={editActions} resumeManager={resumeManager} /> diff --git a/components/ui/autocomplete.tsx b/components/ui/autocomplete.tsx index 494daa2c..1dc92583 100644 --- a/components/ui/autocomplete.tsx +++ b/components/ui/autocomplete.tsx @@ -60,6 +60,10 @@ function AutocompleteBase({ const inputRef = useRef(null); const sheetInputRef = useRef(null); const lastSelectionRef = useRef(0); + const [sheetViewport, setSheetViewport] = useState<{ + top: number; + height: number; + } | null>(null); const inputId = useId(); @@ -182,6 +186,30 @@ function AutocompleteBase({ return () => window.clearTimeout(focusTimer); }, [isOpen, useMobileSheet]); + // track visual viewport for autocomplete mobile popup + useEffect(() => { + if (!isOpen || !useMobileSheet) { + setSheetViewport(null); + return; + } + + const updateViewport = () => { + const top = window.visualViewport?.offsetTop ?? 0; + const height = window.visualViewport?.height ?? window.innerHeight; + setSheetViewport({ top, height }); + }; + + updateViewport(); + + window.visualViewport?.addEventListener("resize", updateViewport); + window.visualViewport?.addEventListener("scroll", updateViewport); + + return () => { + window.visualViewport?.removeEventListener("resize", updateViewport); + window.visualViewport?.removeEventListener("scroll", updateViewport); + }; + }, [isOpen, useMobileSheet]); + const suppressMobileKeyboard = isMobile && mobileDropdownMode === "inline" && !allowCustomValue; @@ -374,12 +402,15 @@ function AutocompleteBase({ className={cn( "overflow-y-auto overscroll-contain bg-white text-sm shadow-lg ring-1 ring-black ring-opacity-5", useMobileSheet - ? "fixed bottom-0 left-0 right-0 z-[1100] mt-0 max-h-[70vh] rounded-t-[0.33em] border border-b-0 border-gray-200 pb-3 shadow-2xl" + ? "fixed left-0 right-0 z-[1100] mt-0 rounded-t-[0.33em] border border-b-0 border-gray-200 pb-3 shadow-2xl" : "absolute left-0 right-0 z-50 mt-1 max-h-[400px] rounded-[0.33em] py-1", )} style={ - useInlineMobileDropdown && dropdownMaxHeight !== undefined - ? { maxHeight: dropdownMaxHeight } + useMobileSheet && sheetViewport + ? { + top: sheetViewport.top, + maxHeight: sheetViewport.height, + } : undefined } > @@ -597,6 +628,10 @@ export function AutocompleteTreeMulti({ }); const rootRef = useRef(null); const inputRef = useRef(null); + const [sheetViewport, setSheetViewport] = useState<{ + top: number; + height: number; + } | null>(null); // Build ID -> label map (child shows "Parent ยท Child") const labelMap = useMemo(() => { @@ -738,6 +773,30 @@ export function AutocompleteTreeMulti({ }; }, [isOpen, useInlineMobileDropdown]); + // track visual viewport for autocomplete mobile popup + useEffect(() => { + if (!isOpen || !useMobileSheet) { + setSheetViewport(null); + return; + } + + const updateViewport = () => { + const top = window.visualViewport?.offsetTop ?? 0; + const height = window.visualViewport?.height ?? window.innerHeight; + setSheetViewport({ top, height }); + }; + + updateViewport(); + + window.visualViewport?.addEventListener("resize", updateViewport); + window.visualViewport?.addEventListener("scroll", updateViewport); + + return () => { + window.visualViewport?.removeEventListener("resize", updateViewport); + window.visualViewport?.removeEventListener("scroll", updateViewport); + }; + }, [isOpen, useMobileSheet]); + return (
diff --git a/cypress.config.ts b/cypress.config.ts deleted file mode 100644 index a552f67d..00000000 --- a/cypress.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "cypress"; - -export default defineConfig({ - e2e: { - setupNodeEvents(on, config) {}, - }, -}); diff --git a/cypress/e2e/createjob.cy.ts b/cypress/e2e/createjob.cy.ts deleted file mode 100644 index 7529b032..00000000 --- a/cypress/e2e/createjob.cy.ts +++ /dev/null @@ -1,116 +0,0 @@ -it("Employer make a new Job", function () { - //cy.loginAsEmployer(); - cy.visit("http://hire.localhost:3000/dashboard"); - cy.wait(12000); - //cy.visit("http://hire.localhost:3000/dashboard"); - - cy.contains("Add Listing").click(); - cy.url().should("include", "/listings/create"); - cy.get('input[placeholder="Enter job title here..."]').type("cypress test job listing",); - cy.get("div.max-w-5xl").should("contain", "cypress test job listing"); - - // Look for - cy.contains("Credited Interns (Practicum)").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - // cy.contains("Publish Listing").should("be.disabled"); - - cy.contains("Voluntary Interns").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.get('input[placeholder="Enter job location here..."]').clear().type("cypress test location",); - cy.get('input[placeholder="Enter job location here..."]').should('have.value', 'cypress test location'); - - cy.contains("Part-time").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Full-time").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Flexible/Project-based").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("On-site").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Hybrid").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Remote").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - //Make sure toggles work correctly by toggling twice - cy.contains("No").parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Yes").parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("No").parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Yes").parent().within(() => { - cy.get("[data-state=unchecked]") - }); - - cy.contains("Description").parent().parent().within(() => { - cy.get('div[aria-label="editable markdown"]').type("This is a cypress test job description."); - }); - - cy.contains("Publish Listing").should("be.disabled"); - - cy.contains("Requirements").parent().within(() => { - cy.get('div[aria-label="editable markdown"]').type("Requirements for cypress test job."); - }); - - cy.contains("GitHub Repository").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("portfolio").parent().parent().within(() => { - cy.get("[data-state=unchecked]") - cy.get("button").click() - cy.get("[data-state=checked]") - }); - - cy.contains("Publish Listing").should("be.enabled").click(); - - cy.url().should("include", "/dashboard"); - cy.contains("cypress test job listing"); - -}); diff --git a/cypress/e2e/deletejob.cy.ts b/cypress/e2e/deletejob.cy.ts deleted file mode 100644 index fdd4a280..00000000 --- a/cypress/e2e/deletejob.cy.ts +++ /dev/null @@ -1,14 +0,0 @@ -describe('Employer delete a Job', () => { - it('Delete the job', () => { - //cy.loginAsEmployer(); - cy.visit("http://hire.localhost:3000/dashboard"); - cy.wait(12000); - //cy.visit("http://hire.localhost:3000/dashboard"); - - cy.contains("cypress test job listing").click(); - cy.contains("Delete").click(); - cy.contains("Are you sure you want to delete").parent().within(() => { - cy.contains("Delete").click(); - }); - }) -}) \ No newline at end of file diff --git a/cypress/e2e/login.cy.ts b/cypress/e2e/login.cy.ts deleted file mode 100644 index 5b1a1949..00000000 --- a/cypress/e2e/login.cy.ts +++ /dev/null @@ -1,10 +0,0 @@ -it('prevents invalid credentials from logging in', () => { - cy.visit('http://hire.localhost:3000/login') - - //NEED TO FIX - cy.get('input[placeholder="Email Address"]').type("fakeemail@gmail.com") - cy.get('input[placeholder="Password..."]').type("fakepassword") - cy.get('button[type="submit"]').click() - - cy.contains("Invalid password.") -}) \ No newline at end of file diff --git a/cypress/e2e/search.cy.ts b/cypress/e2e/search.cy.ts deleted file mode 100644 index 58e55c66..00000000 --- a/cypress/e2e/search.cy.ts +++ /dev/null @@ -1,15 +0,0 @@ -it("Bring the user to the Internship Dashboard and search for Intestship", function () { - cy.visit("http://localhost:3000/"); -//NEED TO FIX - cy.contains("Find Internships").click(); - - cy.url().should("include", "/search"); - - cy.get('input[placeholder="Search listings"]').type( - "cypress test job listing{enter}", - ); - - cy.contains("cypress test job listing").should("exist").click(); - - cy.get("div.flex-1").should("contain", "cypress test job listing"); -}); diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json deleted file mode 100644 index 02e42543..00000000 --- a/cypress/fixtures/example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io", - "body": "Fixtures are a great way to mock data for responses to routes" -} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index d4aeafda..00000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,16 +0,0 @@ -/// - -Cypress.Commands.add("loginAsEmployer", () => { - cy.request({ - method: "POST", - url: "http://localhost:5000/api/auth/hire/login", - body: { - email: "yoy424051@gmail.com", - password: "we have one of the strongest passwords ever lol", - }, - }).then((resp) => { - const token = (resp.body as { token: string }).token; - console.log(resp.body); - cy.setCookie("employer-user-token", token, { secure: true, path: "/" }); - }); -}); diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts deleted file mode 100644 index e4e246ec..00000000 --- a/cypress/support/e2e.ts +++ /dev/null @@ -1,17 +0,0 @@ -// *********************************************************** -// This example support/e2e.ts is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: -import './commands' \ No newline at end of file diff --git a/cypress/support/index.d.ts b/cypress/support/index.d.ts deleted file mode 100644 index 65663156..00000000 --- a/cypress/support/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare namespace Cypress { - interface Chainable { - loginAsEmployer(): Chainable; - } -} diff --git a/next.config.mjs b/next.config.mjs index ec4ba456..2a1ed6e0 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -43,12 +43,12 @@ const imageOrigins = [ const cspHeader = ` default-src 'self'; - script-src 'self' 'unsafe-eval' 'unsafe-inline' https://cdnjs.cloudflare.com; + script-src 'self' 'unsafe-eval' 'unsafe-inline' https://cdnjs.cloudflare.com https://*.posthog.com; frame-src 'self' http://localhost:* ${connectOrigins}; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' blob: data: http://localhost:* ${imageOrigins}; font-src 'self' https://fonts.gstatic.com; - connect-src 'self' http://localhost:* https://storage.googleapis.com ${connectOrigins}; + connect-src 'self' http://localhost:* https://storage.googleapis.com https://*.posthog.com ${connectOrigins}; object-src 'none'; base-uri 'self'; form-action 'self'; diff --git a/package.json b/package.json index f00a2994..0c4715ce 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,6 @@ "@types/pg": "^8.20.0", "@types/react": "^19", "@types/react-dom": "^19", - "cypress": "^15.6.0", "eslint": "^9.18.0", "eslint-config-prettier": "^10.0.1", "eslint-plugin-prettier": "^5.2.2",