All files / src/components/ui Footer.jsx

83.33% Statements 5/6
60% Branches 3/5
100% Functions 3/3
100% Lines 5/5

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                              75x   75x                                             15x   15x                                                                                                 75x                                                  
import React from 'react';
import * as icons from 'simple-icons';
import {useTranslation} from "react-i18next";
 
/**
 * BrandIcon renders an SVG icon from the simple-icons set or custom SVG data.
 *
 * @param {object} props - Component props.
 * @param {object} props.icon - Icon object containing `svg` string.
 * @param {string} props.color - Fill color for the SVG.
 * @param {string} [props.className] - Additional CSS classes.
 * @param {number} [props.size=24] - Width and height of the icon.
 * @returns {JSX.Element|null} SVG element or null if no valid icon.
 */
function BrandIcon({icon, color, className, size = 24}) {
    Iif (!icon || !icon.svg) return null;
 
    return (
        <svg
            role="img"
            viewBox="0 0 24 24"
            width={size}
            height={size}
            fill={color}
            className={className}
            dangerouslySetInnerHTML={{__html: icon.svg}}
            xmlns="http://www.w3.org/2000/svg"
        />
    );
}
 
/**
 * Footer component with social brand icons and copyright text.
 * Uses i18next for translations.
 *
 * @component
 * @module components/ui/Footer
 * @returns {JSX.Element} The Footer element with icon links and copyright.
 */
export default function Footer() {
    const {t} = useTranslation();
 
    return (
        <footer
            className="sticky bottom-0 z-50 backdrop-blur-sm backdrop-saturate-150
               bg-gray-200/60 dark:bg-gray-900/80 text-gray-900 dark:text-gray-400 shadow-inner">
            <div className="max-w-7xl mx-auto px-4 py-4 sm:py-6">
                {/* Responsive Icon Grid */}
                <div
                    className="flex flex-wrap justify-center gap-x-4 gap-y-3 sm:space-x-6 sm:flex-nowrap sm:gap-y-0 mb-3 sm:mb-4">
                    {[
                        {
                            key: "linkedin",
                            href: "https://www.linkedin.com/in/daniele-masone",
                            icon: {
                                svg: `<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.85-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667h-3.554V9h3.414v1.561h.049c.477-.9 1.637-1.85 3.367-1.85 3.598 0 4.265 2.368 4.265 5.452v6.289zM5.337 7.433a2.063 2.063 0 1 1 0-4.126 2.063 2.063 0 0 1 0 4.126zM6.814 20.452H3.861V9h2.953v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.723v20.549C0 23.226.792 24 1.771 24h20.451C23.2 24 24 23.226 24 22.272V1.723C24 .774 23.2 0 22.225 0z"/>`,
                            },
                            color: "#0A66C2",
                            label: "linkedin",
                            className: "hover:opacity-80 transition"
                        },
                        {
                            key: "instagram",
                            href: "https://www.instagram.com/ing_daniele_masone/",
                            icon: icons.siInstagram,
                            color: "#E4405F",
                            label: "Instagram"
                        },
                        {
                            key: "facebook",
                            href: "https://www.facebook.com/danieleMasone",
                            icon: icons.siFacebook,
                            color: "#1877F2",
                            label: "Facebook"
                        },
                        {
                            key: "github",
                            href: "https://github.com/DanieleMasone",
                            icon: icons.siGithub,
                            color: "currentColor",
                            label: "GitHub",
                            className: "text-[#181717] dark:text-gray-200"
                        },
                        {
                            key: "udemy",
                            href: "https://www.udemy.com/user/daniele-masone/",
                            icon: icons.siUdemy,
                            color: "#EC5252",
                            label: "Udemy"
                        }
                    ].map(({key, href, icon, color, label, className}) => (
                        <a
                            key={key}
                            href={href}
                            target="_blank"
                            rel="noopener noreferrer"
                            aria-label={label}
                            className="p-2 hover:opacity-80 transition rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-blue-500"
                        >
                            <BrandIcon
                                icon={icon}
                                color={color}
                                className={className}
                                size={28}
                            />
                        </a>
                    ))}
                </div>
 
                <p className="text-xs sm:text-sm text-gray-700 dark:text-gray-400 text-center">
                    {t("footer_copyright")}
                </p>
            </div>
        </footer>
    );
}