Skip to content

Commit

Permalink
V3 - feat: add employee list filters (#884)
Browse files Browse the repository at this point in the history
  • Loading branch information
trulshj authored Nov 20, 2024
1 parent 8a3e178 commit 7da6a93
Show file tree
Hide file tree
Showing 10 changed files with 266 additions and 40 deletions.
12 changes: 9 additions & 3 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@
"read_more": "Read more"
},
"employee_card": {
"show": "Show",
"of": "of",
"consultants": "consultants"
"show": "Showing",
"of": "out of",
"consultants": "consultants",
"location": "Location",
"all": "All",
"Project Management": "Project Management",
"Design": "Design",
"Utvikling": "Development",
"Administasjon": "Administration"
}
}
10 changes: 8 additions & 2 deletions messages/no.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@
"read_more": "Les mer"
},
"employee_card": {
"show": "Vis",
"show": "Viser",
"of": "av",
"consultants": "konsulenter"
"consultants": "konsulenter",
"location": "Lokasjon",
"all": "Alle",
"Project Management": "Prosjektledelse",
"Design": "Design",
"Utvikling": "Utvikling",
"Administasjon": "Administrasjon"
}
}
10 changes: 8 additions & 2 deletions messages/se.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,14 @@
"read_more": "Läs mer"
},
"employee_card": {
"show": "Visa",
"show": "Visar",
"of": "av",
"consultants": "konsulentar"
"consultants": "konsulter",
"location": "Kontor",
"all": "Alla",
"Project Management": "Projektledning",
"Design": "Design",
"Utvikling": "Utveckling",
"Administasjon": "Administration"
}
}
2 changes: 1 addition & 1 deletion src/components/employeeCard/employeeCard.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
text-wrap: wrap;
flex-direction: column;
width: 100%;
height: fit-content, 100%;
height: fit-content;
gap: 0.25rem;
align-self: stretch;
}
Expand Down
194 changes: 194 additions & 0 deletions src/components/sections/employees/EmployeeList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"use client";

import { useTranslations } from "next-intl";
import { useState } from "react";

import Button from "src/components/buttons/Button";
import EmployeeCard from "src/components/employeeCard/EmployeeCard";
import Text from "src/components/text/Text";
import { ChewbaccaEmployee, Competence } from "src/types/employees";

import styles from "./employees.module.css";

const competences: Competence[] = [
"Utvikling",
"Administasjon",
"Design",
"Project Management",
];

function countCompetences(employees: ChewbaccaEmployee[]) {
const competenceCounts: Record<Competence, number> = {
Utvikling: 0,
Administasjon: 0,
Design: 0,
"Project Management": 0,
};

employees
.flatMap((e) => e.competences)
.forEach((c) => (competenceCounts[c] += 1));

return competenceCounts;
}

function countLocations(employees: ChewbaccaEmployee[]) {
const locationCounts: Record<string, number> = {};

employees
.flatMap((e) => e.officeName)
.filter((o) => !!o)
.forEach((o) =>
!locationCounts[o!]
? (locationCounts[o!] = 1)
: (locationCounts[o!] += 1),
);

return locationCounts;
}

export interface EmployeesProps {
employees: ChewbaccaEmployee[];
language: string;
employeesPageSlug: string;
}

interface EmployeeFilters {
competenceFilter: Competence | null;
locationFilter: string | null;
}

export default function EmployeeList({
employees,
language,
employeesPageSlug,
}: EmployeesProps) {
const competenceCounts = countCompetences(employees);
const locationCounts = countLocations(employees);
const locations = Object.keys(locationCounts);

const t = useTranslations("employee_card");

const [filteredEmployees, setFilteredEmployees] =
useState<ChewbaccaEmployee[]>(employees);

const [employeeFilters, setEmployeeFilters] = useState<EmployeeFilters>({
competenceFilter: null,
locationFilter: null,
});

function filterEmployees(newFilters: Partial<EmployeeFilters>) {
const combinedFilters = { ...employeeFilters, ...newFilters };
setEmployeeFilters(combinedFilters);

const newFilteredEmployees = employees.filter((e) => {
if (
combinedFilters.competenceFilter !== null &&
!e.competences.includes(combinedFilters.competenceFilter)
) {
return false;
}

if (
combinedFilters.locationFilter !== null &&
e.officeName !== combinedFilters.locationFilter
) {
return false;
}

return true;
});

setFilteredEmployees(newFilteredEmployees);
}

return (
<>
<div className={styles.employeeFiltersWrapper}>
<div className={styles.employeeFilterWrapper}>
<Text type="labelSemibold" className={styles.employeeFilterLabel}>
Fag
</Text>
<Button
size="small"
type={
employeeFilters.competenceFilter == null
? "secondaryFilled"
: "secondary"
}
onClick={() => filterEmployees({ competenceFilter: null })}
>
{t("all")}
</Button>
{competences.map((competence) => {
const active = employeeFilters.competenceFilter == competence;
return (
<Button
size="small"
key={competence}
type={active ? "secondaryFilled" : "secondary"}
onClick={() =>
filterEmployees({ competenceFilter: competence })
}
>
{t(competence)} ({competenceCounts[competence]})
</Button>
);
})}
</div>
<div className={styles.employeeFilterWrapper}>
<Text type="labelSemibold" className={styles.employeeFilterLabel}>
{t("location")}
</Text>
<Button
size="small"
type={
employeeFilters.locationFilter == null
? "secondaryFilled"
: "secondary"
}
onClick={() => filterEmployees({ locationFilter: null })}
>
{t("all")}
</Button>
{locations.map((location) => {
const active = employeeFilters.locationFilter == location;
return (
<Button
size="small"
key={location}
type={active ? "secondaryFilled" : "secondary"}
onClick={() => filterEmployees({ locationFilter: location })}
>
{location} ({locationCounts[location]})
</Button>
);
})}
</div>
</div>

<div className={styles.employeeCountWrapper}>
<div style={{ display: "flex" }}></div>

<p className={styles.employeeCount}>
{t("show")}{" "}
<span className={styles.employeeCountValue}>{employees.length}</span>{" "}
{t("of")}{" "}
<span className={styles.employeeCountValue}>{employees.length}</span>{" "}
{t("consultants")}
</p>
</div>

<div className={styles.peopleContainer}>
{filteredEmployees.map((employee) => (
<EmployeeCard
employee={employee}
employeePageSlug={employeesPageSlug}
language={language}
key={employee.name}
/>
))}
</div>
</>
);
}
29 changes: 6 additions & 23 deletions src/components/sections/employees/Employees.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { headers } from "next/headers";
import { getTranslations } from "next-intl/server";

