Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 11 additions & 2 deletions apps/member-profile/app/routes/_profile.offers.full-time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -328,9 +328,18 @@ export default function FullTimeOffersPage() {
type FullTimeOfferInView = SerializeFrom<typeof loader>['offers'][number];

function FullTimeOffersTable() {
const { offers } = useLoaderData<typeof loader>();
const { appliedCompany, offers } = useLoaderData<typeof loader>();
const [searchParams] = useSearchParams();

const hasOtherFilters =
!!searchParams.getAll('totalCompensation').length ||
!!searchParams.getAll('location').length;

const emptyMessage =
appliedCompany && !hasOtherFilters
? `No record exists for full-time offers at ${appliedCompany.name} yet.`
: 'No full-time offers found matching the criteria.';

const columns: TableColumnProps<FullTimeOfferInView>[] = [
{
displayName: 'Company',
Expand Down Expand Up @@ -394,7 +403,7 @@ function FullTimeOffersTable() {
<Table
columns={columns}
data={offers}
emptyMessage="No full-time offers found matching the criteria."
emptyMessage={emptyMessage}
rowTo={({ id }) => {
return {
pathname: generatePath(Route['/offers/full-time/:id'], { id }),
Expand Down
13 changes: 11 additions & 2 deletions apps/member-profile/app/routes/_profile.offers.internships.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,18 @@ export default function InternshipOffersPage() {
type InternshipOfferInView = SerializeFrom<typeof loader>['offers'][number];

function InternshipOffersTable() {
const { offers } = useLoaderData<typeof loader>();
const { appliedCompany, offers } = useLoaderData<typeof loader>();
const [searchParams] = useSearchParams();

const hasOtherFilters =
!!searchParams.getAll('hourlyRate').length ||
!!searchParams.getAll('location').length;

const emptyMessage =
appliedCompany && !hasOtherFilters
? `No record exists for internship offers at ${appliedCompany.name} yet.`
: 'No internship offers found matching the criteria.';

const columns: TableColumnProps<InternshipOfferInView>[] = [
{
displayName: 'Company',
Expand Down Expand Up @@ -370,7 +379,7 @@ function InternshipOffersTable() {
<Table
columns={columns}
data={offers}
emptyMessage="No internship offers found matching the criteria."
emptyMessage={emptyMessage}
rowTo={({ id }) => {
return {
pathname: generatePath(Route['/offers/internships/:id'], { id }),
Expand Down
14 changes: 11 additions & 3 deletions apps/member-profile/app/routes/_profile.offers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Outlet } from 'react-router';
import { Outlet, useSearchParams } from 'react-router';

import { cx, Dashboard } from '@oyster/ui';

Expand Down Expand Up @@ -27,14 +27,22 @@ type OffersNavigationProps = {
};

function OffersNavigation({ className }: OffersNavigationProps) {
const [searchParams] = useSearchParams();

const company = searchParams.get('company');

const search = company
? '?' + new URLSearchParams({ company }).toString()
: '';

return (
<nav className={cx('mr-auto', className)}>
<ul className="flex items-center gap-4">
<NavigationItem to={Route['/offers/internships']}>
<NavigationItem to={Route['/offers/internships'] + search}>
Internships
</NavigationItem>

<NavigationItem to={Route['/offers/full-time']}>
<NavigationItem to={Route['/offers/full-time'] + search}>
Full-Time
</NavigationItem>
</ul>
Expand Down
1 change: 1 addition & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 52 additions & 0 deletions changes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Changes

## Problem

In the Offers section, the company filter was dropped whenever you switched
between the Internships and Full-Time tabs. If you searched for a company on one
tab and clicked over to the other, the filter reset and you had to search for
that company again.

The cause was in `OffersNavigation` (`_profile.offers.tsx`). The two tab links
were bare pathnames, and React Router navigates a bare pathname with an empty
query string, so the `?company=` param was discarded and the destination loader
returned the unfiltered list.

## Solution

I made the tab links carry the current `company` param, so the filter survives a
tab switch and each tab shows that same company's offers.

I only forwarded `company`. The other params belong to a single list:
`hourlyRate` exists only on Internships, `totalCompensation` only on Full-Time,
and the `location` options are built per offer type. Forwarding those could
filter a tab by a value that isn't in its own dropdown. I dropped `page` too, so
you don't land on page 4 of a one-page list.

I also added a clearer empty state. When the carried-over company has no offers
of the type you're viewing, the table now says "No record exists for internship
offers at {company} yet." instead of the generic "no offers found matching the
criteria" copy, which reads like a failed search rather than missing data. It
only shows when the company is the only active filter, since otherwise the empty
result is probably caused by the other filters.

`appliedCompany` was already in both loader payloads, so I didn't need to change
any loaders or queries. I didn't write any styling, and I left the "x" on the
company pill untouched, so clearing the filter works exactly as it did before.

## Files changed

- `apps/member-profile/app/routes/_profile.offers.tsx` — imported
`useSearchParams` and appended `?company=` to both tab links in
`OffersNavigation`.
- `apps/member-profile/app/routes/_profile.offers.internships.tsx` —
destructured `appliedCompany`, computed `emptyMessage`, passed it to `Table`.
- `apps/member-profile/app/routes/_profile.offers.full-time.tsx` — same, with
full-time wording and the `totalCompensation` filter check.

## Testing

`type-check`, `lint` and `test` all pass. I verified in the browser that the
filter persists both directions, the new message shows for both offer types, a
company with data in both tabs is unaffected, and an unfiltered page renders
plain tab links.