Second pass on localization
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import MenuBarItem from "../MenuBarItem";
|
import MenuBarItem from "../MenuBarItem";
|
||||||
import Button from "../Button";
|
import Button from "../Button";
|
||||||
import AvatarContainer from "../AvatarContainer";
|
import AvatarContainer from "../AvatarContainer";
|
||||||
@@ -11,13 +12,6 @@ import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
|||||||
import { HeaderView } from "./Header.view";
|
import { HeaderView } from "./Header.view";
|
||||||
import type { HeaderProps, NavSize } from "./Header.types";
|
import type { HeaderProps, NavSize } from "./Header.types";
|
||||||
|
|
||||||
// Configuration data for testing
|
|
||||||
export const navigationItems = [
|
|
||||||
{ href: "#", text: "Use cases", extraPadding: true },
|
|
||||||
{ href: "/learn", text: "Learn" },
|
|
||||||
{ href: "#", text: "About" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const avatarImages = [
|
export const avatarImages = [
|
||||||
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
|
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
|
||||||
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
|
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
|
||||||
@@ -46,6 +40,7 @@ export const logoConfig = [
|
|||||||
|
|
||||||
const HeaderContainer = memo<HeaderProps>(() => {
|
const HeaderContainer = memo<HeaderProps>(() => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const t = useTranslation("header");
|
||||||
|
|
||||||
// Schema markup for site navigation
|
// Schema markup for site navigation
|
||||||
const schemaData = {
|
const schemaData = {
|
||||||
@@ -60,6 +55,13 @@ const HeaderContainer = memo<HeaderProps>(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Navigation items with translations
|
||||||
|
const navigationItems = [
|
||||||
|
{ href: "#", text: t("navigation.useCases"), extraPadding: true },
|
||||||
|
{ href: "/learn", text: t("navigation.learn") },
|
||||||
|
{ href: "#", text: t("navigation.about") },
|
||||||
|
];
|
||||||
|
|
||||||
const renderNavigationItems = (size: NavSize) => {
|
const renderNavigationItems = (size: NavSize) => {
|
||||||
return navigationItems.map((item, index) => (
|
return navigationItems.map((item, index) => (
|
||||||
<MenuBarItem
|
<MenuBarItem
|
||||||
@@ -67,7 +69,7 @@ const HeaderContainer = memo<HeaderProps>(() => {
|
|||||||
href={item.href}
|
href={item.href}
|
||||||
size={item.extraPadding && size === "xsmall" ? "xsmallUseCases" : size}
|
size={item.extraPadding && size === "xsmall" ? "xsmallUseCases" : size}
|
||||||
isActive={pathname === item.href}
|
isActive={pathname === item.href}
|
||||||
ariaLabel={`Navigate to ${item.text} page`}
|
ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)}
|
||||||
>
|
>
|
||||||
{item.text}
|
{item.text}
|
||||||
</MenuBarItem>
|
</MenuBarItem>
|
||||||
@@ -94,8 +96,8 @@ const HeaderContainer = memo<HeaderProps>(() => {
|
|||||||
|
|
||||||
const renderLoginButton = (size: NavSize) => {
|
const renderLoginButton = (size: NavSize) => {
|
||||||
return (
|
return (
|
||||||
<MenuBarItem href="#" size={size} ariaLabel="Log in to your account">
|
<MenuBarItem href="#" size={size} ariaLabel={t("ariaLabels.logInToAccount")}>
|
||||||
Log in
|
{t("buttons.logIn")}
|
||||||
</MenuBarItem>
|
</MenuBarItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -108,10 +110,10 @@ const HeaderContainer = memo<HeaderProps>(() => {
|
|||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size={buttonSize}
|
size={buttonSize}
|
||||||
ariaLabel="Create a new rule with avatar decoration"
|
ariaLabel={t("ariaLabels.createNewRule")}
|
||||||
>
|
>
|
||||||
{renderAvatarGroup(containerSize, avatarSize)}
|
{renderAvatarGroup(containerSize, avatarSize)}
|
||||||
<span>Create rule</span>
|
<span>{t("buttons.createRule")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import MenuBar from "../MenuBar";
|
import MenuBar from "../MenuBar";
|
||||||
import type { HeaderViewProps } from "./Header.types";
|
import type { HeaderViewProps } from "./Header.types";
|
||||||
|
|
||||||
@@ -9,6 +12,8 @@ export function HeaderView({
|
|||||||
renderCreateRuleButton,
|
renderCreateRuleButton,
|
||||||
renderLogo,
|
renderLogo,
|
||||||
}: HeaderViewProps) {
|
}: HeaderViewProps) {
|
||||||
|
const t = useTranslation("header");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<script
|
<script
|
||||||
@@ -18,12 +23,12 @@ export function HeaderView({
|
|||||||
<header
|
<header
|
||||||
className="sticky top-0 z-50 bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]"
|
className="sticky top-0 z-50 bg-[var(--color-surface-default-primary)] w-full border-b border-[var(--border-color-default-tertiary)]"
|
||||||
role="banner"
|
role="banner"
|
||||||
aria-label="Main navigation header"
|
aria-label={t("ariaLabels.mainNavigationHeader")}
|
||||||
>
|
>
|
||||||
<nav
|
<nav
|
||||||
className="flex items-center justify-between mx-auto h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
|
className="flex items-center justify-between mx-auto h-[40px] lg:h-[84px] xl:h-[88px] px-[var(--spacing-measures-spacing-016)] py-[var(--spacing-measures-spacing-008)] lg:px-[var(--spacing-measures-spacing-64,64px)] lg:py-[var(--spacing-measures-spacing-016,16px)]"
|
||||||
role="navigation"
|
role="navigation"
|
||||||
aria-label="Main navigation"
|
aria-label={t("ariaLabels.mainNavigation")}
|
||||||
>
|
>
|
||||||
{/* Logo - Consistent left positioning across all breakpoints */}
|
{/* Logo - Consistent left positioning across all breakpoints */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import MenuBarItem from "../MenuBarItem";
|
import MenuBarItem from "../MenuBarItem";
|
||||||
import Button from "../Button";
|
import Button from "../Button";
|
||||||
import AvatarContainer from "../AvatarContainer";
|
import AvatarContainer from "../AvatarContainer";
|
||||||
@@ -11,13 +12,6 @@ import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
|||||||
import HomeHeaderView from "./HomeHeader.view";
|
import HomeHeaderView from "./HomeHeader.view";
|
||||||
import type { HomeHeaderProps, NavSize } from "./HomeHeader.types";
|
import type { HomeHeaderProps, NavSize } from "./HomeHeader.types";
|
||||||
|
|
||||||
// Configuration data for testing
|
|
||||||
export const navigationItems = [
|
|
||||||
{ href: "#", text: "Use cases", extraPadding: true },
|
|
||||||
{ href: "/learn", text: "Learn" },
|
|
||||||
{ href: "#", text: "About" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const avatarImages = [
|
export const avatarImages = [
|
||||||
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
|
{ src: getAssetPath(ASSETS.AVATAR_1), alt: "Avatar 1" },
|
||||||
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
|
{ src: getAssetPath(ASSETS.AVATAR_2), alt: "Avatar 2" },
|
||||||
@@ -54,6 +48,7 @@ export const logoConfig = [
|
|||||||
|
|
||||||
const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const t = useTranslation("header");
|
||||||
|
|
||||||
// Schema markup for site navigation (home page specific)
|
// Schema markup for site navigation (home page specific)
|
||||||
const schemaData = {
|
const schemaData = {
|
||||||
@@ -69,6 +64,13 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Navigation items with translations
|
||||||
|
const navigationItems = [
|
||||||
|
{ href: "#", text: t("navigation.useCases"), extraPadding: true },
|
||||||
|
{ href: "/learn", text: t("navigation.learn") },
|
||||||
|
{ href: "#", text: t("navigation.about") },
|
||||||
|
];
|
||||||
|
|
||||||
const renderNavigationItems = (size: NavSize) => {
|
const renderNavigationItems = (size: NavSize) => {
|
||||||
return navigationItems.map((item, index) => (
|
return navigationItems.map((item, index) => (
|
||||||
<MenuBarItem
|
<MenuBarItem
|
||||||
@@ -102,7 +104,7 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
|||||||
: "default"
|
: "default"
|
||||||
}
|
}
|
||||||
isActive={pathname === item.href}
|
isActive={pathname === item.href}
|
||||||
ariaLabel={`Navigate to ${item.text} page`}
|
ariaLabel={t("ariaLabels.navigateToPage").replace("{text}", item.text)}
|
||||||
>
|
>
|
||||||
{item.text}
|
{item.text}
|
||||||
</MenuBarItem>
|
</MenuBarItem>
|
||||||
@@ -133,9 +135,9 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
|||||||
href="#"
|
href="#"
|
||||||
size={size}
|
size={size}
|
||||||
variant={size === "xsmall" || size === "default" ? "home" : "default"}
|
variant={size === "xsmall" || size === "default" ? "home" : "default"}
|
||||||
ariaLabel="Log in to your account"
|
ariaLabel={t("ariaLabels.logInToAccount")}
|
||||||
>
|
>
|
||||||
Log in
|
{t("buttons.logIn")}
|
||||||
</MenuBarItem>
|
</MenuBarItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -149,10 +151,10 @@ const HomeHeaderContainer = memo<HomeHeaderProps>(() => {
|
|||||||
<Button
|
<Button
|
||||||
size={buttonSize}
|
size={buttonSize}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
ariaLabel="Create a new rule with avatar decoration"
|
ariaLabel={t("ariaLabels.createNewRule")}
|
||||||
>
|
>
|
||||||
{renderAvatarGroup(containerSize, avatarSize)}
|
{renderAvatarGroup(containerSize, avatarSize)}
|
||||||
<span>Create rule</span>
|
<span>{t("buttons.createRule")}</span>
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import HeaderTab from "../HeaderTab";
|
import HeaderTab from "../HeaderTab";
|
||||||
import MenuBar from "../MenuBar";
|
import MenuBar from "../MenuBar";
|
||||||
import type { HomeHeaderViewProps } from "./HomeHeader.types";
|
import type { HomeHeaderViewProps } from "./HomeHeader.types";
|
||||||
@@ -14,6 +15,8 @@ function HomeHeaderView({
|
|||||||
renderCreateRuleButton,
|
renderCreateRuleButton,
|
||||||
renderLogo,
|
renderLogo,
|
||||||
}: HomeHeaderViewProps) {
|
}: HomeHeaderViewProps) {
|
||||||
|
const t = useTranslation("homeHeader");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Script
|
<Script
|
||||||
@@ -24,12 +27,12 @@ function HomeHeaderView({
|
|||||||
<header
|
<header
|
||||||
className="w-full bg-transparent overflow-hidden"
|
className="w-full bg-transparent overflow-hidden"
|
||||||
role="banner"
|
role="banner"
|
||||||
aria-label="Home page navigation header"
|
aria-label={t("ariaLabels.homePageNavigationHeader")}
|
||||||
>
|
>
|
||||||
<nav
|
<nav
|
||||||
className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
|
className="relative flex items-center justify-between mx-auto h-[50px] sm:h-[62px] md:h-[68px] lg:h-[68px] xl:h-[88px] px-[var(--spacing-scale-008)] pr-[var(--spacing-scale-016)] pt-[var(--spacing-scale-010)] sm:px-[var(--spacing-scale-010)] sm:pr-[var(--spacing-scale-020)] sm:pt-[var(--spacing-scale-010)] md:px-[var(--spacing-scale-016)] md:pr-[var(--spacing-scale-032)] md:pt-[var(--spacing-scale-016)] lg:pl-[var(--spacing-scale-024)] lg:pt-[var(--spacing-scale-016)] lg:pr-[var(--spacing-scale-056)] xl:pl-[var(--spacing-scale-048)] xl:pt-[var(--spacing-scale-024)] xl:pr-[var(--spacing-scale-056)]"
|
||||||
role="navigation"
|
role="navigation"
|
||||||
aria-label="Main navigation"
|
aria-label={t("ariaLabels.mainNavigation")}
|
||||||
>
|
>
|
||||||
<HeaderTab className="flex items-center self-end" stretch={true}>
|
<HeaderTab className="flex items-center self-end" stretch={true}>
|
||||||
{/* Logo - Consistent left positioning within HeaderTab */}
|
{/* Logo - Consistent left positioning within HeaderTab */}
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import type { LanguageSwitcherProps, Language } from "./LanguageSwitcher.types";
|
import type { LanguageSwitcherProps, Language } from "./LanguageSwitcher.types";
|
||||||
|
|
||||||
const AVAILABLE_LANGUAGES: Language[] = [
|
|
||||||
{
|
|
||||||
code: "en",
|
|
||||||
name: "English",
|
|
||||||
nativeName: "English",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) {
|
function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) {
|
||||||
|
const t = useTranslation("languageSwitcher");
|
||||||
|
|
||||||
|
const AVAILABLE_LANGUAGES: Language[] = [
|
||||||
|
{
|
||||||
|
code: "en",
|
||||||
|
name: t("languages.english.name"),
|
||||||
|
nativeName: t("languages.english.nativeName"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<label htmlFor="language-select" className="sr-only">
|
<label htmlFor="language-select" className="sr-only">
|
||||||
Select language
|
{t("label")}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="language-select"
|
id="language-select"
|
||||||
className="bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] font-inter text-sm leading-5 font-normal border border-[var(--color-surface-default-secondary)] rounded-[var(--radius-measures-radius-small)] px-[var(--spacing-scale-012)] py-[var(--spacing-scale-008)] focus:outline-none focus:ring-2 focus:ring-[var(--color-surface-default-brand-royal)] focus:ring-offset-2 cursor-pointer"
|
className="bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] font-inter text-sm leading-5 font-normal border border-[var(--color-surface-default-secondary)] rounded-[var(--radius-measures-radius-small)] px-[var(--spacing-scale-012)] py-[var(--spacing-scale-008)] focus:outline-none focus:ring-2 focus:ring-[var(--color-surface-default-brand-royal)] focus:ring-offset-2 cursor-pointer"
|
||||||
aria-label="Select language"
|
aria-label={t("ariaLabel")}
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
{AVAILABLE_LANGUAGES.map((language) => (
|
{AVAILABLE_LANGUAGES.map((language) => (
|
||||||
@@ -30,7 +33,7 @@ function LanguageSwitcherView({ className = "" }: LanguageSwitcherProps) {
|
|||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
<p className="text-[var(--color-content-default-secondary)] font-inter text-xs leading-4 font-normal mt-[var(--spacing-scale-008)]">
|
<p className="text-[var(--color-content-default-secondary)] font-inter text-xs leading-4 font-normal mt-[var(--spacing-scale-008)]">
|
||||||
Language switching functionality coming soon
|
{t("comingSoonMessage")}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
|
import { useTranslation } from "../contexts/MessagesContext";
|
||||||
|
|
||||||
interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
|
interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -8,6 +11,7 @@ interface MenuBarProps extends React.HTMLAttributes<HTMLElement> {
|
|||||||
|
|
||||||
const MenuBar = memo<MenuBarProps>(
|
const MenuBar = memo<MenuBarProps>(
|
||||||
({ children, className = "", size = "default", ...props }) => {
|
({ children, className = "", size = "default", ...props }) => {
|
||||||
|
const t = useTranslation("menuBar");
|
||||||
const sizeStyles: Record<string, string> = {
|
const sizeStyles: Record<string, string> = {
|
||||||
xsmall:
|
xsmall:
|
||||||
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[4px]",
|
"px-[var(--spacing-scale-004)] py-[var(--spacing-scale-004)] gap-[var(--spacing-scale-001)] rounded-[4px]",
|
||||||
@@ -25,7 +29,7 @@ const MenuBar = memo<MenuBarProps>(
|
|||||||
<nav
|
<nav
|
||||||
className={baseStyles}
|
className={baseStyles}
|
||||||
role="menubar"
|
role="menubar"
|
||||||
aria-label="Main navigation menu"
|
aria-label={t("ariaLabel")}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { memo } from "react";
|
import { memo } from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import QuoteDecor from "../QuoteDecor";
|
import QuoteDecor from "../QuoteDecor";
|
||||||
import type { QuoteBlockViewProps } from "./QuoteBlock.types";
|
import type { QuoteBlockViewProps } from "./QuoteBlock.types";
|
||||||
|
|
||||||
@@ -19,6 +20,9 @@ function QuoteBlockView({
|
|||||||
onImageLoad,
|
onImageLoad,
|
||||||
onImageError,
|
onImageError,
|
||||||
}: QuoteBlockViewProps) {
|
}: QuoteBlockViewProps) {
|
||||||
|
const t = useTranslation("quoteBlock");
|
||||||
|
const avatarAlt = t("avatarAlt").replace("{author}", author);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`${config.container} ${className}`}
|
className={`${config.container} ${className}`}
|
||||||
@@ -54,7 +58,7 @@ function QuoteBlockView({
|
|||||||
{!imageError ? (
|
{!imageError ? (
|
||||||
<Image
|
<Image
|
||||||
src={currentAvatarSrc}
|
src={currentAvatarSrc}
|
||||||
alt={`Portrait of ${author}`}
|
alt={avatarAlt}
|
||||||
width={64}
|
width={64}
|
||||||
height={64}
|
height={64}
|
||||||
className={`filter sepia ${
|
className={`filter sepia ${
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import type { RuleCardViewProps } from "./RuleCard.types";
|
import type { RuleCardViewProps } from "./RuleCard.types";
|
||||||
|
|
||||||
export function RuleCardView({
|
export function RuleCardView({
|
||||||
@@ -9,12 +12,15 @@ export function RuleCardView({
|
|||||||
onClick,
|
onClick,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
}: RuleCardViewProps) {
|
}: RuleCardViewProps) {
|
||||||
|
const t = useTranslation("ruleCard");
|
||||||
|
const ariaLabel = t("ariaLabel").replace("{title}", title);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${backgroundColor} rounded-[var(--radius-measures-radius-small)] pt-[var(--spacing-scale-012)] pr-[var(--spacing-scale-012)] pl-[var(--spacing-scale-012)] pb-[var(--spacing-scale-024)] md:p-[var(--spacing-scale-024)] md:h-[210px] lg:h-[277px] flex flex-col gap-[18px] shadow-lg backdrop-blur-sm transition-all duration-500 ease-in-out hover:shadow-xl hover:scale-[1.02] focus:outline-none focus:ring-2 focus:ring-[var(--color-community-teal-500)] focus:ring-offset-2 cursor-pointer min-h-[44px] min-w-[44px] ${className}`}
|
className={`${backgroundColor} rounded-[var(--radius-measures-radius-small)] pt-[var(--spacing-scale-012)] pr-[var(--spacing-scale-012)] pl-[var(--spacing-scale-012)] pb-[var(--spacing-scale-024)] md:p-[var(--spacing-scale-024)] md:h-[210px] lg:h-[277px] flex flex-col gap-[18px] shadow-lg backdrop-blur-sm transition-all duration-500 ease-in-out hover:shadow-xl hover:scale-[1.02] focus:outline-none focus:ring-2 focus:ring-[var(--color-community-teal-500)] focus:ring-offset-2 cursor-pointer min-h-[44px] min-w-[44px] ${className}`}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
role="button"
|
role="button"
|
||||||
aria-label={`Learn more about ${title} governance pattern`}
|
aria-label={ariaLabel}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import { useTranslation } from "../../contexts/MessagesContext";
|
||||||
import RuleCard from "../RuleCard";
|
import RuleCard from "../RuleCard";
|
||||||
import Button from "../Button";
|
import Button from "../Button";
|
||||||
import { getAssetPath } from "../../../lib/assetUtils";
|
import { getAssetPath } from "../../../lib/assetUtils";
|
||||||
@@ -8,77 +11,79 @@ export function RuleStackView({
|
|||||||
className,
|
className,
|
||||||
onTemplateClick,
|
onTemplateClick,
|
||||||
}: RuleStackViewProps) {
|
}: RuleStackViewProps) {
|
||||||
|
const t = useTranslation("ruleStack");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`w-full bg-transparent py-[var(--spacing-scale-032)] px-[var(--spacing-scale-020)] md:py-[var(--spacing-scale-048)] md:px-[var(--spacing-scale-032)] xmd:py-[var(--spacing-scale-056)] xmd:px-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)] lg:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] xl:px-[var(--spacing-scale-096)] flex flex-col gap-[var(--spacing-scale-024)] xmd:gap-[var(--spacing-scale-032)] lg:gap-[var(--spacing-scale-040)] ${className}`}
|
className={`w-full bg-transparent py-[var(--spacing-scale-032)] px-[var(--spacing-scale-020)] md:py-[var(--spacing-scale-048)] md:px-[var(--spacing-scale-032)] xmd:py-[var(--spacing-scale-056)] xmd:px-[var(--spacing-scale-032)] lg:py-[var(--spacing-scale-064)] lg:px-[var(--spacing-scale-064)] xl:py-[var(--spacing-scale-064)] xl:px-[var(--spacing-scale-096)] flex flex-col gap-[var(--spacing-scale-024)] xmd:gap-[var(--spacing-scale-032)] lg:gap-[var(--spacing-scale-040)] ${className}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-[18px] xmd:grid xmd:grid-cols-2 lg:gap-[var(--spacing-scale-024)]">
|
<div className="flex flex-col gap-[18px] xmd:grid xmd:grid-cols-2 lg:gap-[var(--spacing-scale-024)]">
|
||||||
<RuleCard
|
<RuleCard
|
||||||
title="Consensus clusters"
|
title={t("cards.consensusClusters.title")}
|
||||||
description="Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council."
|
description={t("cards.consensusClusters.description")}
|
||||||
icon={
|
icon={
|
||||||
<Image
|
<Image
|
||||||
src={getAssetPath("assets/Icon_Sociocracy.svg")}
|
src={getAssetPath("assets/Icon_Sociocracy.svg")}
|
||||||
alt="Sociocracy"
|
alt={t("cards.consensusClusters.iconAlt")}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
backgroundColor="bg-[var(--color-surface-default-brand-lime)]"
|
backgroundColor="bg-[var(--color-surface-default-brand-lime)]"
|
||||||
onClick={() => onTemplateClick("Consensus clusters")}
|
onClick={() => onTemplateClick(t("cards.consensusClusters.title"))}
|
||||||
/>
|
/>
|
||||||
<RuleCard
|
<RuleCard
|
||||||
title="Consensus"
|
title={t("cards.consensus.title")}
|
||||||
description="Decisions that affect the group collectively should involve participation of all participants."
|
description={t("cards.consensus.description")}
|
||||||
icon={
|
icon={
|
||||||
<Image
|
<Image
|
||||||
src={getAssetPath("assets/Icon_Consensus.svg")}
|
src={getAssetPath("assets/Icon_Consensus.svg")}
|
||||||
alt="Consensus"
|
alt={t("cards.consensus.iconAlt")}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
backgroundColor="bg-[var(--color-surface-default-brand-rust)]"
|
backgroundColor="bg-[var(--color-surface-default-brand-rust)]"
|
||||||
onClick={() => onTemplateClick("Consensus")}
|
onClick={() => onTemplateClick(t("cards.consensus.title"))}
|
||||||
/>
|
/>
|
||||||
<RuleCard
|
<RuleCard
|
||||||
title="Elected Board"
|
title={t("cards.electedBoard.title")}
|
||||||
description="An elected board determines policies and organizes their implementation."
|
description={t("cards.electedBoard.description")}
|
||||||
icon={
|
icon={
|
||||||
<Image
|
<Image
|
||||||
src={getAssetPath("assets/Icon_ElectedBoard.svg")}
|
src={getAssetPath("assets/Icon_ElectedBoard.svg")}
|
||||||
alt="Elected Board"
|
alt={t("cards.electedBoard.iconAlt")}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
backgroundColor="bg-[var(--color-surface-default-brand-red)]"
|
backgroundColor="bg-[var(--color-surface-default-brand-red)]"
|
||||||
onClick={() => onTemplateClick("Elected Board")}
|
onClick={() => onTemplateClick(t("cards.electedBoard.title"))}
|
||||||
/>
|
/>
|
||||||
<RuleCard
|
<RuleCard
|
||||||
title="Petition"
|
title={t("cards.petition.title")}
|
||||||
description="All participants can propose and vote on proposals for the group."
|
description={t("cards.petition.description")}
|
||||||
icon={
|
icon={
|
||||||
<Image
|
<Image
|
||||||
src={getAssetPath("assets/Icon_Petition.svg")}
|
src={getAssetPath("assets/Icon_Petition.svg")}
|
||||||
alt="Petition"
|
alt={t("cards.petition.iconAlt")}
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
className="md:w-[56px] md:h-[56px] lg:w-[90px] lg:h-[90px]"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
backgroundColor="bg-[var(--color-surface-default-brand-teal)]"
|
backgroundColor="bg-[var(--color-surface-default-brand-teal)]"
|
||||||
onClick={() => onTemplateClick("Petition")}
|
onClick={() => onTemplateClick(t("cards.petition.title"))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* See all templates button */}
|
{/* See all templates button */}
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Button variant="outlined" size="large">
|
<Button variant="outlined" size="large">
|
||||||
See all templates
|
{t("button.seeAllTemplates")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"navigation": {
|
||||||
|
"useCases": "Use cases",
|
||||||
|
"learn": "Learn",
|
||||||
|
"about": "About"
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"logIn": "Log in",
|
||||||
|
"createRule": "Create rule"
|
||||||
|
},
|
||||||
|
"ariaLabels": {
|
||||||
|
"mainNavigationHeader": "Main navigation header",
|
||||||
|
"mainNavigation": "Main navigation",
|
||||||
|
"navigateToPage": "Navigate to {text} page",
|
||||||
|
"logInToAccount": "Log in to your account",
|
||||||
|
"createNewRule": "Create a new rule with avatar decoration"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"ariaLabels": {
|
||||||
|
"homePageNavigationHeader": "Home page navigation header",
|
||||||
|
"mainNavigation": "Main navigation"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"label": "Select language",
|
||||||
|
"ariaLabel": "Select language",
|
||||||
|
"comingSoonMessage": "Language switching functionality coming soon",
|
||||||
|
"languages": {
|
||||||
|
"english": {
|
||||||
|
"name": "English",
|
||||||
|
"nativeName": "English"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ariaLabel": "Main navigation menu"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"avatarAlt": "Portrait of {author}"
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"ariaLabel": "Learn more about {title} governance pattern"
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"cards": {
|
||||||
|
"consensusClusters": {
|
||||||
|
"title": "Consensus clusters",
|
||||||
|
"description": "Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council.",
|
||||||
|
"iconAlt": "Sociocracy"
|
||||||
|
},
|
||||||
|
"consensus": {
|
||||||
|
"title": "Consensus",
|
||||||
|
"description": "Decisions that affect the group collectively should involve participation of all participants.",
|
||||||
|
"iconAlt": "Consensus"
|
||||||
|
},
|
||||||
|
"electedBoard": {
|
||||||
|
"title": "Elected Board",
|
||||||
|
"description": "An elected board determines policies and organizes their implementation.",
|
||||||
|
"iconAlt": "Elected Board"
|
||||||
|
},
|
||||||
|
"petition": {
|
||||||
|
"title": "Petition",
|
||||||
|
"description": "All participants can propose and vote on proposals for the group.",
|
||||||
|
"iconAlt": "Petition"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"button": {
|
||||||
|
"seeAllTemplates": "See all templates"
|
||||||
|
},
|
||||||
|
"ariaLabel": "Learn more about {title} governance pattern"
|
||||||
|
}
|
||||||
@@ -4,6 +4,13 @@ import numberedCards from "./components/numberedCards.json";
|
|||||||
import askOrganizer from "./components/askOrganizer.json";
|
import askOrganizer from "./components/askOrganizer.json";
|
||||||
import featureGrid from "./components/featureGrid.json";
|
import featureGrid from "./components/featureGrid.json";
|
||||||
import footer from "./components/footer.json";
|
import footer from "./components/footer.json";
|
||||||
|
import header from "./components/header.json";
|
||||||
|
import homeHeader from "./components/homeHeader.json";
|
||||||
|
import languageSwitcher from "./components/languageSwitcher.json";
|
||||||
|
import menuBar from "./components/menuBar.json";
|
||||||
|
import quoteBlock from "./components/quoteBlock.json";
|
||||||
|
import ruleCard from "./components/ruleCard.json";
|
||||||
|
import ruleStack from "./components/ruleStack.json";
|
||||||
import navigation from "./navigation.json";
|
import navigation from "./navigation.json";
|
||||||
import metadata from "./metadata.json";
|
import metadata from "./metadata.json";
|
||||||
|
|
||||||
@@ -14,6 +21,13 @@ export default {
|
|||||||
askOrganizer,
|
askOrganizer,
|
||||||
featureGrid,
|
featureGrid,
|
||||||
footer,
|
footer,
|
||||||
|
header,
|
||||||
|
homeHeader,
|
||||||
|
languageSwitcher,
|
||||||
|
menuBar,
|
||||||
|
quoteBlock,
|
||||||
|
ruleCard,
|
||||||
|
ruleStack,
|
||||||
navigation,
|
navigation,
|
||||||
metadata,
|
metadata,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user