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
1 change: 0 additions & 1 deletion frontend/src/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const navigation: NavigationItem[] = [
];

const coordNavigation: NavigationItem[] = [
{ name: "My Team", to: { name: "my-coordination-team" } },
{ name: "Coordination Teams", to: { name: "coordination-teams" } },
{ name: "Packages", to: { name: "event-packages" } },
{ name: "Templates", to: { name: "contract-templates" } },
Expand Down
57 changes: 50 additions & 7 deletions frontend/src/components/companies/MembersCompanies.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@
<CreateCompanyDialogTrigger />
</div>

<ParticipationFilters
v-model:selected="selectedStatus"
v-model:selected-package="selectedPackage"
:packages="packages"
/>
<div class="flex flex-wrap gap-3 mb-4 items-center">
<ParticipationFilters
v-model:selected="selectedStatus"
v-model:selected-package="selectedPackage"
:packages="packages"
/>
<Select v-model="selectedTeamId">
<SelectTrigger class="w-52">
<SelectValue placeholder="All teams" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All teams</SelectItem>
<SelectItem
v-for="team in coordinationTeams"
:key="team.id"
:value="team.id"
>
{{ team.name }}
</SelectItem>
</SelectContent>
</Select>
</div>

<div
v-if="!membersSorted.length && companiesLoading"
Expand All @@ -23,8 +40,9 @@

<DynamicScroller
v-else
:key="selectedTeamId"
:items="membersSorted"
class="h-100"
page-mode
:min-item-size="1"
>
<template #default="{ item }">
Expand Down Expand Up @@ -70,6 +88,7 @@
<script setup lang="ts">
import type { Company, CompanyParticipation } from "@/dto/companies";
import type { Member } from "@/dto/members";
import type { CoordinationTeam } from "@/dto/coordinationTeams";
import MemberWithAvatar from "@/components/members/MemberWithAvatar.vue";
import { DynamicScroller } from "vue-virtual-scroller";
import { useInsertionSort, useSortByParticipationStatus } from "@/lib/utils";
Expand All @@ -82,17 +101,34 @@ import { useParticipationFilter } from "@/composables/useParticipationFilter";
import type { ObjectID, ParticipationStatus } from "@/dto";
import ParticipationFilters from "@/components/ParticipationFilters.vue";
import { useEventPackagesQuery } from "@/mutations/packages";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

const props = defineProps<{
companies: Company[];
companiesLoading?: boolean;
members: Member[];
eventId: number;
coordinationTeams?: CoordinationTeam[];
}>();

// TODO shift me to top
const membersSorted = computed(() => {
return [...props.members]?.sort((a, b) => a.name.localeCompare(b.name));
const sorted = [...props.members]?.sort((a, b) =>
a.name.localeCompare(b.name),
);
if (!selectedTeamId.value || selectedTeamId.value === "all") return sorted;
const team = props.coordinationTeams?.find(
(t) => t.id === selectedTeamId.value,
);
if (!team) return sorted;
const teamMemberSet = new Set(team.coordinatedMembers);
return sorted.filter((m) => teamMemberSet.has(m.id));
});

const membersMap = computed(() => {
Expand All @@ -105,6 +141,11 @@ const membersMap = computed(() => {
);
});

// Set of member IDs visible after team filtering
const visibleMemberIds = computed(
() => new Set(membersSorted.value.map((m) => m.id)),
);

interface CompanyWithParticipation extends Company {
participation: CompanyParticipation;
}
Expand All @@ -118,6 +159,7 @@ const participations = computed(() =>
if (currParticipation && currParticipation.member in membersMap.value!) {
const member = membersMap.value?.[currParticipation.member];
if (!member) return acc; // Skip if member not found
if (!visibleMemberIds.value.has(member.id)) return acc; // skip filtered-out members

if (!acc.has(member.id)) acc.set(member.id, []);

Expand All @@ -138,6 +180,7 @@ const participations = computed(() =>

const selectedStatus = ref<ParticipationStatus | null>(null);
const selectedPackage = ref<ObjectID | null>(null);
const selectedTeamId = ref<string>("all");

// Fetch packages for filter, pre-filtered by current event
const { data: packages } = useEventPackagesQuery();
Expand Down
49 changes: 46 additions & 3 deletions frontend/src/components/speakers/MembersSpeakers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,24 @@
<CreateSpeakerDialogTrigger />
</div>

<ParticipationFilters v-model:selected="selectedStatus" />
<div class="flex flex-wrap gap-3 mb-4 items-center">
<ParticipationFilters v-model:selected="selectedStatus" />
<Select v-model="selectedTeamId">
<SelectTrigger class="w-52">
<SelectValue placeholder="All teams" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All teams</SelectItem>
<SelectItem
v-for="team in coordinationTeams"
:key="team.id"
:value="team.id"
>
{{ team.name }}
</SelectItem>
</SelectContent>
</Select>
</div>

<div
v-if="!membersSorted.length && speakersLoading"
Expand All @@ -19,8 +36,9 @@

<DynamicScroller
v-else
:key="selectedTeamId"
:items="membersSorted"
class="h-100"
page-mode
:min-item-size="1"
>
<template #default="{ item }">
Expand Down Expand Up @@ -65,6 +83,7 @@

<script setup lang="ts">
import type { Member } from "@/dto/members";
import type { CoordinationTeam } from "@/dto/coordinationTeams";
import MemberWithAvatar from "@/components/members/MemberWithAvatar.vue";
import { DynamicScroller } from "vue-virtual-scroller";
import type { Speaker, SpeakerWithParticipation } from "@/dto/speakers";
Expand All @@ -77,17 +96,34 @@ import { ChevronDown } from "lucide-vue-next";
import { useParticipationFilter } from "@/composables/useParticipationFilter";
import type { ParticipationStatus } from "@/dto";
import ParticipationFilters from "@/components/ParticipationFilters.vue";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

const props = defineProps<{
speakers: Speaker[];
speakersLoading?: boolean;
members: Member[];
eventId: number;
coordinationTeams?: CoordinationTeam[];
}>();

// TODO shift me to top
const membersSorted = computed(() => {
return [...props.members]?.sort((a, b) => a.name.localeCompare(b.name));
const sorted = [...props.members]?.sort((a, b) =>
a.name.localeCompare(b.name),
);
if (!selectedTeamId.value || selectedTeamId.value === "all") return sorted;
const team = props.coordinationTeams?.find(
(t) => t.id === selectedTeamId.value,
);
if (!team) return sorted;
const teamMemberSet = new Set(team.coordinatedMembers);
return sorted.filter((m) => teamMemberSet.has(m.id));
});

const membersMap = computed(() => {
Expand All @@ -100,6 +136,11 @@ const membersMap = computed(() => {
);
});

// Set of member IDs visible after team filtering
const visibleMemberIds = computed(
() => new Set(membersSorted.value.map((m) => m.id)),
);

const participations = computed(() =>
props.speakers?.reduce((acc, speaker) => {
const currParticipation = speaker.participations.find(
Expand All @@ -109,6 +150,7 @@ const participations = computed(() =>
if (currParticipation && currParticipation.member in membersMap.value!) {
const member = membersMap.value?.[currParticipation.member];
if (!member) return acc; // Skip if member not found
if (!visibleMemberIds.value.has(member.id)) return acc; // skip filtered-out members

if (!acc.has(member.id)) acc.set(member.id, []);

Expand All @@ -128,6 +170,7 @@ const participations = computed(() =>
);

const selectedStatus = ref<ParticipationStatus | null>(null);
const selectedTeamId = ref<string>("all");

const participationsFiltered = useParticipationFilter<SpeakerWithParticipation>(
participations as ComputedRef<Map<string, SpeakerWithParticipation[]>>,
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,6 @@ const router = createRouter({
),
meta: { roles: ["COORDINATOR", "ADMIN"] },
},
{
path: "me/coord-team",
name: "my-coordination-team",
component: () =>
import(
"./views/Dashboard/CoordinationTeams/CoordinatorTeamView.vue"
),
meta: { roles: ["COORDINATOR"] },
},
{
path: "gmail",
name: "gmail-messages",
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/views/Dashboard/Companies/CompaniesView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:companies-loading="isCompaniesLoading"
:members="membersList?.data || []"
:event-id="eventStore.selectedEvent?.id || 0"
:coordination-teams="coordinationTeamsList?.data || []"
/>
</template>

Expand All @@ -16,6 +17,7 @@ import { useEventStore } from "@/stores/event";
import { getAllMembers } from "@/api/members";
import type { AllMembersFilter } from "@/dto/members";
import MembersCompanies from "@/components/companies/MembersCompanies.vue";
import { getAllCoordinationTeams } from "@/api/coordinationTeams";

const eventStore = useEventStore();
const companiesFilters = computed<AllCompaniesFilter>(() => ({
Expand All @@ -35,4 +37,9 @@ const { data: membersList } = useQuery({
key: () => ["members", JSON.stringify(membersFilters.value)],
query: () => getAllMembers(membersFilters.value),
});

const { data: coordinationTeamsList } = useQuery({
key: ["coordinationTeams"],
query: () => getAllCoordinationTeams(),
});
</script>
Loading
Loading