diff --git a/.github/README.md b/.github/README.md index 3d1e429..80cea35 100644 --- a/.github/README.md +++ b/.github/README.md @@ -18,7 +18,7 @@ La pipeline si attiva automaticamente per: - Configura Node.js 18 e pnpm 8 - Cache intelligente delle dipendenze -- Installazione con `pnpm install --frozen-lockfile` +- Installazione con `pnpm install` ### 2. **Lint Code** @@ -154,38 +154,6 @@ pnpm audit --fix Il progetto usa `eslint-config-react-app` - configurazione ottimale per React. -### Prettier (opzionale) - -```json -// .prettierrc -{ - "semi": true, - "trailingComma": "all", - "singleQuote": true, - "printWidth": 80, - "tabWidth": 2 -} -``` - -### VS Code Settings - -```json -// .vscode/settings.json -{ - "editor.formatOnSave": true, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - } -} -``` - -## 🎯 Next Steps - -1. **Commit** questo workflow -2. **Crea una PR** per testare la pipeline -3. **Verifica** che tutti i job passino -4. **Estendi** con test backend quando necessario - --- > **Nota**: La pipeline è progettata per essere **non-bloccante** per il workflow di sviluppo, ma **rigorosa** per la qualità del codice in produzione. diff --git a/.gitignore b/.gitignore index c6c9fd0..0a133cf 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,8 @@ yarn-error.log* # Cache /cache +team-provider-info.json + #amplify-do-not-edit-begin amplify/\#current-cloud-backend amplify/.config/local-* @@ -46,6 +48,4 @@ amplify-gradle-config.json amplifytools.xcconfig .secret-* **.sample - -team-provider-info.json #amplify-do-not-edit-end diff --git a/amplify/backend/function/getAppConfig/src/yarn.lock b/amplify/backend/function/getAppConfig/src/yarn.lock new file mode 100644 index 0000000..bbe75b9 --- /dev/null +++ b/amplify/backend/function/getAppConfig/src/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/aws-lambda@^8.10.92": + version "8.10.152" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.152.tgz#f68424a8175f0a54a2a941e65b76c3f51f3bd89d" + integrity sha512-soT/c2gYBnT5ygwiHPmd9a1bftj462NWVk2tKCc1PYHSIacB2UwbTS2zYG4jzag1mRDuzg/OjtxQjQ2NKRB6Rw== diff --git a/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json b/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json index 640b9c5..431239c 100644 --- a/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json +++ b/amplify/backend/function/homecloudSharedUtils/homecloudSharedUtils-awscloudformation-template.json @@ -20,19 +20,23 @@ } }, "Resources": { - "LambdaLayerVersionb9e104ba": { + "LambdaLayerVersiona6cb83be": { "Type": "AWS::Lambda::LayerVersion", "Properties": { - "CompatibleRuntimes": [ - "nodejs22.x" - ], + "CompatibleRuntimes": { + "Ref": "runtimes" + }, "Content": { "S3Bucket": { "Ref": "deploymentBucketName" }, - "S3Key": "amplify-builds/homecloudSharedUtils-LambdaLayerVersionb9e104ba-build.zip" + "S3Key": { + "Ref": "s3Key" + } + }, + "Description": { + "Ref": "description" }, - "Description": "Updated layer version 2025-06-27T14:08:50.394Z", "LayerName": { "Fn::Sub": [ "homecloudSharedUtils-${env}", @@ -47,11 +51,13 @@ "DeletionPolicy": "Delete", "UpdateReplacePolicy": "Retain" }, - "LambdaLayerPermissionPrivateb9e104ba": { + "LambdaLayerPermissionPrivatea6cb83be": { "Type": "AWS::Lambda::LayerVersionPermission", "Properties": { "Action": "lambda:GetLayerVersion", - "LayerVersionArn": "arn:aws:lambda:eu-north-1:274756574788:layer:homecloudSharedUtils-dev:14", + "LayerVersionArn": { + "Ref": "LambdaLayerVersiona6cb83be" + }, "Principal": { "Ref": "AWS::AccountId" } @@ -61,7 +67,7 @@ "Outputs": { "Arn": { "Value": { - "Ref": "LambdaLayerVersionb9e104ba" + "Ref": "LambdaLayerVersiona6cb83be" } } } diff --git a/amplify/backend/function/homecloudSharedUtils/parameters.json b/amplify/backend/function/homecloudSharedUtils/parameters.json index bc1a2a8..4f1be15 100644 --- a/amplify/backend/function/homecloudSharedUtils/parameters.json +++ b/amplify/backend/function/homecloudSharedUtils/parameters.json @@ -2,5 +2,5 @@ "runtimes": [ "nodejs22.x" ], - "description": "Updated layer version 2025-06-24T22:32:31.876Z" + "description": "Updated layer version 2025-07-24T17:43:32.969Z" } \ No newline at end of file diff --git a/src/components/FuzzyText/FuzzyText.jsx b/src/components/FuzzyText/FuzzyText.jsx new file mode 100644 index 0000000..72d6b98 --- /dev/null +++ b/src/components/FuzzyText/FuzzyText.jsx @@ -0,0 +1,202 @@ +import React, { useEffect, useRef } from "react"; + +const FuzzyText = ({ + children, + fontSize = "clamp(2rem, 10vw, 10rem)", + fontWeight = 900, + fontFamily = "inherit", + color = "#fff", + enableHover = true, + baseIntensity = 0.18, + hoverIntensity = 0.5, +}) => { + const canvasRef = useRef(null); + + useEffect(() => { + let animationFrameId; + let isCancelled = false; + const canvas = canvasRef.current; + if (!canvas) return; + + const init = async () => { + if (document.fonts?.ready) { + await document.fonts.ready; + } + if (isCancelled) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + const computedFontFamily = + fontFamily === "inherit" + ? window.getComputedStyle(canvas).fontFamily || "sans-serif" + : fontFamily; + + const fontSizeStr = + typeof fontSize === "number" ? `${fontSize}px` : fontSize; + let numericFontSize; + if (typeof fontSize === "number") { + numericFontSize = fontSize; + } else { + const temp = document.createElement("span"); + temp.style.fontSize = fontSize; + document.body.appendChild(temp); + const computedSize = window.getComputedStyle(temp).fontSize; + numericFontSize = parseFloat(computedSize); + document.body.removeChild(temp); + } + + const text = React.Children.toArray(children).join(""); + + const offscreen = document.createElement("canvas"); + const offCtx = offscreen.getContext("2d"); + if (!offCtx) return; + + offCtx.font = `${fontWeight} ${fontSizeStr} ${computedFontFamily}`; + offCtx.textBaseline = "alphabetic"; + const metrics = offCtx.measureText(text); + + const actualLeft = metrics.actualBoundingBoxLeft ?? 0; + const actualRight = metrics.actualBoundingBoxRight ?? metrics.width; + const actualAscent = metrics.actualBoundingBoxAscent ?? numericFontSize; + const actualDescent = + metrics.actualBoundingBoxDescent ?? numericFontSize * 0.2; + + const textBoundingWidth = Math.ceil(actualLeft + actualRight); + const tightHeight = Math.ceil(actualAscent + actualDescent); + + const extraWidthBuffer = 10; + const offscreenWidth = textBoundingWidth + extraWidthBuffer; + + offscreen.width = offscreenWidth; + offscreen.height = tightHeight; + + const xOffset = extraWidthBuffer / 2; + offCtx.font = `${fontWeight} ${fontSizeStr} ${computedFontFamily}`; + offCtx.textBaseline = "alphabetic"; + offCtx.fillStyle = color; + offCtx.fillText(text, xOffset - actualLeft, actualAscent); + + const horizontalMargin = 50; + const verticalMargin = 0; + canvas.width = offscreenWidth + horizontalMargin * 2; + canvas.height = tightHeight + verticalMargin * 2; + ctx.translate(horizontalMargin, verticalMargin); + + const interactiveLeft = horizontalMargin + xOffset; + const interactiveTop = verticalMargin; + const interactiveRight = interactiveLeft + textBoundingWidth; + const interactiveBottom = interactiveTop + tightHeight; + + let isHovering = false; + const fuzzRange = 30; + + const run = () => { + if (isCancelled) return; + ctx.clearRect( + -fuzzRange, + -fuzzRange, + offscreenWidth + 2 * fuzzRange, + tightHeight + 2 * fuzzRange + ); + const intensity = isHovering ? hoverIntensity : baseIntensity; + for (let j = 0; j < tightHeight; j++) { + const dx = Math.floor(intensity * (Math.random() - 0.5) * fuzzRange); + ctx.drawImage( + offscreen, + 0, + j, + offscreenWidth, + 1, + dx, + j, + offscreenWidth, + 1 + ); + } + animationFrameId = window.requestAnimationFrame(run); + }; + + run(); + + const isInsideTextArea = (x, y) => { + return ( + x >= interactiveLeft && + x <= interactiveRight && + y >= interactiveTop && + y <= interactiveBottom + ); + }; + + const handleMouseMove = (e) => { + if (!enableHover) return; + const rect = canvas.getBoundingClientRect(); + const x = e.clientX - rect.left; + const y = e.clientY - rect.top; + isHovering = isInsideTextArea(x, y); + }; + + const handleMouseLeave = () => { + isHovering = false; + }; + + const handleTouchMove = (e) => { + if (!enableHover) return; + e.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const touch = e.touches[0]; + const x = touch.clientX - rect.left; + const y = touch.clientY - rect.top; + isHovering = isInsideTextArea(x, y); + }; + + const handleTouchEnd = () => { + isHovering = false; + }; + + if (enableHover) { + canvas.addEventListener("mousemove", handleMouseMove); + canvas.addEventListener("mouseleave", handleMouseLeave); + canvas.addEventListener("touchmove", handleTouchMove, { + passive: false, + }); + canvas.addEventListener("touchend", handleTouchEnd); + } + + const cleanup = () => { + window.cancelAnimationFrame(animationFrameId); + if (enableHover) { + canvas.removeEventListener("mousemove", handleMouseMove); + canvas.removeEventListener("mouseleave", handleMouseLeave); + canvas.removeEventListener("touchmove", handleTouchMove); + canvas.removeEventListener("touchend", handleTouchEnd); + } + }; + + canvas.cleanupFuzzyText = cleanup; + }; + + init(); + + return () => { + isCancelled = true; + window.cancelAnimationFrame(animationFrameId); + if (canvas && canvas.cleanupFuzzyText) { + canvas.cleanupFuzzyText(); + } + }; + }, [ + children, + fontSize, + fontWeight, + fontFamily, + color, + enableHover, + baseIntensity, + hoverIntensity, + ]); + + return ; +}; + +export default FuzzyText; diff --git a/src/pages/ErrorPage.js b/src/pages/ErrorPage.js index c319c99..ba8985e 100644 --- a/src/pages/ErrorPage.js +++ b/src/pages/ErrorPage.js @@ -1,10 +1,24 @@ import React from "react"; +import { Link } from "react-router-dom"; +import "../styles/ErrorPage.css"; +import FuzzyText from "../components/FuzzyText/FuzzyText.jsx"; const ErrorPage = () => { return ( -
La pagina che stai cercando non esiste.
+Oops! La pagina che stai cercando non esiste.
+ + Torna alla Home +