Skip to content
Closed
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
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@ NEXT_PUBLIC_APPINSIGHTS_CLIENT_SAMPLING_PERCENTAGE=20
# Track Web Vitals metrics (true/false, default: true)
NEXT_PUBLIC_APPINSIGHTS_TRACK_WEB_VITALS=true

# displayed in the footer
# displayed in the footer - the continuous deployment line.
# Set in CI as Docker build args (see .github/workflows/template-build.yml);
# set them locally if you need that line to render in dev.
Comment on lines +26 to +28
NEXT_PUBLIC_GITHUB_RUN_DATE=2021-09-23T05:00:00.000Z
NEXT_PUBLIC_GITHUB_SHA=0000000000000000000000000000000000000000
NEXT_PUBLIC_GITHUB_REPOSITORY=SSWConsulting/SSW.Website

# Current site URL
SITE_URL=***
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/template-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
- name: Get current date
id: date
run: |
echo "date=$(Get-Date -Format yyyy-MM-ddThh:mm:ssZ -AsUtc)" >> $env:GITHUB_OUTPUT
echo "date=$(Get-Date -Format yyyy-MM-ddTHH:mm:ssZ -AsUtc)" >> $env:GITHUB_OUTPUT

- name: Azure CLI - Login
uses: azure/login@v3
Expand Down
14 changes: 10 additions & 4 deletions components/blocks/v3/globe/globe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,26 @@ function OfficeAccordionItem({ office, isOpen, onToggle }) {
return (
<div
className={cn(
"relative border-b border-hairline bg-gray-100 dark:border-white/10 dark:bg-sswBorder",
// last:border-b-0 stops the final row doubling up against the
// container's own bottom border.
"relative border-b border-hairline bg-gray-100 last:border-b-0 dark:border-white/10 dark:bg-sswBorder",
isOpen && "bg-gray-200 dark:bg-sswBlack"
)}
>
{/* Hover tints the label and chevron red rather than the row background,
so the background keeps meaning "open" and the two never read alike. */}
<button
type="button"
onClick={onToggle}
aria-expanded={isOpen}
className="flex w-full items-center justify-between px-4 py-5 text-left"
className="group flex w-full items-center justify-between px-4 py-5 text-left"
>
<span className="text-lg text-foreground">{office?.name}</span>
<span className="text-lg text-foreground transition-colors group-hover:text-sswRed">
{office?.name}
</span>
<FiChevronDown
className={cn(
"size-5 shrink-0 text-foreground transition-transform duration-300",
"size-5 shrink-0 text-foreground transition-all duration-300 group-hover:text-sswRed",
isOpen && "rotate-180"
)}
/>
Expand Down
4 changes: 2 additions & 2 deletions components/blocks/v3/imageCards/imageCards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ function ImageCard({ card, cardBackgroundClass, showBorder }) {
/>
<div
className={cn(
"relative flex aspect-[4/3] items-center justify-center bg-sswRed p-4 sm:p-8"
"relative flex aspect-3/2 items-center justify-center bg-sswRed p-4 sm:p-8"
)}
>
{card?.graphic?.imageSource && (
<div className={cn("relative size-40 max-w-[70%]")}>
<div className={cn("relative size-28 max-w-[70%]")}>
<Image
src={card.graphic.imageSource}
alt={card.graphic.altText ?? card?.title ?? ""}
Expand Down
8 changes: 8 additions & 0 deletions components/blocks/v3/peopleCarousel/peopleCarousel.schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,18 @@ export const V3PeopleCarouselSchema: Template = {
label: "X (Twitter) URL",
name: "twitter",
},
{
type: "string",
label: "GitHub URL",
name: "github",
description: "Optional. The icon only shows when a URL is set.",
},
{
type: "string",
label: "SSW People URL",
name: "sswPeople",
description:
"Where the card itself links to. The whole card is clickable.",
},
],
},
Expand Down
36 changes: 14 additions & 22 deletions components/blocks/v3/peopleCarousel/peopleCarousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,24 @@ import { cn } from "@/lib/utils";
import { ArrowLeft, ArrowRight } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { FaLinkedinIn } from "react-icons/fa";
import { FaGithub, FaLinkedinIn } from "react-icons/fa";
import { FaXTwitter } from "react-icons/fa6";
import { tinaField } from "tinacms/dist/react";
import { CarouselDots } from "../shared/carouselDots";
import { CarouselMoreCard } from "../shared/carouselMoreCard";
import { PersonCardTexture } from "./personCardTexture";

// The SSW mark is a 2×2 grid of squares. Rendered in currentColor so it inherits
// the white→sswRed hover from its link, like the react-icons beside it.
function SswPeopleIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 100 100" fill="currentColor" className={className}>
<rect x="0" y="0" width="46" height="46" />
<rect x="54" y="0" width="46" height="46" />
<rect x="0" y="54" width="46" height="46" />
<rect x="54" y="54" width="46" height="46" />
</svg>
);
}

const socials = [
{ key: "linkedin", label: "LinkedIn", Icon: FaLinkedinIn },
{ key: "twitter", label: "X", Icon: FaXTwitter },
{ key: "sswPeople", label: "SSW People", Icon: SswPeopleIcon },
{ key: "github", label: "GitHub", Icon: FaGithub },
];

// The photo and name link to the profile; the social icons link out on their
// own. A plain <div> stands in when there's no profile to link to.
// The profile link stretches over the whole card via an ::after overlay, so
// anywhere that isn't a social icon opens the SSW People profile. The icons
// sit above that overlay (z-10) and keep their own targets — nesting real
// <a>s inside one another isn't valid, hence the pseudo-element.
// A plain <div> stands in when there's no profile to link to.
function ProfileLink({ person, className, children }) {
if (!person?.sswPeople) {
return <div className={className}>{children}</div>;
Expand All @@ -52,7 +42,7 @@ function ProfileLink({ person, className, children }) {
target="_blank"
rel="noopener noreferrer"
aria-label={`View ${person?.name ?? "this person"}'s SSW People profile`}
className={cn("!no-underline", className)}
className={cn("!no-underline after:absolute after:inset-0", className)}
>
{children}
</Link>
Expand All @@ -61,7 +51,7 @@ function ProfileLink({ person, className, children }) {

function PersonCard({ person, index, scope }) {
return (
<div className="flex h-full flex-col overflow-hidden rounded-card border-0.75 border-hairline bg-white transition-colors duration-300 hover:border-sswRed dark:border-sswBorder dark:bg-sswCard">
<div className="group relative flex h-full flex-col overflow-hidden rounded-card border-0.75 border-hairline bg-white transition-colors duration-300 hover:border-sswRed dark:border-sswBorder dark:bg-sswCard">
<ProfileLink person={person} className="block">
{/* Red panel frames the photo with padding on the sides and top while the
photo stays flush to the bottom, so the person reads as standing in it. */}
Expand All @@ -80,7 +70,7 @@ function PersonCard({ person, index, scope }) {

<div className="px-4 pt-4 text-center xl:px-6 xl:pt-6">
{person?.name && (
<h3 className="text-xl font-semibold text-foreground">
<h3 className="text-xl font-semibold text-foreground transition-colors group-hover:text-sswRed">
{person.name}
</h3>
)}
Expand All @@ -92,8 +82,10 @@ function PersonCard({ person, index, scope }) {
</div>
</ProfileLink>

{/* flex-1 keeps the icon rows aligned when names wrap to two lines. */}
<div className="flex flex-1 items-start justify-center gap-1 p-4 xl:px-6 xl:pb-6">
{/* flex-1 keeps the icon rows aligned when names wrap to two lines.
z-10 lifts the icons above the profile link's ::after overlay so they
stay independently clickable. */}
<div className="relative z-10 flex flex-1 items-start justify-center gap-1 p-4 xl:px-6 xl:pb-6">
{socials.map(({ key, label, Icon }) =>
person?.[key] ? (
<Link
Expand Down
2 changes: 1 addition & 1 deletion components/blocks/v3/videoFeature/videoFeature.schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const V3VideoFeatureSchema: Template = {
"Mainstream tech, real ROI, and clear action items. Every decision is measured by one question: does it actually work?",
},
],
recognitionHeading: "Trusted & Recognised",
recognitionHeading: "Trusted & Recognized",
recognitionBadges: [
{ label: "We love Microsoft - Cloud Partner", icon: "SiMicrosoft" },
{ label: "MAPA Learning Excellence", icon: "BiAward" },
Expand Down
2 changes: 1 addition & 1 deletion components/blocks/v3/videoFeature/videoFeature.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export function V3VideoFeature({ data }) {
{data?.recognitionHeading && (
<span
data-tina-field={tinaField(data, "recognitionHeading")}
className="font-mono text-xs uppercase tracking-widest text-muted-foreground"
className="font-mono text-xs uppercase tracking-wider text-muted-foreground"
>
{data.recognitionHeading}
</span>
Expand Down
97 changes: 97 additions & 0 deletions components/layout/footer/deployment-info.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"use client";

import { CustomLink } from "@/components/customLink";
import { cn } from "@/lib/utils";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import utc from "dayjs/plugin/utc";
import { useEffect, useState } from "react";

// Extended here rather than in app/layout.tsx on purpose. The previous version
// of this component relied on the layout extending `relativeTime`, so when the
// component was removed in #4827 the plugin looked unused and was stripped in
// #4925 — leaving `.fromNow()` primed to throw if anyone restored it (see the
// original fix in #926). Owning the dependency locally keeps the two together.
// dayjs.extend is idempotent, so re-extending is safe.
dayjs.extend(relativeTime);
dayjs.extend(utc);

const commitHash = process.env.NEXT_PUBLIC_GITHUB_SHA;
const repo = process.env.NEXT_PUBLIC_GITHUB_REPOSITORY;

// Resolved once at module scope. NEXT_PUBLIC_* values are inlined at build
// time and cannot change, so there is nothing to recompute per render.
//
// isValid() is the guard that matters, not the null check: dayjs returns a
// truthy object for unparseable input, and calling toISOString() on it throws
// RangeError. This component sits in the root layout of a force-static site,
// so an unguarded throw would fail the entire build rather than one page.
const rawBuildDate = process.env.NEXT_PUBLIC_GITHUB_RUN_DATE;
const parsedBuildDate = rawBuildDate ? dayjs.utc(rawBuildDate) : null;
const buildDate = parsedBuildDate?.isValid() ? parsedBuildDate : null;
const exact = buildDate?.format("D MMM YYYY [at] HH:mm UTC") ?? null;
const iso = buildDate?.toISOString();

const LINK_CLASS =
"font-medium text-gray-300 transition-colors hover:text-white";

export const DeploymentInfo = ({ className }: { className?: string }) => {
// Resolved after mount, never during render. Every page is `force-static`,
// so a relative time computed on the server would be evaluated once during
// the Docker build that also stamps NEXT_PUBLIC_GITHUB_RUN_DATE — baking
// "a few seconds ago" into the HTML and leaving it there until the next
// deploy. The absolute date renders server-side and is swapped for the
// relative one on hydration, so the first paint is correct rather than
// merely plausible.
const [relative, setRelative] = useState<string | null>(null);
useEffect(() => {
if (!buildDate) return;
setRelative(buildDate.fromNow());
}, []);

return (
// text-pretty keeps the last line from collapsing to a single orphan word,
// which this sentence is prone to since its length shifts with the relative
// build time ("2 days ago" vs "about a minute ago").
<div className={cn("text-pretty text-gray-400", className)}>
This website is under{" "}
<CustomLink
href="https://www.ssw.com.au/rules/rules-to-better-websites-deployment"
className={LINK_CLASS}
>
continuous deployment
</CustomLink>
{buildDate && (
<>
. Last updated{" "}
{/* <time> rather than a span so the exact instant is machine-readable
and reaches keyboard and touch users, who never see a title
tooltip. A positioned tooltip element is deliberately avoided —
the old one caused horizontal overflow on mobile (#4826). */}
<time
dateTime={iso}
title={exact ?? undefined}
className={cn(
"font-medium text-gray-300",
relative && "cursor-help"
)}
>
{relative ?? exact}
</time>
</>
)}
{commitHash && repo && (
<>
. Last commit{" "}
<CustomLink
href={`https://github.com/${repo}/commit/${commitHash}`}
className={LINK_CLASS}
>
{commitHash.slice(0, 7)}
</CustomLink>
</>
)}
.
</div>
);
};
Loading
Loading