Skip to content
Merged
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
4 changes: 2 additions & 2 deletions public/app.js

Large diffs are not rendered by default.

151 changes: 98 additions & 53 deletions src/helpers/load-meeting-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,60 +292,58 @@ export function loadMeetingData(
}

// day & time indexes
if (isActive) {
const weekday = settings.weekdays[
start?.weekday === 7
? 0
: (start?.weekday as keyof typeof settings.weekdays)
] as keyof typeof strings.days;

// day index
const dayIndex = indexes.weekday.findIndex(
({ key }) => key === weekday
const weekdayKey = settings.weekdays[
start?.weekday === 7
? 0
: (start?.weekday as keyof typeof settings.weekdays)
] as keyof typeof strings.days;

// day index
const dayIndex = indexes.weekday.findIndex(
({ key }) => key === weekdayKey
);
if (dayIndex === -1) {
indexes.weekday.push({
key: weekdayKey,
name: strings.days[weekdayKey],
slugs: [slug],
});
} else {
indexes.weekday[dayIndex].slugs.push(slug);
}

// time differences for sorting
const minutes_midnight = start.hour * 60 + start.minute;
minutes_week = minutes_midnight + meeting.day * 1440;

// time index (can be multiple)
const times = [];
if (minutes_midnight >= 240 && minutes_midnight < 720) {
times.push(0); // morning (4am–11:59pm)
}
if (minutes_midnight >= 660 && minutes_midnight < 1020) {
times.push(1); // midday (11am–4:59pm)
}
if (minutes_midnight >= 960 && minutes_midnight < 1260) {
times.push(2); // evening (4pm–8:59pm)
}
if (minutes_midnight >= 1200 || minutes_midnight < 300) {
times.push(3); // night (8pm–4:59am)
}
times.forEach(time => {
const timeIndex = indexes.time.findIndex(
({ key }) => key === settings.times[time]
);
if (dayIndex === -1) {
indexes.weekday.push({
key: weekday,
name: strings.days[weekday],
if (timeIndex === -1) {
indexes.time.push({
key: settings.times[time],
name: strings[settings.times[time]],
slugs: [slug],
});
} else {
indexes.weekday[dayIndex].slugs.push(slug);
}

// time differences for sorting
const minutes_midnight = start.hour * 60 + start.minute;
minutes_week = minutes_midnight + meeting.day * 1440;

// time index (can be multiple)
const times = [];
if (minutes_midnight >= 240 && minutes_midnight < 720) {
times.push(0); // morning (4am–11:59pm)
}
if (minutes_midnight >= 660 && minutes_midnight < 1020) {
times.push(1); // midday (11am–4:59pm)
}
if (minutes_midnight >= 960 && minutes_midnight < 1260) {
times.push(2); // evening (4pm–8:59pm)
}
if (minutes_midnight >= 1200 || minutes_midnight < 300) {
times.push(3); // night (8pm–4:59am)
indexes.time[timeIndex].slugs.push(slug);
}
times.forEach(time => {
const timeIndex = indexes.time.findIndex(
({ key }) => key === settings.times[time]
);
if (timeIndex === -1) {
indexes.time.push({
key: settings.times[time],
name: strings[settings.times[time]],
slugs: [slug],
});
} else {
indexes.time[timeIndex].slugs.push(slug);
}
});
}
});
} else {
const timeIndex = indexes.time.findIndex(
({ key }) => key === 'appointment'
Expand Down Expand Up @@ -384,13 +382,12 @@ export function loadMeetingData(
}

// build region index
if (isActive && !!regions.length) {
if (regions.length) {
indexes.region = populateRegionsIndex(regions, 0, indexes.region, slug);
}

// build type index (can be multiple) -- if inactive, only index the 'inactive' type
const typesToIndex: string[] = isActive ? types : ['inactive'];
typesToIndex
// build type index (can be multiple)
types
.filter(type => type in strings.types)
.forEach(type => {
const typeSlug = formatSlug(
Expand Down Expand Up @@ -535,6 +532,54 @@ export function loadMeetingData(

const slugs = Object.keys(meetings);

// Helper function to recursively filter out options where all meetings are inactive
const filterInactiveOnlyOptions = (indexArray: Index[]): Index[] => {
return indexArray
.map(index => {
// Check if at least one meeting in this index is active
const hasActiveMeeting = index.slugs.some(
slug => meetings[slug]?.isActive
);

// If no active meetings, filter out this option
if (!hasActiveMeeting) {
return null;
}

// Recursively filter children if they exist
if (index.children?.length) {
return {
...index,
children: filterInactiveOnlyOptions(index.children),
};
}

return index;
})
.filter((index): index is Index => index !== null);
};

// Metatypes that should always be visible regardless of active status
const specialMetaTypes = ['active', 'inactive', 'online', 'in-person'];

// Filter out inactive-only options from all indexes except special metatypes
indexes.region = filterInactiveOnlyOptions(indexes.region);
indexes.weekday = filterInactiveOnlyOptions(indexes.weekday);
indexes.time = filterInactiveOnlyOptions(indexes.time);
indexes.type = indexes.type
.map(index => {
// Preserve special metatypes
if (specialMetaTypes.includes(index.key)) {
return index;
}
// Check if at least one meeting in this index is active
const hasActiveMeeting = index.slugs.some(
slug => meetings[slug]?.isActive
);
return hasActiveMeeting ? index : null;
})
.filter((index): index is Index => index !== null);

// determine capabilities (filter out options that apply to every meeting)
const meetingsCount = slugs.length;
indexes.region = streamlineRegionsIndex(indexes.region, meetingsCount);
Expand Down
160 changes: 160 additions & 0 deletions test/__tests__/inactive-meetings-filter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { describe, expect, it } from 'vitest';

import { loadMeetingData } from '../../src/helpers/load-meeting-data';
import { defaults } from '../../src/hooks';

import type { JSONData } from '../../src/types';

describe('Inactive meetings in filters', () => {
it('includes inactive meetings in filter indexes when active meetings exist', () => {
const data: JSONData[] = [
{
name: 'Active Women Meeting',
slug: 'active-women',
time: '09:00',
address: '123 Main St',
city: 'Portland',
state: 'OR',
country: 'USA',
latitude: 45.5,
longitude: -122.6,
day: '5', // Friday
types: ['W'], // Women
},
{
name: 'Inactive Women Meeting',
slug: 'inactive-women',
formatted_address: 'Portland, OR, USA',
day: '5', // Friday
time: '19:00',
types: ['W'], // Women - but meeting is inactive (no address/conference)
},
];

const { indexes, meetings } = loadMeetingData(
data,
{
coordinates: false,
distance: false,
geolocation: false,
inactive: false,
location: false,
region: false,
sharing: false,
time: false,
type: false,
weekday: false,
},
defaults,
defaults.strings[defaults.language],
'America/Los_Angeles'
);

// Verify meetings are correctly marked
expect(meetings['active-women'].isActive).toBe(true);
expect(meetings['inactive-women'].isActive).toBe(false);

// Both meetings should be in the Women type index
const womenType = indexes.type.find(t => t.key === 'women');
expect(womenType).toBeDefined();
expect(womenType?.slugs).toContain('active-women');
expect(womenType?.slugs).toContain('inactive-women');

// Both meetings should be in the Friday weekday index
const fridayWeekday = indexes.weekday.find(w => w.key === 'friday');
expect(fridayWeekday).toBeDefined();
expect(fridayWeekday?.slugs).toContain('active-women');
expect(fridayWeekday?.slugs).toContain('inactive-women');
});

it('hides filter options when all meetings are inactive', () => {
const data: JSONData[] = [
{
name: 'Active Meeting',
slug: 'active-meeting',
time: '09:00',
address: '123 Main St',
city: 'Portland',
state: 'OR',
country: 'USA',
latitude: 45.5,
longitude: -122.6,
day: '0', // Sunday
},
{
name: 'Inactive Women Meeting',
slug: 'inactive-women',
formatted_address: 'Portland, OR, USA',
day: '5', // Friday
time: '19:00',
types: ['W'], // Women - but meeting is inactive
},
];

const { indexes } = loadMeetingData(
data,
{
coordinates: false,
distance: false,
geolocation: false,
inactive: false,
location: false,
region: false,
sharing: false,
time: false,
type: false,
weekday: false,
},
defaults,
defaults.strings[defaults.language],
'America/Los_Angeles'
);

// Women type should NOT be in the index (all Women meetings are inactive)
const womenType = indexes.type.find(t => t.key === 'women');
expect(womenType).toBeUndefined();

// Friday should NOT be in the weekday index (all Friday meetings are inactive)
const fridayWeekday = indexes.weekday.find(w => w.key === 'friday');
expect(fridayWeekday).toBeUndefined();

// Sunday SHOULD be in the weekday index (has active meeting)
const sundayWeekday = indexes.weekday.find(w => w.key === 'sunday');
expect(sundayWeekday).toBeDefined();
expect(sundayWeekday?.slugs).toContain('active-meeting');
});

it('preserves special metatypes regardless of active status', () => {
const data: JSONData[] = [
{
name: 'Inactive Meeting',
slug: 'inactive-meeting',
formatted_address: 'Portland, OR, USA',
},
];

const { indexes } = loadMeetingData(
data,
{
coordinates: false,
distance: false,
geolocation: false,
inactive: false,
location: false,
region: false,
sharing: false,
time: false,
type: false,
weekday: false,
},
defaults,
defaults.strings[defaults.language],
'America/Los_Angeles'
);

// Inactive metatype should be present even though all meetings are inactive
const inactiveType = indexes.type.find(t => t.key === 'inactive');
expect(inactiveType).toBeDefined();
expect(inactiveType?.slugs).toContain('inactive-meeting');
});
});
5 changes: 0 additions & 5 deletions test/__tests__/load-meeting-data.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@ describe('loadMeetingData', () => {
name: 'Morning',
slugs: ['test-meeting', 'other-meeting'],
},
{
key: 'appointment',
name: 'Appointment',
slugs: ['inactive-meeting'],
},
],
type: [
{
Expand Down