The site now presents one freelance developer on one page: hero, introduction, four typical cases (developer for a product, make an app reliable, automate a process, a website) with what you get, how it runs and what it costs, a process automation section, technology and project fit, how I work, two live references, questions and contact. - Hero and closing headline are drawn from running binary digits. - Each case explains its four steps with an animated drawing; the automation section shows example workflows with work travelling between systems; a pipeline run shows what a test catches before a release. - Contact is one email address, kept in parts and assembled in the browser so it never appears in the page source (src/domain/email.ts, spec first). - Imprint and privacy pages added. - The old site (blog, case studies, contact form, fixed-price pages, video rendering and related scripts) is removed. Its URLs redirect to the homepage. - The deploy smoke test checks the legal pages and the redirects instead of the removed case study. - Dev scripts use docker compose v2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
34 lines
977 B
TypeScript
34 lines
977 B
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import { CONTACT_EMAIL_PARTS } from "@/src/content/site";
|
|
import { assembleEmail, spellOutEmail } from "@/src/domain/email";
|
|
|
|
/**
|
|
* EmailLink: shows the contact address without putting it into the page source.
|
|
* The server renders a spelled-out form ("name [at] domain [dot] tld"). The real
|
|
* address and the mailto link are only assembled in the browser. This keeps simple
|
|
* address harvesters out; it does not stop a bot that runs JavaScript.
|
|
*/
|
|
export const EmailLink: React.FC<{ className?: string }> = ({
|
|
className = "",
|
|
}) => {
|
|
const [address, setAddress] = React.useState<string | null>(null);
|
|
|
|
React.useEffect(() => {
|
|
setAddress(assembleEmail(CONTACT_EMAIL_PARTS));
|
|
}, []);
|
|
|
|
if (address === null) {
|
|
return (
|
|
<span className={className}>{spellOutEmail(CONTACT_EMAIL_PARTS)}</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<a href={`mailto:${address}`} className={className}>
|
|
{address}
|
|
</a>
|
|
);
|
|
};
|