Skip to content
Merged
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
34 changes: 1 addition & 33 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand Down Expand Up @@ -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.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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-*
Expand All @@ -46,6 +48,4 @@ amplify-gradle-config.json
amplifytools.xcconfig
.secret-*
**.sample

team-provider-info.json
#amplify-do-not-edit-end
8 changes: 8 additions & 0 deletions amplify/backend/function/getAppConfig/src/yarn.lock
Original file line number Diff line number Diff line change
@@ -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==
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
Expand All @@ -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"
}
Expand All @@ -61,7 +67,7 @@
"Outputs": {
"Arn": {
"Value": {
"Ref": "LambdaLayerVersionb9e104ba"
"Ref": "LambdaLayerVersiona6cb83be"
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
202 changes: 202 additions & 0 deletions src/components/FuzzyText/FuzzyText.jsx
Original file line number Diff line number Diff line change
@@ -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);
Comment on lines +145 to +150
Copy link

Copilot AI Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling preventDefault() on all touchmove events can interfere with normal scrolling behavior. Consider only preventing default when the touch is actually over the interactive area.

Suggested change
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 rect = canvas.getBoundingClientRect();
const touch = e.touches[0];
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
if (isInsideTextArea(x, y)) {
e.preventDefault();
isHovering = true;
} else {
isHovering = false;
}

Copilot uses AI. Check for mistakes.
};

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;
Copy link

Copilot AI Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a custom property 'cleanupFuzzyText' directly to the canvas DOM element is not a standard practice and could lead to memory leaks or conflicts. Consider using a WeakMap or ref-based cleanup approach instead.

Suggested change
canvas.cleanupFuzzyText = cleanup;
cleanupMap.set(canvas, cleanup);

Copilot uses AI. Check for mistakes.
};

init();

return () => {
isCancelled = true;
window.cancelAnimationFrame(animationFrameId);
if (canvas && canvas.cleanupFuzzyText) {
canvas.cleanupFuzzyText();
}
};
}, [
children,
fontSize,
fontWeight,
fontFamily,
color,
enableHover,
baseIntensity,
hoverIntensity,
]);

return <canvas ref={canvasRef} />;
};

export default FuzzyText;
20 changes: 17 additions & 3 deletions src/pages/ErrorPage.js
Original file line number Diff line number Diff line change
@@ -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 (
<div>
<h1>Errore 404</h1>
<p>La pagina che stai cercando non esiste.</p>
<div className="error-page">
<FuzzyText
baseIntensity={0.2}
hoverIntensity={0.4}
enableHover={true}
fontWeight={900}
color="#ffffff"
>
404
</FuzzyText>
<p>Oops! La pagina che stai cercando non esiste.</p>
<Link to="/" className="back-button">
Torna alla Home
</Link>
</div>
);
};
Expand Down
Loading
Loading