import EmployeeCard from "src/components/employeeCard/EmployeeCard";
import {
domainFromEmail,
fetchAllChewbaccaEmployees,
Expand All @@ -11,14 +9,14 @@ import { EmployeesSection } from "studio/lib/interfaces/pages";
import { EMPLOYEE_PAGE_SLUG_QUERY } from "studio/lib/queries/siteSettings";
import { loadStudioQuery } from "studio/lib/store";

import EmployeeList from "./EmployeeList";
import styles from "./employees.module.css";
export interface EmployeesProps {
language: string;
section: EmployeesSection;
}

export default async function Employees({ language, section }: EmployeesProps) {
const t = await getTranslations("employee_card");
const employeesPageRes = await loadStudioQuery<{ slug: string }>(
EMPLOYEE_PAGE_SLUG_QUERY,
{
Expand All @@ -39,31 +37,16 @@ export default async function Employees({ language, section }: EmployeesProps) {
(employee) =>
employee.email != null && domainFromEmail(employee.email) === domain,
);
const total = employees.length;

return (
<div className={styles.wrapper}>
<div className={styles.employees}>
<h1 className={styles.header}>{section.basicTitle}</h1>
<div className={styles.employeeCountWrapper}>
<p className={styles.employeeCount}>
{t("show")}
<span className={styles.employeeCountValue}>{total}</span>
{t("of")}
<span className={styles.employeeCountValue}>{total}</span>{" "}
{t("consultants")}
</p>
</div>
<div className={styles.peopleContainer}>
{employees.map((employee) => (
<EmployeeCard
employee={employee}
employeePageSlug={employeesPageSlug}
language={language}
key={employee.name}
/>
))}
</div>
<EmployeeList
employees={employees}
employeesPageSlug={employeesPageSlug}
language={language}
/>
</div>
</div>
);
Expand Down
21 changes: 21 additions & 0 deletions src/components/sections/employees/employees.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@
align-self: flex-start;
}

.employeeFiltersWrapper {
padding: 1.5rem 0;
align-self: flex-start;
display: flex;
flex-direction: column;
gap: 0.75rem;
}

.employeeFilterWrapper {
flex-wrap: wrap;
display: flex;
align-items: center;
gap: 0.75rem;
}

.employeeFilterLabel {
flex-shrink: 0;
min-width: 4.5rem;
width: 4.5rem;
}

.employeeCountWrapper {
width: 100%;
}
Expand Down
11 changes: 5 additions & 6 deletions src/components/text/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export type TextType =
| "bodyExtraSmall"
| "bodySmall"
| "bodyNormal"
| "bodySmall"
| "bodyBig"
| "bodyXl"
| "mobileH1"
Expand All @@ -34,11 +33,11 @@ const elementMap: { [key in TextType]: keyof JSX.IntrinsicElements } = {
h6: "h6",
desktopLink: "p",
desktopLinkBig: "p",
labelSmall: "p",
labelLight: "p",
labelRegular: "p",
labelSemibold: "p",
labelBold: "p",
labelSmall: "span",
labelLight: "span",
labelRegular: "span",
labelSemibold: "span",
labelBold: "span",
quoteItalic: "p",
quoteNormal: "p",
bodyExtraSmall: "p",
Expand Down
9 changes: 7 additions & 2 deletions src/components/text/text.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
font-family: var(--font-britti-sans);
}

.bodyBig,
.bodyNormal,
.labelSmall,
.labelLight,
.labelRegular,
Expand Down Expand Up @@ -122,6 +120,13 @@
line-height: 120%;
}

.labelBold {
font-size: 1rem;
font-style: normal;
font-weight: 800;
line-height: 120%;
}

.quoteNormal {
font-size: 1.5rem;
font-style: normal;
Expand Down
Loading

0 comments on commit 7da6a93

Please sign in to comment.