diff --git a/src/components/employeeCard/EmployeeCard.tsx b/src/components/employeeCard/EmployeeCard.tsx index f4d74ac36..056c5b765 100644 --- a/src/components/employeeCard/EmployeeCard.tsx +++ b/src/components/employeeCard/EmployeeCard.tsx @@ -24,64 +24,85 @@ export default function EmployeeCard({ employee.name && employee.email && (
- -
- {employee.name} -
- -
- - - {employee.name} - - - -
- {employee.competences.map((competence) => ( - + +
+ {employee.name} +
+ +
+ + - {competence} - - ))} -
+ {employee.name} + +
- - {employee.email} - - {employee.telephone && ( - - - {formatPhoneNumber(employee.telephone)} - +
+ {employee.competences.map((competence) => ( + + {competence} + + ))} +
+ + + {employee.email} - )} + {employee.telephone && ( + + + {formatPhoneNumber(employee.telephone)} + + + )} +
) ); } -export function EmployeeCardSkeleton() { +export function EmployeeCardSkeleton({ + background = "light", +}: { + background?: "light" | "dark"; +}) { + const backgroundClass = + background === "dark" ? styles["employeeImage--dark"] : ""; + const backgroundClassText = + background === "dark" + ? `${styles.skeletonText} ${styles["skeletonText--dark"]}` + : styles.skeletonText; return ( -
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
); } diff --git a/src/components/employeeCard/employeeCard.module.css b/src/components/employeeCard/employeeCard.module.css index c99bd84b2..920269654 100644 --- a/src/components/employeeCard/employeeCard.module.css +++ b/src/components/employeeCard/employeeCard.module.css @@ -4,18 +4,6 @@ gap: 1rem; } -.employeeImage { - display: flex; - flex-direction: column; - align-items: center; - background-color: var(--background-bg-dark); - border-radius: 12px; - height: 125px; - width: 50%; - padding: 1rem; - position: relative; -} - .employeeInfoWrapper { display: flex; flex-direction: column; @@ -32,15 +20,23 @@ .employeeNameLink { text-decoration: none; + color: currentColor; +} + +.employeeEmail a, +.employeePhone a { + color: currentColor; } .employeeWrapper { + container-type: inline-size; + container-name: employee; +} +.employeeWrapper__inner { display: flex; flex-direction: column; - align-items: flex-start; - min-width: 280px; - max-width: var(--Text-paragraph, 537px); - gap: var(--small, 6px); + gap: 1rem; + row-gap: var(--small, 6px); } .employeeInfoWrapper { @@ -48,21 +44,23 @@ text-wrap: wrap; flex-direction: column; width: 100%; - height: fit-content; gap: 0.25rem; - align-self: stretch; } .employeeImage { display: block; height: 206px; - min-width: 280px; + min-width: 206px; + width: 100%; max-width: var(--Text-paragraph, 537px); position: relative; background-color: var(--background-bg-dark); border-radius: var(--medium, 12px); } +.employeeImage--dark { + background-color: var(--background-bg-light-primary); +} .employeeInfo { color: var(--stroke-tertiary, #5e5e5e); @@ -73,14 +71,10 @@ } .employeeName { - color: var(--text-primary, #222424); + color: currentColor; } .employeeRole { - display: flex; - flex-direction: row; - overflow: visible; - align-self: stretch; } .employeeRoleDot::after { @@ -101,17 +95,20 @@ color: var(--text-tertiary, #5e5e5e); } -/* Update the skeleton styles */ +/* Skeleton styles */ .skeletonCard { animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite; } .skeletonText { height: 20px; - background-color: var(--background-bg-light-primary); + background-color: var(--background-bg-dark); border-radius: 4px; align-self: flex-start; } +.skeletonText--dark { + background-color: var(--background-bg-light-primary); +} .skeletonName { width: 150px; @@ -134,3 +131,27 @@ opacity: 0.5; } } + +@container employee (min-width: 380px) { + .employeeWrapper__inner { + flex-direction: row; + } + .employeeEmail { + margin-top: auto; + } + .employeeEmail, + .employeePhone, + .employeeRoleDot { + font-size: 1rem; + } + .employeePhone { + margin-top: 0.5rem; + } + .employeeInfoWrapper { + padding: 0.5rem 0; + } + + .skeletonAutoMargin { + margin-top: auto; + } +} diff --git a/src/components/sections/contact-box/ContactBox.tsx b/src/components/sections/contact-box/ContactBox.tsx new file mode 100644 index 000000000..57989e386 --- /dev/null +++ b/src/components/sections/contact-box/ContactBox.tsx @@ -0,0 +1,87 @@ +import { Suspense } from "react"; + +import { EmployeeCardSkeleton } from "src/components/employeeCard/EmployeeCard"; +import Text from "src/components/text/Text"; +import { ChewbaccaEmployee } from "src/types/employees"; +import { fetchEmployeesByEmails } from "src/utils/employees"; +import { ContactBoxSection } from "studio/lib/interfaces/pages"; +import { EMPLOYEE_PAGE_SLUG_QUERY } from "studio/lib/queries/siteSettings"; +import { loadStudioQuery } from "studio/lib/store"; + +import styles from "./contact-box.module.css"; +import ContactSelector, { EmployeeAndTag } from "./ContactSelector"; + +export interface ContactBoxProps { + section: ContactBoxSection; + language: string; +} + +export default async function ContactBox({ + section, + language, +}: ContactBoxProps) { + const employeesPageRes = await loadStudioQuery<{ slug: string }>( + EMPLOYEE_PAGE_SLUG_QUERY, + { + language, + }, + ); + const employeesPageSlug = employeesPageRes.data.slug; + + const contactPoints = fetchEmployeesByEmails( + section.contactPoints.map((contactPoint) => contactPoint.email), + ).then((result) => + result.ok + ? result.value.map((e) => employeeAndTag(e, section.contactPoints)) + : [], + ); + + const backgroundClass = + section.background === "light" ? styles["contactBox__inner--light"] : ""; + + return ( +
+
+
+ + {section.basicTitle} + + + {section.optionalSubtitle && ( + {section.optionalSubtitle} + )} +
+ +
+ } + > + + +
+
+
+ ); +} + +function employeeAndTag( + employee: ChewbaccaEmployee, + contactPoints: ContactBoxSection["contactPoints"], +): EmployeeAndTag { + const tag = + contactPoints.find((contactPoint) => contactPoint.email === employee.email) + ?.tag ?? ""; + return { + employee, + tag, + tagSlug: slugify(tag), + }; +} +function slugify(tag: string) { + return tag.toLowerCase().replace(/ /g, "-"); +} diff --git a/src/components/sections/contact-box/ContactBoxPreview.tsx b/src/components/sections/contact-box/ContactBoxPreview.tsx new file mode 100644 index 000000000..1c2eb9e62 --- /dev/null +++ b/src/components/sections/contact-box/ContactBoxPreview.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { useQuery } from "@sanity/react-loader"; +import { Suspense } from "react"; + +import { PreviewProps } from "src/types/preview"; +import { ContactBoxSection, PageBuilder } from "studio/lib/interfaces/pages"; +import { PAGE_QUERY } from "studio/lib/queries/pages"; + +import ContactBox from "./ContactBox"; + +export default function ContactBoxPreview({ + initialData, + sectionIndex, +}: PreviewProps) { + const { data: newData } = useQuery( + PAGE_QUERY, + { id: initialData.data._id, language: initialData.data.language }, + { initial: initialData }, + ); + + const section = newData + ? (newData.sections.find( + (section, index) => + section._type === "contactBox" && index === sectionIndex, + ) as ContactBoxSection) + : (initialData.data.sections.find( + (section, index) => + section._type === "contactBox" && index === sectionIndex, + ) as ContactBoxSection); + + return ( + + + + ); +} diff --git a/src/components/sections/contact-box/ContactSelector.tsx b/src/components/sections/contact-box/ContactSelector.tsx new file mode 100644 index 000000000..e9936b78d --- /dev/null +++ b/src/components/sections/contact-box/ContactSelector.tsx @@ -0,0 +1,73 @@ +"use client"; + +import { use, useState } from "react"; + +import EmployeeCard from "src/components/employeeCard/EmployeeCard"; +import { Tag } from "src/components/tag"; +import { ChewbaccaEmployee } from "src/types/employees"; + +import styles from "./contact-box.module.css"; + +export type EmployeeAndTag = { + employee: ChewbaccaEmployee; + tag: string; + tagSlug: string; +}; + +export type ContactSelectorProps = { + contactPoints: Promise; + employeesPageSlug: string; + language: string; + background?: "dark" | "light"; +}; + +export default function ContactSelector({ + contactPoints: contactPointsPromise, + employeesPageSlug, + language, + background = "dark", +}: ContactSelectorProps) { + const contactPoints = use(contactPointsPromise); + + const [selectedTag, setSelectedTag] = useState( + contactPoints[0].tagSlug, + ); + + return ( +
+
+ {contactPoints.map((contactPoint) => ( + setSelectedTag(contactPoint.tagSlug)} + text={contactPoint.tag} + /> + ))} +
+
+ {contactPoints.map((contactPoint) => ( + + ))} +
+
+ ); +} diff --git a/src/components/sections/contact-box/contact-box.module.css b/src/components/sections/contact-box/contact-box.module.css new file mode 100644 index 000000000..be33fa6e2 --- /dev/null +++ b/src/components/sections/contact-box/contact-box.module.css @@ -0,0 +1,76 @@ +.contactBox { + margin: 8rem auto; + padding: 0 1rem; + max-width: var(--max-content-width-large); +} + +.contactBox__inner { + --_contactBox__background: var(--background-bg-dark); + --_contactBox__color: var(--text-primary-light); + + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + + padding: 1.5rem 3rem; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + gap: var(--Padding-l, 48px); + align-self: stretch; + + border-radius: var(--radius-small); + background: var(--_contactBox__background); + color: var(--_contactBox__color); +} +.contactBox__inner--light { + --_contactBox__background: var(--background-bg-light-primary); + --_contactBox__color: var(--text-primary); +} + +.textContent { + height: 100%; + display: flex; + flex-direction: column; + gap: 1rem; + + justify-content: flex-end; +} + +.contactSelectorWrapper { + container-type: inline-size; + container-name: contactSelectorWrapper; +} + +.contactSelector { + display: flex; + gap: 1rem; +} + +.tagList { + display: flex; + flex-direction: column; + gap: 0.75rem; + + align-items: flex-start; + min-width: 120px; +} + +.employeeCard { + flex: 1; +} + +@media (max-width: 400px) { + .contactBox__inner { + padding: 1rem 1rem; + } +} + +@container contactSelectorWrapper (max-width: 350px) { + .contactSelector { + flex-direction: column; + } + + .tagList { + flex-direction: row; + } +} diff --git a/src/components/tag/index.tsx b/src/components/tag/index.tsx index 18f16130e..468d3ae93 100644 --- a/src/components/tag/index.tsx +++ b/src/components/tag/index.tsx @@ -5,22 +5,30 @@ import Text from "src/components/text/Text"; import styles from "./tag.module.css"; type TagInner = - | { + | ({ type: "button"; onClick?: () => void; - } - | { + } & JSX.IntrinsicElements["button"]) + | ({ type: "link"; href: string; - }; + } & JSX.IntrinsicElements["link"]); type TagProps = { active?: boolean; + background?: "light" | "dark"; text: string; } & TagInner; -export const Tag = ({ text, active = false, ...props }: TagProps) => { - const className = `${styles.tag} ${active ? styles["tag--active"] : ""}`; +export const Tag = ({ + text, + background = "light", + active = false, + ...props +}: TagProps) => { + const activeClass = active ? styles["tag--active"] : ""; + const bgDarkClass = background === "dark" ? styles["tag--bgDark"] : ""; + const className = `${styles.tag} ${activeClass} ${bgDarkClass}`; if (props.type === "button") { return (