Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
7306d16
feat: finalize applicant statuses when arching applicants and stuff
neue-dev Sep 15, 2026
1e3ec22
feat: add PDF preview functionality and improve document loading in M…
anaj00 Sep 19, 2026
803d2ca
fix: remove unused applicant props + format grad date correctly
neue-dev Sep 19, 2026
fa7b243
fix: export correct metadata for pages acc. to next
neue-dev Sep 19, 2026
d97a27f
fix: page exports
neue-dev Sep 19, 2026
dc3c49f
fix: ignoreBuildErrors: false
neue-dev Sep 19, 2026
90a04b8
Chore/surface build errors (#570)
neue-dev Sep 19, 2026
d427ab3
feat: enable toolbar in PDF viewer component
anaj00 Sep 19, 2026
be3c385
Merge branch 'develop' of https://github.com/BetterInternship/Client …
anaj00 Sep 19, 2026
317964c
fix: make PDF preview responsive
anaj00 Sep 19, 2026
03ace72
feat: redesign applicant review experience
anaj00 Sep 19, 2026
b07854d
feat: add listing preview metadata
anaj00 Sep 19, 2026
8346fce
feat: enhance ApplicationRow component with improved internship type …
anaj00 Sep 19, 2026
f329059
fix: update status ordering logic in ApplicationsContent component
anaj00 Sep 19, 2026
44ca8f7
Merge branch 'develop' into refactor/component-unification
jayylmao Sep 20, 2026
4e22c88
style(student): rm bottom bar nav on mobile
jayylmao Sep 20, 2026
ac74d10
chore: get rid of old conversations infrastructure
neue-dev Sep 20, 2026
e9e8dc0
chore: get rid of unnecessary job service test
neue-dev Sep 20, 2026
3f7c208
chore: get rid of unused god mass apply modal and god api routes
neue-dev Sep 20, 2026
102f26e
chore: remove old auth methods for password-based student login
neue-dev Sep 20, 2026
a5e53a6
chore: remove unused modals
neue-dev Sep 20, 2026
927518d
chore: remove unused application routes; application withdrawal to be…
neue-dev Sep 20, 2026
db44bc9
chore: rm unused form template route
neue-dev Sep 20, 2026
7a53676
chore: rm deprecated tin iom link
neue-dev Sep 20, 2026
425e1f6
chore: rm unused query keys for deprecated chat feat
neue-dev Sep 20, 2026
40b120e
Chore/deprecation cleanup (#571)
neue-dev Sep 20, 2026
8f3e593
chore: rm jest step in ci/cd
neue-dev Sep 20, 2026
268bdf9
chore: rm jest step in ci/cd (#572)
neue-dev Sep 20, 2026
4094c7c
chore: remove all jest files
neue-dev Sep 20, 2026
9640fc2
chore: redeploy
neue-dev Sep 20, 2026
8b6693e
style(student): rm top bar on student listing page
jayylmao Sep 21, 2026
189ece3
Merge branch 'develop' into refactor/component-unification
jayylmao Sep 21, 2026
e4cc49f
style(student): hide mobile navigation when unneeded on mobile (#573)
jayylmao Sep 21, 2026
54743d8
feat(employers): add notification icon for users not receiving applic…
anaj00 Sep 21, 2026
c4a2f4e
feat(verification): implement OTP cooldown management and session sto…
anaj00 Sep 22, 2026
172b6b4
Merge branch 'feat/finalize-applicants' into develop
anaj00 Sep 22, 2026
05b1877
refactor: remove unused Archive import and label from ApplicantPage a…
anaj00 Sep 22, 2026
fc7f05f
refactor(create/edit job): discard modal visual consistency
jayylmao Sep 22, 2026
e3bd07c
chore(package): bump package version
jayylmao Sep 22, 2026
b59a868
feat(dropdown): add mobile-optimized layout
jayylmao Sep 22, 2026
1cf0ce5
refactor(create/edit job): discard modal visual consistency
jayylmao Sep 22, 2026
ea94e10
refactor(create/edit job): discard modal visual consistency
jayylmao Sep 22, 2026
fc3e7a3
refactor(create/edit job): discard modal visual consistency
jayylmao Sep 22, 2026
8151077
style(job details): rm extra top padding to increase info density
jayylmao Sep 22, 2026
6675f2f
style(job not found): use unified component for not found error
jayylmao Sep 22, 2026
7c4d339
chore(job page): rm unnecessary import
jayylmao Sep 22, 2026
3b401e5
style(applicant page): rm unnecessary vertical padding to improve info
jayylmao Sep 22, 2026
3e8ac1f
style(applicants list): improve display on no applicants
jayylmao Sep 22, 2026
f780536
feat(recruiter job page): move navigation into menu to improve info
jayylmao Sep 22, 2026
f3ad244
chore: pass job into layout for job share cta
jayylmao Sep 22, 2026
4c6e22e
refactor(create/edit job): discard modal visual consistency
jayylmao Sep 22, 2026
591d826
style(job details): improve info density by removing vertical padding
jayylmao Sep 22, 2026
cbfa43e
feat(recruit): minimize mobile user chrome (#574)
jayylmao Sep 22, 2026
868bc76
fix(student): breakpoint detection for mobile bottom nav with top nav
jayylmao Sep 22, 2026
d1fa9ff
fix(student): breakpoint detection for mobile bottom nav with top nav…
jayylmao Sep 22, 2026
ab4f50a
refactor(hire): rm nested scroll contexts
jayylmao Sep 22, 2026
3933e3b
fix(my jobs): spacing with simplified scroll context
jayylmao Sep 22, 2026
7521143
refactor(student): rm nested scroll contexts
jayylmao Sep 22, 2026
2a4f9b4
fix(job header): fix positioning on simplified scroll context
jayylmao Sep 22, 2026
1df7414
fix(recruit admin): inconsistent mobile detection for admin button
jayylmao Sep 22, 2026
8e7b62e
fix(search): scrollbar not being flush on the right side of the window
jayylmao Sep 22, 2026
ebefc5f
fix(student): student header not pinning on simplified scroll context
jayylmao Sep 22, 2026
29fcdaa
fix(search): scrollbar not flush against the right
jayylmao Sep 22, 2026
2cc3846
chore(package): bump components package ver
jayylmao Sep 22, 2026
131c76c
fix(mobile): top nav not displaying on ipad (#576)
jayylmao Sep 22, 2026
07c6b8d
feat(seo): make dedicated listing page visible to crawlers from search
jayylmao Sep 24, 2026
2719c2f
fix: shared cache leak between clients
jayylmao Sep 24, 2026
4c6cae7
feat: cache useRefs data to reduce db calls
jayylmao Sep 24, 2026
d67ba0c
refactor: move baseurl function for seo to own file
jayylmao Sep 24, 2026
9a3751e
feat(seo): add crawler rules generator
jayylmao Sep 24, 2026
d815f61
feat(seo): add sitemap generator
jayylmao Sep 24, 2026
3609260
feat(seo): add metadata to listing page
jayylmao Sep 24, 2026
303ef53
feat(seo): add unlisted metadata to job listing
jayylmao Sep 24, 2026
e664608
feat(seo): add search layout page for metadata
jayylmao Sep 24, 2026
be362c1
feat(seo): server-side search page rendering
jayylmao Sep 24, 2026
e6a142f
chore(package): update db schema types
jayylmao Sep 24, 2026
770c348
fix: robots txt 404
jayylmao Sep 24, 2026
96a572f
chore: ignore postman files
jayylmao Sep 24, 2026
729c687
feat(seo): add schema to each job listing for crawlers
jayylmao Sep 24, 2026
077236b
chore: add robots.txt to rewrite exclusion
jayylmao Sep 24, 2026
b7f99fa
feat(shortlinks): add employer-portal short link resolver
anaj00 Sep 25, 2026
33f0698
chore: redeploy
anaj00 Sep 25, 2026
664990b
patch: remove double header on job modal
neue-dev Sep 25, 2026
ef399c3
chore: remove outdated components and types related to the Sofi AI su…
anaj00 Sep 25, 2026
8045764
Merge branch 'develop' of https://github.com/BetterInternship/Client …
anaj00 Sep 25, 2026
cd88885
fix: nextjs build issue with google fonts
neue-dev Sep 25, 2026
ccd3c3b
fix: modal layout and sizing after applying
neue-dev Sep 25, 2026
ba3d421
fix: mangled job description in metadata
jayylmao Sep 25, 2026
52b9f96
feat(seo): send request to not index dev/admin sites
jayylmao Sep 25, 2026
48f4df8
fix(seo): link previews not working
jayylmao Sep 25, 2026
213d9ee
fix(seo): super listing sites not indexing properly
jayylmao Sep 25, 2026
0188b87
fix(seo): forgor iom admin is under a diff repo lol
jayylmao Sep 25, 2026
172446d
fix(seo): include super listing sites and remove invalid company pages
jayylmao Sep 25, 2026
d2706cc
fix(search): back button in listing page goes to search when no in-app
jayylmao Sep 25, 2026
9ed2027
fix(search): broken ctrl-click job behavior
jayylmao Sep 25, 2026
2a304fc
fix: incorrect markdown->html conversion with server util causing crash
jayylmao Sep 25, 2026
963119e
feat: seo optimization (#577)
jayylmao Sep 25, 2026
a7fb265
feat: add refresh functionality for employer login metrics
anaj00 Sep 28, 2026
a4a6cef
fix(hire-auth-shell): improve layout and styling for better responsiv…
anaj00 Sep 28, 2026
a8cbbcf
feat: add footer to AllowLanding and adjust layout for better respons…
anaj00 Sep 28, 2026
b5b727a
fix: retain commented eslint directives for unused variables in Setup…
anaj00 Sep 28, 2026
0acd781
feat: enhance MobileBottomNav with responsive behavior and focus hand…
anaj00 Sep 28, 2026
2c99e99
fix: improve MobileBottomNav focus handling and update layout for bet…
anaj00 Sep 28, 2026
7be50e7
refactor: simplify MobileBottomNav by removing unused state and effects
anaj00 Sep 28, 2026
2a22804
fix: wrap Footer in a div for better responsive visibility on AllowLa…
anaj00 Sep 28, 2026
3a52ef3
Merge branch 'main' into develop
anaj00 Sep 28, 2026
73c9477
refactor: remove unused ArchiveRestore button from ApplicantPage
anaj00 Sep 28, 2026
7306814
Merge branch 'develop' of https://github.com/BetterInternship/Client …
anaj00 Sep 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ CLAUDE.md
AGENTS.md
.claude/
.claudeignore

# Postman files
.postman/
postman/
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,3 @@ npm run dev
```bash
npm run test
```

1 change: 0 additions & 1 deletion app/hire/%5F%5F/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const InternalSetupPage = () => {
await queryClient.invalidateQueries({ queryKey: ["my-profile"] });
await queryClient.invalidateQueries({ queryKey: ["my-applications"] });
await queryClient.invalidateQueries({ queryKey: ["my-saved-jobs"] });
await queryClient.invalidateQueries({ queryKey: ["my-conversations"] });
await queryClient.invalidateQueries({ queryKey: ["my-forms"] });
await queryClient.invalidateQueries({ queryKey: ["my-form-templates"] });
await queryClient.invalidateQueries({ queryKey: ["my-form-template"] });
Expand Down
1 change: 0 additions & 1 deletion app/hire/__/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const InternalSetupPage = () => {
await queryClient.invalidateQueries({ queryKey: ["my-profile"] });
await queryClient.invalidateQueries({ queryKey: ["my-applications"] });
await queryClient.invalidateQueries({ queryKey: ["my-saved-jobs"] });
await queryClient.invalidateQueries({ queryKey: ["my-conversations"] });
await queryClient.invalidateQueries({ queryKey: ["my-forms"] });
await queryClient.invalidateQueries({ queryKey: ["my-form-templates"] });
await queryClient.invalidateQueries({ queryKey: ["my-form-template"] });
Expand Down
12 changes: 8 additions & 4 deletions app/hire/allowLanding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@ export default function AllowLanding({
const hideHeader = isStudentLanding || isAuthRoute;

return (
<div className="h-svh flex flex-col overflow-y-auto">
<>
<Suspense>{!hideHeader && <HireAppHeader />}</Suspense>
<div className="grow overflow-auto flex flex-col ">{children}</div>
{!isStudentLanding && <Footer />}
</div>
<div className="flex min-h-0 grow flex-col">{children}</div>
{!isStudentLanding && (
<div className="hidden md:block">
<Footer />
</div>
)}
</>
);
}
24 changes: 24 additions & 0 deletions app/hire/auth/secure-access/[employerUserId]/[hash]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Forwards minted employer magic links to the API endpoint that consumes the
* keyring and sets the employer auth cookies.
*/
export async function GET(
request: Request,
{
params,
}: { params: Promise<{ employerUserId: string; hash: string }> },
) {
const { employerUserId, hash } = await params;
const apiBaseUrl = process.env.NEXT_PUBLIC_API_URL?.replace(/\/+$/, '');

if (!apiBaseUrl) {
return Response.redirect(new URL('/login', request.url), 307);
}

const apiUrl = new URL(
`${apiBaseUrl}/auth/secure-access/${encodeURIComponent(employerUserId)}/${encodeURIComponent(hash)}`,
);
apiUrl.search = new URL(request.url).search;

return Response.redirect(apiUrl, 307);
}
3 changes: 0 additions & 3 deletions app/hire/authctx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,6 @@ export const AuthContextProvider = ({

await Promise.all([
queryClient.invalidateQueries({ queryKey: ["my-employer-profile"] }),
queryClient.invalidateQueries({
queryKey: ["my-employer-conversations"],
}),
queryClient.invalidateQueries({ queryKey: ["me"] }),
queryClient.invalidateQueries({ queryKey: ["my-employer-team"] }),
]);
Expand Down
23 changes: 9 additions & 14 deletions app/hire/dashboard/applicant/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ function ApplicantPageContent() {
(a) => applicationId === a.id,
);
const otherUserApplications = applications?.employer_applications.filter(
(a) => a.user_id === userApplication?.user_id,
(a) =>
a.user_id === userApplication?.user_id && a.id !== userApplication?.id,
);
const jobId = userApplication?.job_id;
const userId = userApplication?.user_id;
Expand Down Expand Up @@ -68,8 +69,11 @@ function ApplicantPageContent() {
);

const getStatuses = () => {
const statusOrder = [0, 1, 4, 6];

return unique_app_statuses
.filter((status) => status.id !== 7 && status.id !== 5 && status.id !== 0)
.filter((status) => status.id !== 7 && status.id !== 5)
.sort((a, b) => statusOrder.indexOf(a.id) - statusOrder.indexOf(b.id))
.map((status): DropdownMenuItem => {
const config = DB_STATUS_MAP[status.id];

Expand All @@ -94,18 +98,9 @@ function ApplicantPageContent() {
application={userApplication}
statuses={getStatuses()}
userApplications={otherUserApplications}
onArchive={() => {
if (!userApplication) return;
if (userApplication.visibility === "archived") {
triggerAction("UNARCHIVE", [userApplication]);
} else {
triggerAction("ARCHIVE", [userApplication]);
}
}}
onDelete={() => {
if (!userApplication) return;
if (userApplication) triggerAction("DELETE", [userApplication]);
}}
onArchive={() =>
userApplication && triggerAction("ARCHIVE", [userApplication])
}
/>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/hire/dashboard/manage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function ManageContent() {
return (
<>
<JobHeader job={job} onJobUpdate={updateJob} backHref="/dashboard" />
<PageContainer>
<PageContainer className="py-0">
{profile.data?.is_verified === false ? (
<div className="flex flex-col items-center justify-center text-center gap-2 py-24 text-gray-500">
<Clock className="w-8 h-8 mb-2" />
Expand Down
4 changes: 4 additions & 0 deletions app/hire/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { StatusNotice } from "@betterinternship/components/status-notice";
import { Button } from "@betterinternship/components";
import { Pause, Plus } from "lucide-react";
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- retained with the commented banner
import { IomPartnershipCta } from "@/components/features/hire/iom-partnership-cta";

const NORMAL_LISTING_CREATE_PATH = "/listings/create";
Expand Down Expand Up @@ -76,6 +77,7 @@ function MagicLinkContinuation({
}

function DashboardContent() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- retained with the commented banner
const { isAuthenticated, redirectIfNotLoggedIn, user } = useAuthContext();
const router = useRouter();
const profile = useProfile();
Expand Down Expand Up @@ -108,12 +110,14 @@ function DashboardContent() {

return (
<PageContainer className="flex flex-col gap-2">
{/* MOA partnership banner temporarily disabled.
{profile.data && (
<IomPartnershipCta
profile={profile.data}
recruiterEmail={user?.email}
/>
)}
*/}
<PageHeader
title="Job listings"
description={`${activeJobs.length} active listings, ${inactiveJobs.length} inactive listings`}
Expand Down
7 changes: 7 additions & 0 deletions app/hire/god/employers/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useRouter, useSearchParams } from "next/navigation";
import { Suspense, useState, useCallback, useRef } from "react";
import { toast } from "sonner";
import { BellOff } from "lucide-react";
import { Button, Badge, Input } from "@betterinternship/components";
import {
ListShell,
Expand Down Expand Up @@ -166,6 +167,12 @@ function TeamEmailsList({
>
{m.email}
</span>
{!m.receives_applicant_digest && (
<BellOff
className="ml-1 inline-block size-3 text-destructive"
aria-label="Does not receive applicant digests"
/>
)}
</span>
))}
</span>
Expand Down
41 changes: 32 additions & 9 deletions app/hire/god/moa-uploads/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,33 @@ import {
useApproveMoaUpload,
useRejectMoaUpload,
useGodUniversities,
getGodMoaDocumentUrl,
MoaUpload,
} from "@/lib/api/god.api";
import { Paginator } from "@/components/ui/paginator";
import { PDFPreview } from "@/components/shared/pdf-preview";

const PAGE_SIZE = 20;

const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL;

function ModalShell({
open,
onClose,
title,
children,
className,
}: {
open: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
className?: string;
}) {
if (!open) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-xs">
<div className="bg-white rounded-[0.33em] border w-full max-w-xl mx-4 max-h-[90vh] overflow-auto">
<div
className={`bg-white rounded-[0.33em] border w-full mx-4 max-h-[90vh] overflow-auto ${className ?? "max-w-xl"}`}
>
<div className="flex items-center justify-between px-6 py-4 border-b">
<h2 className="text-lg font-semibold">{title}</h2>
<button
Expand Down Expand Up @@ -84,6 +88,7 @@ function GodMoaUploadsPageContent() {
const total = data?.total ?? 0;

const [approveTarget, setApproveTarget] = useState<MoaUpload | null>(null);
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [selectedUniversity, setSelectedUniversity] = useState("");
const defaultExpiry = useMemo(() => {
const d = new Date();
Expand Down Expand Up @@ -128,11 +133,20 @@ function GodMoaUploadsPageContent() {
}
};

const rows = uploads.map((u: MoaUpload) => {
const fileUrl = u.document_link
? `${API_BASE_URL}/god/moa-documents/${u.id}/file`
: null;
const handlePreview = async (moaId: string) => {
try {
const result = await getGodMoaDocumentUrl(moaId);
if (!result.success || !result.url) {
toast.error(result.error ?? "Failed to load MOA document.");
return;
}
setPreviewUrl(result.url);
} catch {
toast.error("Failed to load MOA document.");
}
};

const rows = uploads.map((u: MoaUpload) => {
return (
<RowCard
key={u.id}
Expand All @@ -146,13 +160,13 @@ function GodMoaUploadsPageContent() {
}
leftActions={
<>
{fileUrl && (
{u.document_link && (
<Button
scheme="primary"
size="xs"
onClick={(ev) => {
ev.stopPropagation();
window.open(fileUrl, "_blank");
void handlePreview(u.id);
}}
>
View PDF
Expand Down Expand Up @@ -250,6 +264,15 @@ function GodMoaUploadsPageContent() {
</div>
</ListShell>

<ModalShell
open={!!previewUrl}
onClose={() => setPreviewUrl(null)}
title="MOA Preview"
className="max-w-6xl"
>
{previewUrl && <PDFPreview url={previewUrl} />}
</ModalShell>

<ModalShell
open={!!approveTarget}
onClose={() => setApproveTarget(null)}
Expand Down
Loading
Loading