Skip to content

Commit

Permalink
V3-Contact information block (#849)
Browse files Browse the repository at this point in the history
* structure for contact information

* feat(Button): add extraSmall size

* feat(CustomerCase): sales contact information

---------

Co-authored-by: Mathias Oterhals Myklebust <[email protected]>
  • Loading branch information
anemne and mathiazom authored Oct 31, 2024
1 parent 983a32a commit bde2a1b
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 110 deletions.
6 changes: 4 additions & 2 deletions src/components/buttons/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from "react";

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

type ButtonType = "primary" | "secondary";
type ButtonSize = "large" | "small";
type ButtonType = "primary" | "secondary" | "secondaryFilled";
type ButtonSize = "large" | "small" | "extraSmall";

interface IButton {
size?: ButtonSize;
Expand All @@ -18,11 +18,13 @@ interface IButton {
const sizeClassMap: { [key in ButtonSize]: string } = {
large: styles.large,
small: styles.small,
extraSmall: styles.extraSmall,
};

const typeClassMap: { [key in ButtonType]: string } = {
primary: styles.primary,
secondary: styles.secondary,
secondaryFilled: styles.secondaryFilled,
};

const Button = ({
Expand Down
25 changes: 25 additions & 0 deletions src/components/buttons/button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
}
}

.extraSmall {
composes: button;
padding: 0.375rem 1.25rem;
font-size: 1rem;
font-weight: 400;

@media (min-width: 1024px) {
font-size: 1.125rem;
}
}

.primary {
background: var(--primary-dark);
color: var(--primary-white);
Expand Down Expand Up @@ -65,6 +76,20 @@
}
}

.secondaryFilled {
background: var(--primary-black);
color: var(--primary-white);
border: 1px solid var(--primary-black);

&:hover {
background: var(--primary-black-darker);
}

&:active {
background: var(--primary-black-darker);
}
}

.back {
composes: secondary;

Expand Down
74 changes: 74 additions & 0 deletions src/components/consultantCard/ConsultantCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Image from "next/image";
import Link from "next/link";

import CustomLink from "src/components/link/CustomLink";
import { ChewbaccaEmployee } from "src/types/employees";
import { aliasFromEmail } from "src/utils/employees";
import { LinkType } from "studio/lib/interfaces/navigation";

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

export interface ConsultantCardProps {
currentLanguage: string;
consultant: ChewbaccaEmployee;
employeePageSlug?: string;
}

export default function ConsultantCard({
currentLanguage,
consultant,
employeePageSlug,
}: ConsultantCardProps) {
const title = <p className={styles.consultantName}>{consultant.name}</p>;
return (
consultant.imageThumbUrl &&
consultant.name &&
consultant.email && (
<div key={consultant.email} className={styles.consultant}>
<div className={styles.consultantImage}>
<Image
src={consultant.imageUrl ?? consultant.imageThumbUrl}
alt={consultant.name}
objectFit="cover"
fill={true}
/>
</div>
<div className={styles.consultantInfo}>
{employeePageSlug !== undefined ? (
<Link
href={`/${currentLanguage}/${employeePageSlug}/${aliasFromEmail(consultant.email)}`}
>
{title}
</Link>
) : (
title
)}
{consultant.officeName && (
<p className={styles.consultantRole}>{consultant.officeName}</p>
)}
{consultant.email && (
<p className={styles.consultantEmail}>{consultant.email}</p>
)}
{consultant.telephone && (
<p className={styles.consultantTelephone}>{consultant.telephone}</p>
)}
{employeePageSlug && (
<CustomLink
size={"small"}
link={{
_key: "go-to-mini-cv",
_type: "link",
linkType: LinkType.Internal,
linkTitle: "Gå til Mini-CV", // TODO: translate
language: currentLanguage,
internalLink: {
_ref: `${employeePageSlug}/${aliasFromEmail(consultant.email)}`,
},
}}
/>
)}
</div>
</div>
)
);
}
43 changes: 43 additions & 0 deletions src/components/consultantCard/consultantCard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.consultant {
display: flex;
width: 100%;
gap: 1rem;
}

.consultantImage {
display: flex;
flex-direction: column;
align-items: center;
background-color: var(--primary-black);
border-radius: 12px;
height: 125px;
width: 50%;
padding: 1rem;
position: relative;
}

.consultantInfo {
display: flex;
flex-direction: column;
width: 50%;
gap: 0.5rem;
}

.consultantName {
color: var(--primary-black);
font-size: 16px;
font-weight: 600;
}

.consultantRole {
color: var(--primary-black);
font-size: 16px;
font-weight: 300;
}

.consultantEmail,
.consultantTelephone {
color: var(--primary-black);
font-size: 14px;
font-weight: 300;
}
2 changes: 2 additions & 0 deletions src/components/customerCases/customerCase/CustomerCase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Delivery,
} from "studioShared/lib/interfaces/customerCases";

import ContactInformation from "./contactInformation/ContactInformation";
import styles from "./customerCase.module.css";
import FeaturedCases from "./featuredCases/FeaturedCases";
import CustomerCaseConsultants from "./sections/customerCaseConsultants/CustomerCaseConsultants";
Expand Down Expand Up @@ -129,6 +130,7 @@ export default async function CustomerCase({
customerCasesPath={customerCasesPagePath}
/>
)}
<ContactInformation language={customerCase.language} />
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Text from "src/components/text/Text";
import { fetchEmployeesByEmails } from "src/utils/employees";
import { CompanyLocation } from "studio/lib/interfaces/companyDetails";
import { COMPANY_LOCATIONS_QUERY } from "studio/lib/queries/admin";
import { EMPLOYEE_PAGE_SLUG_QUERY } from "studio/lib/queries/siteSettings";
import { loadStudioQuery } from "studio/lib/store";

import styles from "./contactInformation.module.css";
import ContactSelector, {
ContactByLocationMap,
} from "./contactSelector/ContactSelector";

interface ContactInformationProps {
language: string;
}

async function fetchContactByLocationMap(
companyLocations: CompanyLocation[],
): Promise<ContactByLocationMap> {
const contactByLocation: ContactByLocationMap = {};
for (const location of companyLocations) {
if (
location.contactPerson === undefined ||
location.contactPerson.length === 0
) {
continue;
}
const contactRes = await fetchEmployeesByEmails([location.contactPerson]);
if (!contactRes.ok || contactRes.value.length === 0) {
continue;
}
contactByLocation[location._id] = contactRes.value[0];
}
return contactByLocation;
}

export default async function ContactInformation({
language,
}: ContactInformationProps) {
const employeePageSlug = (
await loadStudioQuery<{ slug: string } | null>(EMPLOYEE_PAGE_SLUG_QUERY, {
language,
})
).data?.slug;
const companyLocations = (
await loadStudioQuery<CompanyLocation[]>(COMPANY_LOCATIONS_QUERY, {})
).data;
const contactByLocation = await fetchContactByLocationMap(companyLocations);
const locationIdsWithContact = Object.keys(contactByLocation);
const locationsWithContact = companyLocations.filter((location) =>
locationIdsWithContact.includes(location._id),
);

return (
<div className={styles.wrapper}>
<div className={styles.content}>
<div className={styles.titleSection}>
{/* TODO: translation */}
<Text type={"bodyXl"}>
Trenger du hjelp med lignende eller noe helt annet?
</Text>
<Text type="bodyBig"> Kontakt salg! </Text>
</div>
<div className={styles.contactSection}>
<ContactSelector
language={language}
locations={locationsWithContact}
contactByLocation={contactByLocation}
employeePageSlug={employeePageSlug}
/>
</div>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.wrapper {
display: flex;
flex-direction: column;
margin: 4rem 0;

@media (min-width: 1024px) {
align-items: center;
}
}

.content {
display: flex;
flex-direction: row;
gap: 1rem;
max-width: 960px;

@media (max-width: 1024px) {
flex-direction: column;
gap: 2rem;
}
}

.titleSection {
display: flex;
flex-direction: column;
gap: 0.75rem;
}

.contactSection {
display: flex;
gap: 1.5rem;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import { useState } from "react";

import Button from "src/components/buttons/Button";
import ConsultantCard from "src/components/consultantCard/ConsultantCard";
import { ChewbaccaEmployee } from "src/types/employees";
import { CompanyLocation } from "studio/lib/interfaces/companyDetails";

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

export type ContactByLocationMap = {
[locationId: string]: ChewbaccaEmployee;
};

export interface ContactSelectorProps {
language: string;
locations: CompanyLocation[];
contactByLocation: ContactByLocationMap;
employeePageSlug?: string;
}

export default function ContactSelector({
language,
locations,
contactByLocation,
employeePageSlug,
}: ContactSelectorProps) {
const locationIds = Object.keys(contactByLocation);

const [selectedLocationId, setSelectedLocationId] = useState<string | null>(
locationIds.length === 0 ? locationIds[0] : null,
);

if (locationIds.length === 0) {
return;
}

const selectedOrDefaultLocationId = selectedLocationId ?? locationIds[0];

return (
<>
<div className={styles.locationSection}>
{locations.map((location) => (
<Button
type={
location._id === selectedOrDefaultLocationId
? "secondaryFilled"
: "secondary"
}
size={"extraSmall"}
key={location._id}
onClick={() => setSelectedLocationId(location._id)}
>
{location.companyLocationName}
</Button>
))}
</div>
<div className={styles.consultantSection}>
<ConsultantCard
currentLanguage={language}
consultant={contactByLocation[selectedOrDefaultLocationId]}
employeePageSlug={employeePageSlug}
/>
</div>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.locationSection {
display: flex;
flex-direction: column;
gap: 0.75rem;
}

.consultantSection {
min-width: 400px;
}
Loading

0 comments on commit bde2a1b

Please sign in to comment.