Powered by
diff --git a/components/pagespeed/PageSpeedInsights.tsx b/components/pagespeed/PageSpeedInsights.tsx
deleted file mode 100644
index e45a1be..0000000
--- a/components/pagespeed/PageSpeedInsights.tsx
+++ /dev/null
@@ -1,348 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-"use client";
-
-import { useState, useEffect } from "react";
-import {
- Card,
- CardContent,
- CardFooter,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
-import { Button } from "@/components/ui/button";
-import { Badge } from "@/components/ui/badge";
-import { Skeleton } from "@/components/ui/skeleton";
-import { Progress } from "@/components/ui/progress";
-import { Separator } from "@/components/ui/separator";
-import { SiGooglecloud } from "react-icons/si";
-import { HiDesktopComputer } from "react-icons/hi";
-import { HiDevicePhoneMobile } from "react-icons/hi2";
-import type {
- PageSpeedResult,
- PageSpeedInsightsProps,
-} from "@/types/configs/pagespeed";
-import { usePageSpeedData } from "@/hooks/use-pageSpeed-data";
-
-const getScoreBadgeColor = (score: number): string => {
- if (score >= 90)
- return "bg-emerald-100 text-emerald-800 border-emerald-200 dark:bg-emerald-900/20 dark:text-emerald-400 dark:border-emerald-800";
- if (score >= 50)
- return "bg-amber-100 text-amber-800 border-amber-200 dark:bg-amber-900/20 dark:text-amber-400 dark:border-amber-800";
- return "bg-red-100 text-red-800 border-red-200 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800";
-};
-
-const getCacheStatusInfo = (
- status: "fresh" | "updating" | "updated" | null
-) => {
- switch (status) {
- case "fresh":
- return {
- label: "Fresh",
- className:
- "bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-900/20 dark:text-emerald-400",
- };
- case "updating":
- return {
- label: "Updating",
- className:
- "bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-900/20 dark:text-amber-400",
- };
- case "updated":
- return {
- label: "Updated",
- className:
- "bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-400",
- };
- default:
- return null;
- }
-};
-
-const MetricsSkeleton = () => (
-
- {Array.from({ length: 4 }).map((_, i) => (
-
-
-
-
- ))}
-
-);
-
-const LoadingSkeleton = ({
- progress,
- progressLabel,
-}: {
- progress: number;
- progressLabel: string;
-}) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {progressLabel}
- {progress}%
-
-
-
-
-
-
-
-
-
-);
-
-const MetricsDisplay = ({ data }: { data: PageSpeedResult }) => (
-
-
- Performance Metrics
-
-
- {Object.entries(data.metrics).map(([key, score]) => (
-
-
- {key === "bestPractices" ? "Best Practices" : key}
-
-
- {score as number}
-
-
- ))}
-
-
-);
-
-export default function PageSpeedInsights({
- url = "https://www.coldbydefault.com",
- showRefreshButton = true,
- showBothStrategies = true,
-}: PageSpeedInsightsProps) {
- const [activeStrategy, setActiveStrategy] = useState<"mobile" | "desktop">(
- "desktop"
- );
- const [progress, setProgress] = useState(0);
- const [progressLabel, setProgressLabel] = useState("Initializing...");
-
- const {
- mobileData,
- desktopData,
- loading,
- cacheStatus,
- lastUpdated,
- refresh,
- } = usePageSpeedData({ url, showBothStrategies });
-
- // Progress simulation based on loading state
- useEffect(() => {
- if (loading) {
- // Start progress
- const updateProgress = (value: number, label: string) => {
- setProgress(value);
- setProgressLabel(label);
- };
-
- updateProgress(0, "Connecting to PageSpeed API...");
-
- const progressTimer = setTimeout(() => {
- updateProgress(25, "Analyzing website performance...");
-
- const midTimer = setTimeout(() => {
- updateProgress(60, "Processing metrics...");
- }, 800);
-
- return () => clearTimeout(midTimer);
- }, 300);
-
- return () => clearTimeout(progressTimer);
- } else {
- // Complete progress when data is loaded
- const finalizeProgress = () => {
- setProgress(100);
- setProgressLabel("Analysis complete!");
- };
- finalizeProgress();
- return undefined;
- }
- }, [loading]);
-
- const getCurrentData = () => {
- return activeStrategy === "mobile" ? mobileData : desktopData;
- };
-
- const data = getCurrentData();
-
- if (loading) {
- return (
-
- );
- }
-
- // Error case removed - we always show data (mock or real)
- // The hook handles errors silently and shows mock data instead
-
- if (!data) {
- // This should never happen since we always have mock data
- return (
-
- );
- }
-
- const cacheInfo = getCacheStatusInfo(cacheStatus);
-
- return (
-
-
-
-
-
- PageSpeed Insights
-
- Powered by Google
-
-
- {showBothStrategies && (
-
-
-
-
- )}
- {!showBothStrategies && (
-
- {data.strategy === "desktop" ? (
-
- ) : (
-
- )}
-
- {data.strategy}
-
-
- )}
-
- {data.url}
-
-
-
-
-
- {showRefreshButton && (
- <>
-
-
- {cacheInfo && (
-
- Cache Status:
-
- {cacheInfo.label}
-
-
- )}
-
-
- >
- )}
-
-
-
-
-
-
- {/* Green status dot with inline styles as fallback */}
-
-
-
- {lastUpdated ? (
- <>
- API Online • Last updated:{" "}
- {new Date(lastUpdated).toLocaleDateString()} at{" "}
- {new Date(lastUpdated).toLocaleTimeString([], {
- hour: "2-digit",
- minute: "2-digit",
- })}
- >
- ) : (
- "Loading..."
- )}
-
-
- {cacheStatus === "updating" && (
-
- 📡 Auto-refreshing in background
-
- )}
-
-
-
-
- );
-}
diff --git a/components/pagespeed/index.ts b/components/pagespeed/index.ts
deleted file mode 100644
index 74d5054..0000000
--- a/components/pagespeed/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-export { default as PageSpeedInsights } from "./PageSpeedInsights";
-export { usePageSpeedData } from "../../hooks/use-pageSpeed-data";
diff --git a/data/hubs/portfolio-section.data.ts b/data/hubs/portfolio-section.data.ts
index fac5b3e..6e37492 100644
--- a/data/hubs/portfolio-section.data.ts
+++ b/data/hubs/portfolio-section.data.ts
@@ -1,7 +1,7 @@
/**
* @author ColdByDefault
* @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
+ */
import {
Code,
@@ -80,7 +80,7 @@ export const dataNodes: ArchitectureNode[] = [
{
icon: Target,
title: "API Routes Structure",
- subtitle: "RESTful Endpoints + GitHub API + PageSpeed API",
+ subtitle: "RESTful Endpoints + GitHub API",
color: "bg-orange-500/10 text-orange-600",
},
{
@@ -332,46 +332,6 @@ export function CertificationShowcaseMobile({
);
-}`,
- },
- {
- title: "API Data Hook",
- language: "TypeScript",
- code: `// Data fetching with caching and error handling
-export function usePageSpeedData({
- url,
- showBothStrategies = true,
-}: UsePageSpeedDataProps): UsePageSpeedDataReturn {
- const [mobileData, setMobileData] = useState
(null);
- const [desktopData, setDesktopData] = useState(null);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
- const [cacheStatus, setCacheStatus] = useState<
- "fresh" | "updating" | "updated" | null
- >(null);
-
- const fetchStrategy = useCallback(async (
- strategy: "mobile" | "desktop",
- forceRefresh = false
- ): Promise => {
- try {
- const queryParams = new URLSearchParams({ url, strategy });
- if (forceRefresh) queryParams.append("refresh", "true");
-
- const response = await fetch(\`/api/pagespeed?\${queryParams}\`);
- const result = await response.json();
-
- if (strategy === "mobile") {
- setMobileData(result);
- } else {
- setDesktopData(result);
- }
- } catch (err) {
- setError(err instanceof Error ? err.message : "An error occurred");
- }
- }, [url]);
-
- return { mobileData, desktopData, loading, error, cacheStatus, refresh };
}`,
},
];
@@ -415,7 +375,6 @@ export const routeStructure = {
"api/blog/*",
"api/chatbot/*",
"api/github/*",
- "api/pagespeed/*",
],
};
@@ -436,7 +395,7 @@ export const componentStructure = {
{
folder: "hooks/",
description: "Global reusable hooks",
- examples: ["use-mobile.ts", "use-language.ts", "use-pageSpeed-data.ts"],
+ examples: ["use-mobile.ts", "use-language.ts"],
},
{
folder: "lib/",
diff --git a/data/main/certificationsData.ts b/data/main/certificationsData.ts
index 87482ad..3ec02a3 100644
--- a/data/main/certificationsData.ts
+++ b/data/main/certificationsData.ts
@@ -1,9 +1,20 @@
/**
* @author ColdByDefault
* @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
+ */
export const certifications = [
+ {
+ id: 4,
+ title: "Computer Science Expert",
+ image: "/default.png",
+ issuer: "IHK Rhein-Neckar (German Chamber of Commerce and Industry)",
+ issuerKey: "ihkRheinNeckar",
+ description:
+ "Subject Area: Software Development and AI, as per § 37 German Vocational Training Act (BBiG).",
+ descriptionKey: "computerScienceExpert",
+ date: "2026",
+ },
{
id: 1,
title: "Python PCEP",
diff --git a/hooks/use-chatbot.ts b/hooks/use-chatbot.ts
index 978a5d5..888ede5 100644
--- a/hooks/use-chatbot.ts
+++ b/hooks/use-chatbot.ts
@@ -2,7 +2,7 @@
* ChatBot Custom Hook - Manages state and API communication
* @author ColdByDefault
* @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
+ */
"use client";
@@ -12,6 +12,7 @@ import type {
ChatBotResponse,
ChatBotApiError,
} from "@/types/configs/chatbot";
+import { CHATBOT_FALLBACK_MESSAGES } from "@/components/chatbot/ChatBot.constants";
interface UseChatBotReturn {
messages: ChatMessage[];
@@ -48,7 +49,7 @@ export function useChatBot(): UseChatBotReturn {
(msg) => ({
...msg,
timestamp: new Date(msg.timestamp),
- })
+ }),
);
setMessages(messagesWithDates);
}
@@ -56,9 +57,8 @@ export function useChatBot(): UseChatBotReturn {
if (savedSession) {
sessionIdRef.current = savedSession;
}
- } catch (error) {
- console.error("Failed to load chat history from localStorage:", error);
- // Clear corrupted data
+ } catch {
+ // Clear corrupted data silently
localStorage.removeItem(STORAGE_KEY_MESSAGES);
localStorage.removeItem(STORAGE_KEY_SESSION);
}
@@ -70,8 +70,8 @@ export function useChatBot(): UseChatBotReturn {
if (typeof window !== "undefined" && messages.length > 0) {
try {
localStorage.setItem(STORAGE_KEY_MESSAGES, JSON.stringify(messages));
- } catch (error) {
- console.error("Failed to save chat history to localStorage:", error);
+ } catch {
+ // Storage quota exceeded or disabled - fail silently
}
}
}, [messages]);
@@ -81,8 +81,8 @@ export function useChatBot(): UseChatBotReturn {
if (typeof window !== "undefined" && sessionIdRef.current) {
try {
localStorage.setItem(STORAGE_KEY_SESSION, sessionIdRef.current);
- } catch (error) {
- console.error("Failed to save session ID to localStorage:", error);
+ } catch {
+ // Storage quota exceeded or disabled - fail silently
}
}
}, []);
@@ -174,8 +174,8 @@ export function useChatBot(): UseChatBotReturn {
// Update user message status
setMessages((prev) =>
prev.map((msg) =>
- msg.id === userMessage.id ? { ...msg, status: "sent" } : msg
- )
+ msg.id === userMessage.id ? { ...msg, status: "sent" } : msg,
+ ),
);
if (!response.ok || !("success" in data) || !data.success) {
@@ -185,7 +185,7 @@ export function useChatBot(): UseChatBotReturn {
if (errorData.code === "QUOTA_EXCEEDED" && errorData.retryAfter) {
const retrySeconds = Math.ceil(errorData.retryAfter);
throw new Error(
- `Reem is taking a short break. Please try again in ${retrySeconds} seconds.`
+ `Reem is taking a short break. Please try again in ${retrySeconds} seconds.`,
);
}
@@ -203,8 +203,8 @@ export function useChatBot(): UseChatBotReturn {
if (typeof window !== "undefined") {
try {
localStorage.setItem(STORAGE_KEY_SESSION, data.data.sessionId);
- } catch (error) {
- console.error("Failed to save session ID to localStorage:", error);
+ } catch {
+ // Storage quota exceeded or disabled - fail silently
}
}
@@ -222,32 +222,53 @@ export function useChatBot(): UseChatBotReturn {
setIsConnected(true);
} catch (err) {
- console.error("ChatBot error:", err);
-
// Update user message status to error
setMessages((prev) =>
prev.map((msg) =>
- msg.id === userMessage.id ? { ...msg, status: "error" } : msg
- )
+ msg.id === userMessage.id ? { ...msg, status: "error" } : msg,
+ ),
);
- // Set error message
- const errorMessage =
- err instanceof Error ? err.message : "Failed to send message";
- setError(errorMessage);
-
- // Check if it's a connection issue
- if (
- errorMessage.includes("fetch") ||
- errorMessage.includes("network")
- ) {
- setIsConnected(false);
+ // Determine appropriate fallback message
+ let errorMessage = CHATBOT_FALLBACK_MESSAGES.GENERIC_ERROR;
+
+ if (err instanceof Error) {
+ const errMsg = err.message.toLowerCase();
+
+ if (
+ errMsg.includes("network") ||
+ errMsg.includes("fetch") ||
+ errMsg.includes("connection")
+ ) {
+ errorMessage = CHATBOT_FALLBACK_MESSAGES.NETWORK_ERROR;
+ setIsConnected(false);
+ } else if (
+ errMsg.includes("quota") ||
+ errMsg.includes("rate limit") ||
+ errMsg.includes("too many")
+ ) {
+ errorMessage = CHATBOT_FALLBACK_MESSAGES.RATE_LIMIT_ERROR;
+ } else if (errMsg.includes("taking a short break")) {
+ errorMessage = CHATBOT_FALLBACK_MESSAGES.QUOTA_EXCEEDED;
+ } else if (errMsg.includes("timeout")) {
+ errorMessage = CHATBOT_FALLBACK_MESSAGES.TIMEOUT_ERROR;
+ } else if (
+ errMsg.includes("validation") ||
+ errMsg.includes("invalid")
+ ) {
+ errorMessage = CHATBOT_FALLBACK_MESSAGES.VALIDATION_ERROR;
+ } else if (err.message && !errMsg.includes("failed to send")) {
+ // Use the original error message if it's user-friendly
+ errorMessage = err.message;
+ }
}
+
+ setError(errorMessage);
} finally {
setIsLoading(false);
}
},
- [isLoading]
+ [isLoading],
);
return {
diff --git a/hooks/use-pageSpeed-data.ts b/hooks/use-pageSpeed-data.ts
deleted file mode 100644
index a09cd28..0000000
--- a/hooks/use-pageSpeed-data.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- */
-
-"use client";
-
-import { useState, useEffect, useCallback, useRef } from "react";
-import type {
- PageSpeedResult,
- PageSpeedApiResponse,
-} from "@/types/configs/pagespeed";
-
-interface UsePageSpeedDataProps {
- url: string;
- showBothStrategies?: boolean;
-}
-
-interface UsePageSpeedDataReturn {
- mobileData: PageSpeedResult | null;
- desktopData: PageSpeedResult | null;
- loading: boolean;
- error: string | null;
- cacheStatus: "fresh" | "updating" | "updated" | null;
- lastUpdated: string | null;
- refresh: () => void;
-}
-
-// Fallback mock data - shown immediately while real data loads
-const createMockData = (
- strategy: "mobile" | "desktop",
- url: string,
-): PageSpeedResult => ({
- url,
- strategy,
- metrics: {
- performance: strategy === "desktop" ? 91 : 94,
- accessibility: 93,
- bestPractices: 98,
- seo: 100,
- },
-});
-
-export function usePageSpeedData({
- url,
- showBothStrategies = true,
-}: UsePageSpeedDataProps): UsePageSpeedDataReturn {
- // Initialize with mock data immediately - users see data right away
- const [mobileData, setMobileData] = useState(() =>
- createMockData("mobile", url),
- );
- const [desktopData, setDesktopData] = useState(() =>
- createMockData("desktop", url),
- );
- // Start with loading=false since we have mock data
- const [loading] = useState(false);
- // Never expose errors to users - always null
- const [error] = useState(null);
- const [cacheStatus, setCacheStatus] = useState<
- "fresh" | "updating" | "updated" | null
- >("fresh");
- const [lastUpdated, setLastUpdated] = useState(() =>
- new Date().toISOString(),
- );
-
- // Track if we've fetched real data
- const hasRealData = useRef({ mobile: false, desktop: false });
- const isDevModeDisabled = useRef(false); // Track if API is disabled in dev
- const retryTimeoutRef = useRef | null>(null);
- const fetchFnRef = useRef<((forceRefresh?: boolean) => Promise) | null>(
- null,
- );
-
- const fetchStrategy = useCallback(
- async (
- strategy: "mobile" | "desktop",
- forceRefresh = false,
- ): Promise => {
- try {
- const queryParams = new URLSearchParams({
- url,
- strategy,
- });
-
- if (forceRefresh) {
- queryParams.append("refresh", "true");
- }
-
- const response = await fetch(
- `/api/pagespeed?${queryParams.toString()}`,
- {
- headers: { "X-Client-ID": "pagespeed-component" },
- signal: AbortSignal.timeout(50000),
- },
- );
-
- // Extract and simplify cache status
- const xCache = response.headers.get("X-Cache");
- if (xCache) {
- if (xCache === "HIT") setCacheStatus("fresh");
- else if (xCache.includes("STALE")) setCacheStatus("updating");
- else setCacheStatus("updated");
- }
-
- if (!response.ok) {
- // Silently fail - keep showing mock/cached data
- console.warn(
- `PageSpeed API returned ${response.status} for ${strategy}`,
- );
- return false;
- }
-
- const result = (await response.json()) as PageSpeedApiResponse;
-
- // Check if API is disabled (dev mode) or data is invalid
- if (!result?.metrics || (result as any).disabled) {
- // Keep showing mock data in dev mode
- console.warn(
- `PageSpeed API ${(result as any).disabled ? "disabled in dev mode" : "returned invalid data"} for ${strategy}`,
- );
- // Mark as disabled to prevent infinite retries
- if ((result as any).disabled) {
- isDevModeDisabled.current = true;
- // Mark as having "data" (mock) so we don't retry
- hasRealData.current.mobile = true;
- hasRealData.current.desktop = true;
- }
- return false;
- }
-
- // Check if metrics are all zeros (invalid real data)
- const hasValidMetrics = Object.values(result.metrics).some(
- (value) => value > 0,
- );
- if (!hasValidMetrics) {
- console.warn(`PageSpeed returned zero metrics for ${strategy}`);
- return false;
- }
-
- const validatedResult: PageSpeedResult = {
- url: result.url || url,
- strategy: (result.strategy as "mobile" | "desktop") || strategy,
- metrics: result.metrics,
- ...(result.loadingExperience && {
- loadingExperience: result.loadingExperience,
- }),
- };
-
- // Update with real data
- if (strategy === "mobile") {
- setMobileData(validatedResult);
- hasRealData.current.mobile = true;
- } else {
- setDesktopData(validatedResult);
- hasRealData.current.desktop = true;
- }
-
- setLastUpdated(new Date().toISOString());
- setCacheStatus("fresh");
- return true;
- } catch (err) {
- // Silently fail - keep showing mock/cached data
- console.warn(`PageSpeed fetch failed (${strategy}):`, err);
- return false;
- }
- },
- [url],
- );
-
- // Fetch data with silent background retry on failure
- const fetchAllDataWithRetry = useCallback(
- async (forceRefresh = false): Promise => {
- if (!url) return;
-
- // Skip fetching if API is disabled in dev mode (unless force refresh)
- if (isDevModeDisabled.current && !forceRefresh) {
- return;
- }
-
- setCacheStatus("updating");
-
- try {
- const mobileSuccess = await fetchStrategy("mobile", forceRefresh);
- let needsRetry = !mobileSuccess;
-
- if (showBothStrategies) {
- const desktopSuccess = await fetchStrategy("desktop", forceRefresh);
- needsRetry = !mobileSuccess && !desktopSuccess;
- }
-
- // Schedule silent retry if needed (only if we don't have real data yet and API is not disabled)
- if (
- needsRetry &&
- !hasRealData.current.mobile &&
- !hasRealData.current.desktop &&
- !isDevModeDisabled.current
- ) {
- if (retryTimeoutRef.current) {
- clearTimeout(retryTimeoutRef.current);
- }
- retryTimeoutRef.current = setTimeout(() => {
- console.log("Silent retry: Attempting to fetch PageSpeed data...");
- // Use ref to call the latest version of the function
- void fetchFnRef.current?.(false);
- }, 30000);
- }
- } catch (fetchError) {
- console.warn("Failed to fetch PageSpeed data:", fetchError);
- }
- },
- [url, showBothStrategies, fetchStrategy],
- );
-
- // Keep the ref updated with the latest function
- useEffect(() => {
- fetchFnRef.current = fetchAllDataWithRetry;
- }, [fetchAllDataWithRetry]);
-
- const refresh = useCallback(() => {
- void fetchAllDataWithRetry(true);
- }, [fetchAllDataWithRetry]);
-
- useEffect(() => {
- // Defer fetch to next tick to avoid synchronous setState in effect
- const timeoutId = setTimeout(() => {
- void fetchAllDataWithRetry(false);
- }, 0);
-
- // Cleanup retry timeout on unmount
- return () => {
- clearTimeout(timeoutId);
- if (retryTimeoutRef.current) {
- clearTimeout(retryTimeoutRef.current);
- }
- };
- }, [fetchAllDataWithRetry]);
-
- return {
- mobileData,
- desktopData,
- loading,
- error,
- cacheStatus,
- lastUpdated,
- refresh,
- };
-}
diff --git a/messages/de.json b/messages/de.json
index 965278a..94d2143 100644
--- a/messages/de.json
+++ b/messages/de.json
@@ -119,6 +119,9 @@
"title": "Meine Zertifikate",
"issuedBy": "Ausgestellt von:",
"date": "Datum:",
+ "issuers": {
+ "ihkRheinNeckar": "IHK Rhein-Neckar (Industrie- und Handelskammer)"
+ },
"descriptions": {
"pythonPcep": "Die PCEP (Python Certified Entry-Level Programmer) Zertifizierung erhalten, die grundlegende Kenntnisse der Python-Programmierung nachweist.",
"udemyPythonBootcamp": "Den Kurs \"100 Days of Code - The Complete Python Pro Bootcamp\" abgeschlossen und Python vom Anfänger- bis zum Fortgeschrittenenniveau gemeistert.",
@@ -126,7 +129,8 @@
"udemyHtmlCss": "Den Kurs abgeschlossen und die Grundlagen von HTML und CSS gelernt, um eine Website zu erstellen und zu veröffentlichen.",
"gitGithubBootcamp": "Einen umfassenden Git- und GitHub-Kurs abgeschlossen, der Versionskontrolle, Branching und Kollaborations-Workflows abdeckt.",
"fullStackWebDev": "Frontend- und Backend-Webentwicklungskurs, der HTML, CSS, JavaScript, Node.js und React abdeckt.",
- "kiKompetenzSchulung": "KI-Kompetenzschulung gemäß Artikel 4 des EU AI Acts, die verantwortungsvollen KI-Einsatz und Compliance behandelt."
+ "kiKompetenzSchulung": "KI-Kompetenzschulung gemäß Artikel 4 des EU AI Acts, die verantwortungsvollen KI-Einsatz und Compliance behandelt.",
+ "computerScienceExpert": "Fachinformatiker, Fachrichtung: Anwendungsentwicklung, Einsatzgebiet: KI, gemäß § 37 Berufsbildungsgesetz (BBiG)."
}
},
"Projects": {
@@ -222,8 +226,8 @@
"chatbot": {
"title": "KI-Chatbot-Service",
"description": "Diese Website enthält einen KI-Chatbot für interaktive Unterstützung und Informationen.",
- "geminiTitle": "Groq API Integration",
- "geminiDescription": "Der Chatbot wird von Groq betrieben. Durch die Nutzung des Chatbots akzeptieren Sie die Nutzungsbedingungen und Datenschutzrichtlinien von Groq. Chat-Anfragen werden über die Groq API verarbeitet.",
+ "apiTitle": "Groq API Integration",
+ "apiDescription": "Der Chatbot wird von Groq betrieben. Durch die Nutzung des Chatbots akzeptieren Sie die Nutzungsbedingungen und Datenschutzrichtlinien von Groq. Chat-Anfragen werden über die Groq API verarbeitet.",
"temporaryTitle": "Keine Datenspeicherung",
"temporaryDescription": "Ich speichere keine Chat-Gespräche, Verläufe oder persönlichen Informationen. Alle Chat-Daten werden ausschließlich in Ihrem Browser (Web-Storage) gespeichert und verbleiben vollständig unter Ihrer Kontrolle."
},
diff --git a/messages/en.json b/messages/en.json
index fc2459e..796e33a 100644
--- a/messages/en.json
+++ b/messages/en.json
@@ -119,6 +119,9 @@
"title": "My Certifications",
"issuedBy": "Issued by:",
"date": "Date:",
+ "issuers": {
+ "ihkRheinNeckar": "IHK Rhein-Neckar (German Chamber of Commerce and Industry)"
+ },
"descriptions": {
"pythonPcep": "Earned the PCEP (Python Certified Entry-Level Programmer) certification, demonstrating foundational knowledge of Python programming.",
"udemyPythonBootcamp": "Completed the \"100 Days of Code - The Complete Python Pro Bootcamp,\" mastering Python from beginner to advanced levels.",
@@ -126,7 +129,8 @@
"udemyHtmlCss": "Completed the course, learning the fundamentals of HTML and CSS to build and deploy a website.",
"gitGithubBootcamp": "Completed a comprehensive Git and GitHub course, covering version control, branching, and collaboration workflows.",
"fullStackWebDev": "Frontend and Backend Web Development course, covering HTML, CSS, JavaScript, Node.js, and React.",
- "kiKompetenzSchulung": "AI Competence Training according to Article 4 of the EU AI Act, covering responsible AI usage and compliance."
+ "kiKompetenzSchulung": "AI Competence Training according to Article 4 of the EU AI Act, covering responsible AI usage and compliance.",
+ "computerScienceExpert": "Computer Science Expert, Subject Area: Software Development and AI, as per § 37 German Vocational Training Act (BBiG)."
}
},
"Projects": {
@@ -222,8 +226,8 @@
"chatbot": {
"title": "AI Chatbot Service",
"description": "This website includes an AI chatbot for interactive assistance and information.",
- "geminiTitle": "Groq API Integration",
- "geminiDescription": "The chatbot is powered by Groq. By using the chatbot, you accept Groq's terms of service and privacy policy. Chat requests are processed via the Groq API.",
+ "apiTitle": "Groq API Integration",
+ "apiDescription": "The chatbot is powered by Groq. By using the chatbot, you accept Groq's terms of service and privacy policy. Chat requests are processed via the Groq API.",
"temporaryTitle": "No Data Storage",
"temporaryDescription": "I do not save any chat conversations, history, or personal information. All chat data is stored exclusively in your browser (web storage) and remains entirely under your control."
},
diff --git a/messages/es.json b/messages/es.json
index 441c19b..2be00ba 100644
--- a/messages/es.json
+++ b/messages/es.json
@@ -119,6 +119,9 @@
"title": "Mis certificaciones",
"issuedBy": "Emitido por:",
"date": "Fecha:",
+ "issuers": {
+ "ihkRheinNeckar": "IHK Rhein-Neckar (Cámara de Comercio e Industria Alemana)"
+ },
"descriptions": {
"pythonPcep": "Obtuve la certificación PCEP (Python Certified Entry-Level Programmer), demostrando conocimientos fundamentales de programación en Python.",
"udemyPythonBootcamp": "Completé el curso \"100 Days of Code - The Complete Python Pro Bootcamp\", dominando Python desde nivel principiante hasta avanzado.",
@@ -126,7 +129,8 @@
"udemyHtmlCss": "Completé el curso, aprendiendo los fundamentos de HTML y CSS para construir y publicar un sitio web.",
"gitGithubBootcamp": "Completé un curso integral de Git y GitHub, cubriendo control de versiones, ramificación y flujos de trabajo colaborativos.",
"fullStackWebDev": "Curso de desarrollo web Frontend y Backend, cubriendo HTML, CSS, JavaScript, Node.js y React.",
- "kiKompetenzSchulung": "Formación en Competencias de IA según el Artículo 4 de la Ley de IA de la UE, cubriendo uso responsable de IA y cumplimiento."
+ "kiKompetenzSchulung": "Formación en Competencias de IA según el Artículo 4 de la Ley de IA de la UE, cubriendo uso responsable de IA y cumplimiento.",
+ "computerScienceExpert": "Experto en Informática, Área Temática: Desarrollo de Software e IA, según el § 37 de la Ley Alemana de Formación Profesional (BBiG)."
}
},
"Projects": {
@@ -222,8 +226,8 @@
"chatbot": {
"title": "Servicio de Chatbot IA",
"description": "Este sitio web incluye un chatbot de IA para asistencia interactiva e información.",
- "geminiTitle": "Integración de Groq API",
- "geminiDescription": "El chatbot funciona con Groq. Al usar el chatbot, aceptas los términos de servicio y la política de privacidad de Groq. Las solicitudes del chat se procesan a través de la API de Groq.",
+ "apiTitle": "Integración de Groq API",
+ "apiDescription": "El chatbot funciona con Groq. Al usar el chatbot, aceptas los términos de servicio y la política de privacidad de Groq. Las solicitudes del chat se procesan a través de la API de Groq.",
"temporaryTitle": "Sin Almacenamiento de Datos",
"temporaryDescription": "No guardo ninguna conversación, historial o información personal. Todos los datos del chat se almacenan exclusivamente en tu navegador (almacenamiento web) y permanecen completamente bajo tu control."
},
diff --git a/messages/fr.json b/messages/fr.json
index c62c0a0..986d03b 100644
--- a/messages/fr.json
+++ b/messages/fr.json
@@ -119,6 +119,9 @@
"title": "Mes Certifications",
"issuedBy": "Délivré par :",
"date": "Date :",
+ "issuers": {
+ "ihkRheinNeckar": "IHK Rhein-Neckar (Chambre de Commerce et d'Industrie Allemande)"
+ },
"descriptions": {
"pythonPcep": "Obtention de la certification PCEP (Python Certified Entry-Level Programmer), démontrant des connaissances fondamentales en programmation Python.",
"udemyPythonBootcamp": "Complété « 100 Days of Code - The Complete Python Pro Bootcamp », maîtrisant Python du niveau débutant à avancé.",
@@ -126,7 +129,8 @@
"udemyHtmlCss": "Complété le cours, apprenant les bases du HTML et du CSS pour construire et déployer un site web.",
"gitGithubBootcamp": "Complété un cours complet sur Git et GitHub, couvrant le contrôle de version, le branching et les flux de collaboration.",
"fullStackWebDev": "Cours de développement web Frontend et Backend, couvrant HTML, CSS, JavaScript, Node.js et React.",
- "kiKompetenzSchulung": "Formation aux compétences en IA selon l'Article 4 de la loi européenne sur l'IA, couvrant l'utilisation responsable de l'IA et la conformité."
+ "kiKompetenzSchulung": "Formation aux compétences en IA selon l'Article 4 de la loi européenne sur l'IA, couvrant l'utilisation responsable de l'IA et la conformité.",
+ "computerScienceExpert": "Expert en Informatique, Domaine : Développement Logiciel et IA, selon le § 37 de la loi allemande sur la formation professionnelle (BBiG)."
}
},
"Projects": {
@@ -222,8 +226,8 @@
"chatbot": {
"title": "Service de Chatbot IA",
"description": "Ce site web inclut un chatbot IA pour l'assistance interactive et l'information.",
- "geminiTitle": "Intégration de l'API Groq",
- "geminiDescription": "Le chatbot est alimenté par Groq. En utilisant le chatbot, vous acceptez les conditions d'utilisation et la politique de confidentialité de Groq. Les requêtes de chat sont traitées via l'API Groq.",
+ "apiTitle": "Intégration de l'API Groq",
+ "apiDescription": "Le chatbot est alimenté par Groq. En utilisant le chatbot, vous acceptez les conditions d'utilisation et la politique de confidentialité de Groq. Les requêtes de chat sont traitées via l'API Groq.",
"temporaryTitle": "Aucun Stockage de Données",
"temporaryDescription": "Je ne sauvegarde aucune conversation, historique ou information personnelle. Toutes les données de chat sont stockées exclusivement dans votre navigateur (stockage web) et restent entièrement sous votre contrôle."
},
diff --git a/messages/sv.json b/messages/sv.json
index 53ac5e1..1aeb572 100644
--- a/messages/sv.json
+++ b/messages/sv.json
@@ -119,6 +119,9 @@
"title": "Mina certifieringar",
"issuedBy": "Utfärdat av:",
"date": "Datum:",
+ "issuers": {
+ "ihkRheinNeckar": "IHK Rhein-Neckar (Tysk Handels- och Industrikammare)"
+ },
"descriptions": {
"pythonPcep": "Erhållit certifieringen PCEP (Python Certified Entry-Level Programmer), vilket visar grundläggande kunskaper i Python-programmering.",
"udemyPythonBootcamp": "Genomfört \"100 Days of Code - The Complete Python Pro Bootcamp\" och behärskat Python från nybörjar- till avancerad nivå.",
@@ -126,7 +129,8 @@
"udemyHtmlCss": "Genomfört kursen och lärt mig grunderna i HTML och CSS för att bygga och lansera en webbplats.",
"gitGithubBootcamp": "Genomfört en omfattande Git- och GitHub-kurs som täckte versionshantering, branching och samarbetsarbetsflöden.",
"fullStackWebDev": "Kurs i frontend- och backendutveckling som täcker HTML, CSS, JavaScript, Node.js och React.",
- "kiKompetenzSchulung": "AI-kompetensutbildning enligt artikel 4 i EU:s AI-förordning, som täcker ansvarsfull AI-användning och efterlevnad."
+ "kiKompetenzSchulung": "AI-kompetensutbildning enligt artikel 4 i EU:s AI-förordning, som täcker ansvarsfull AI-användning och efterlevnad.",
+ "computerScienceExpert": "Datavetenskapsexpert, Ämnesområde: Mjukvaruutveckling och AI, enligt § 37 av den tyska yrkesutbildningslagen (BBiG)."
}
},
"Projects": {
@@ -222,8 +226,8 @@
"chatbot": {
"title": "AI Chatbot-tjänst",
"description": "Denna webbplats inkluderar en AI-chatbot för interaktiv assistans och information.",
- "geminiTitle": "Groq API Integration",
- "geminiDescription": "Chatboten drivs av Groq. Genom att använda chatboten accepterar du Groqs användarvillkor och integritetspolicy. Chattförfrågningar bearbetas via Groq API.",
+ "apiTitle": "Groq API Integration",
+ "apiDescription": "Chatboten drivs av Groq. Genom att använda chatboten accepterar du Groqs användarvillkor och integritetspolicy. Chattförfrågningar bearbetas via Groq API.",
"temporaryTitle": "Ingen Datalagring",
"temporaryDescription": "Jag sparar inga chattkonversationer, historik eller personlig information. All chattdata lagras exklusivt i din webbläsare (webblagring) och förblir helt under din kontroll."
},
diff --git a/next.config.ts b/next.config.ts
index fd27412..eaaa9cc 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -99,11 +99,13 @@ const nextConfig: NextConfig = {
key: "Content-Security-Policy",
value: [
"default-src 'self'",
+ // TODO: Remove 'unsafe-inline' and 'unsafe-eval' by implementing CSP nonces for Next.js
+ // Current limitation: Required for Next.js runtime and Vercel Analytics
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://vercel.live https://va.vercel-scripts.com",
"style-src 'self' 'unsafe-inline'", // Allow inline styles for Tailwind and components
"img-src 'self' data: blob: https://avatars.githubusercontent.com https://github.com",
"font-src 'self' data:",
- "connect-src 'self' https://api.github.com https://www.googleapis.com https://generativelanguage.googleapis.com https://vercel.live https://vitals.vercel-analytics.com",
+ "connect-src 'self' https://api.github.com https://www.googleapis.com https://generativelanguage.googleapis.com https://vercel.live https://vitals.vercel-analytics.com https://api.groq.com",
"frame-src 'none'",
"object-src 'none'",
"base-uri 'self'",
diff --git a/package-lock.json b/package-lock.json
index 6b2d231..f4c9f8b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "coldbydefault-portfolio",
- "version": "5.3.18",
+ "version": "6.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "coldbydefault-portfolio",
- "version": "5.3.18",
+ "version": "6.0.1",
"hasInstallScript": true,
"license": "Copyright 2026 Yazan Abo-Ayash. All Rights Reserved.",
"dependencies": {
@@ -39,7 +39,7 @@
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.23.12",
"lucide-react": "^0.542.0",
- "next": "^16.0.10",
+ "next": "^16.1.6",
"next-intl": "^4.6.0",
"next-themes": "^0.4.6",
"react": "^19.2.3",
@@ -65,7 +65,7 @@
"@types/react-dom": "^19.2.3",
"@types/three": "^0.179.0",
"eslint": "^9.39.2",
- "eslint-config-next": "^16.0.10",
+ "eslint-config-next": "^16.1.6",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
@@ -122,7 +122,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -402,8 +401,7 @@
"resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz",
"integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==",
"devOptional": true,
- "license": "Apache-2.0",
- "peer": true
+ "license": "Apache-2.0"
},
"node_modules/@electric-sql/pglite-socket": {
"version": "0.0.20",
@@ -1831,9 +1829,9 @@
"license": "MIT"
},
"node_modules/@next/env": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.1.tgz",
- "integrity": "sha512-3oxyM97Sr2PqiVyMyrZUtrtM3jqqFxOQJVuKclDsgj/L728iZt/GyslkN4NwarledZATCenbk4Offjk1hQmaAA==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
+ "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@@ -1847,9 +1845,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz",
- "integrity": "sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
+ "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
"cpu": [
"arm64"
],
@@ -1863,9 +1861,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz",
- "integrity": "sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
+ "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
"cpu": [
"x64"
],
@@ -1879,9 +1877,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz",
- "integrity": "sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
+ "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
"cpu": [
"arm64"
],
@@ -1895,9 +1893,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz",
- "integrity": "sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
+ "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
"cpu": [
"arm64"
],
@@ -1911,9 +1909,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz",
- "integrity": "sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
+ "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
"cpu": [
"x64"
],
@@ -1927,9 +1925,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz",
- "integrity": "sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
+ "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
"cpu": [
"x64"
],
@@ -1943,9 +1941,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz",
- "integrity": "sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
+ "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
"cpu": [
"arm64"
],
@@ -1959,9 +1957,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz",
- "integrity": "sha512-Ncwbw2WJ57Al5OX0k4chM68DKhEPlrXBaSXDCi2kPi5f4d8b3ejr3RRJGfKBLrn2YJL5ezNS7w2TZLHSti8CMw==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
+ "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
"cpu": [
"x64"
],
@@ -2380,9 +2378,9 @@
"license": "Apache-2.0"
},
"node_modules/@prisma/config": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.3.0.tgz",
- "integrity": "sha512-QyMV67+eXF7uMtKxTEeQqNu/Be7iH+3iDZOQZW5ttfbSwBamCSdwPszA0dum+Wx27I7anYTPLmRmMORKViSW1A==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.4.0.tgz",
+ "integrity": "sha512-EnNrZMwZ9+O6UlG+YO9SP3VhVw4zwMahDRzQm3r0DQn9KeU5NwzmaDAY+BzACrgmaU71Id1/0FtWIDdl7xQp9g==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
@@ -2441,70 +2439,70 @@
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.3.0.tgz",
- "integrity": "sha512-cWRQoPDXPtR6stOWuWFZf9pHdQ/o8/QNWn0m0zByxf5Kd946Q875XdEJ52pEsX88vOiXUmjuPG3euw82mwQNMg==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.4.0.tgz",
+ "integrity": "sha512-H+dgpbbY3VN/j5hOSVP1LXsv/rU0w/4C2zh5PZUwo/Q3NqZjOvBlVvkhtziioRmeEZ3SBAqPCsf1sQ74sI3O/w==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
- "@prisma/debug": "7.3.0",
- "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735",
- "@prisma/fetch-engine": "7.3.0",
- "@prisma/get-platform": "7.3.0"
+ "@prisma/debug": "7.4.0",
+ "@prisma/engines-version": "7.4.0-20.ab56fe763f921d033a6c195e7ddeb3e255bdbb57",
+ "@prisma/fetch-engine": "7.4.0",
+ "@prisma/get-platform": "7.4.0"
}
},
"node_modules/@prisma/engines-version": {
- "version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735",
- "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735.tgz",
- "integrity": "sha512-IH2va2ouUHihyiTTRW889LjKAl1CusZOvFfZxCDNpjSENt7g2ndFsK0vdIw/72v7+jCN6YgkHmdAP/BI7SDgyg==",
+ "version": "7.4.0-20.ab56fe763f921d033a6c195e7ddeb3e255bdbb57",
+ "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.4.0-20.ab56fe763f921d033a6c195e7ddeb3e255bdbb57.tgz",
+ "integrity": "sha512-5o3/bubIYdUeg38cyNf+VDq+LVtxvvi2393Fd1Uru52LPfkGJnmVbCaX1wBOAncgKR3BCloMJFD+Koog9LtYqQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines/node_modules/@prisma/debug": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.3.0.tgz",
- "integrity": "sha512-yh/tHhraCzYkffsI1/3a7SHX8tpgbJu1NPnuxS4rEpJdWAUDHUH25F1EDo6PPzirpyLNkgPPZdhojQK804BGtg==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.4.0.tgz",
+ "integrity": "sha512-fZicwzgFHvvPMrRLCUinrsBTdadJsi/1oirzShjmFvNLwtu2DYlkxwRVy5zEGhp85mrEGnLeS/PdNRCdE027+Q==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines/node_modules/@prisma/get-platform": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.3.0.tgz",
- "integrity": "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.0.tgz",
+ "integrity": "sha512-fOUIoGzAPgtjHVs4DsVSnEDPBEauAmFeZr4Ej3tMwxywam7hHdRtCzgKagQBKcYIJuya8gzYrTqUoukzXtWJaA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
- "@prisma/debug": "7.3.0"
+ "@prisma/debug": "7.4.0"
}
},
"node_modules/@prisma/fetch-engine": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.3.0.tgz",
- "integrity": "sha512-Mm0F84JMqM9Vxk70pzfNpGJ1lE4hYjOeLMu7nOOD1i83nvp8MSAcFYBnHqLvEZiA6onUR+m8iYogtOY4oPO5lQ==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.4.0.tgz",
+ "integrity": "sha512-IXPOYskT89UTVsntuSnMTiKRWCuTg5JMWflgEDV1OSKFpuhwP5vqbfF01/iwo9y6rCjR0sDIO+jdV5kq38/hgA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
- "@prisma/debug": "7.3.0",
- "@prisma/engines-version": "7.3.0-16.9d6ad21cbbceab97458517b147a6a09ff43aa735",
- "@prisma/get-platform": "7.3.0"
+ "@prisma/debug": "7.4.0",
+ "@prisma/engines-version": "7.4.0-20.ab56fe763f921d033a6c195e7ddeb3e255bdbb57",
+ "@prisma/get-platform": "7.4.0"
}
},
"node_modules/@prisma/fetch-engine/node_modules/@prisma/debug": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.3.0.tgz",
- "integrity": "sha512-yh/tHhraCzYkffsI1/3a7SHX8tpgbJu1NPnuxS4rEpJdWAUDHUH25F1EDo6PPzirpyLNkgPPZdhojQK804BGtg==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.4.0.tgz",
+ "integrity": "sha512-fZicwzgFHvvPMrRLCUinrsBTdadJsi/1oirzShjmFvNLwtu2DYlkxwRVy5zEGhp85mrEGnLeS/PdNRCdE027+Q==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.3.0.tgz",
- "integrity": "sha512-N7c6m4/I0Q6JYmWKP2RCD/sM9eWiyCPY98g5c0uEktObNSZnugW2U/PO+pwL0UaqzxqTXt7gTsYsb0FnMnJNbg==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.4.0.tgz",
+ "integrity": "sha512-fOUIoGzAPgtjHVs4DsVSnEDPBEauAmFeZr4Ej3tMwxywam7hHdRtCzgKagQBKcYIJuya8gzYrTqUoukzXtWJaA==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
- "@prisma/debug": "7.3.0"
+ "@prisma/debug": "7.4.0"
}
},
"node_modules/@prisma/get-platform": {
@@ -3711,7 +3709,6 @@
"resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-9.4.2.tgz",
"integrity": "sha512-H4B4+FDNHpvIb4FmphH4ubxOfX5bxmfOw0+3pkQwR9u9wFiyMS7wUDkNn0m4RqQuiLWeia9jfN1eBvtyAVGEog==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.17.8",
"@types/react-reconciler": "^0.32.0",
@@ -4387,7 +4384,6 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -4398,7 +4394,6 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"peerDependencies": {
"@types/react": "^19.2.0"
}
@@ -4423,7 +4418,6 @@
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.179.0.tgz",
"integrity": "sha512-VgbFG2Pgsm84BqdegZzr7w2aKbQxmgzIu4Dy7/75ygiD/0P68LKmp5ie08KMPNqGTQwIge8s6D1guZf1RnZE0A==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@dimforge/rapier3d-compat": "~0.12.0",
"@tweenjs/tween.js": "~23.1.3",
@@ -4491,7 +4485,6 @@
"integrity": "sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.50.1",
"@typescript-eslint/types": "8.50.1",
@@ -5093,7 +5086,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5494,7 +5486,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5841,9 +5832,9 @@
"license": "MIT"
},
"node_modules/confbox": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
- "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
+ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
"devOptional": true,
"license": "MIT"
},
@@ -6190,8 +6181,7 @@
"version": "8.6.0",
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/embla-carousel-autoplay": {
"version": "8.6.0",
@@ -6514,7 +6504,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -6570,13 +6559,13 @@
}
},
"node_modules/eslint-config-next": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.1.tgz",
- "integrity": "sha512-55nTpVWm3qeuxoQKLOjQVciKZJUphKrNM0fCcQHAIOGl6VFXgaqeMfv0aKJhs7QtcnlAPhNVqsqRfRjeKBPIUA==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.6.tgz",
+ "integrity": "sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "16.1.1",
+ "@next/eslint-plugin-next": "16.1.6",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
@@ -6597,9 +6586,9 @@
}
},
"node_modules/eslint-config-next/node_modules/@next/eslint-plugin-next": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.1.tgz",
- "integrity": "sha512-Ovb/6TuLKbE1UiPcg0p39Ke3puyTCIKN9hGbNItmpQsp+WX3qrjO3WaMVSi6JHr9X1NrmthqIguVHodMJbh/dw==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.1.6.tgz",
+ "integrity": "sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6743,7 +6732,6 @@
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@rtsao/scc": "^1.1.0",
"array-includes": "^3.1.9",
@@ -7772,7 +7760,6 @@
"integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -9001,9 +8988,9 @@
}
},
"node_modules/markdown-it": {
- "version": "14.1.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
- "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10084,13 +10071,12 @@
}
},
"node_modules/next": {
- "version": "16.1.1",
- "resolved": "https://registry.npmjs.org/next/-/next-16.1.1.tgz",
- "integrity": "sha512-QI+T7xrxt1pF6SQ/JYFz95ro/mg/1Znk5vBebsWwbpejj1T0A23hO7GYEaVac9QUOT2BIMiuzm0L99ooq7k0/w==",
+ "version": "16.1.6",
+ "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
+ "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
"license": "MIT",
- "peer": true,
"dependencies": {
- "@next/env": "16.1.1",
+ "@next/env": "16.1.6",
"@swc/helpers": "0.5.15",
"baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001579",
@@ -10104,14 +10090,14 @@
"node": ">=20.9.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "16.1.1",
- "@next/swc-darwin-x64": "16.1.1",
- "@next/swc-linux-arm64-gnu": "16.1.1",
- "@next/swc-linux-arm64-musl": "16.1.1",
- "@next/swc-linux-x64-gnu": "16.1.1",
- "@next/swc-linux-x64-musl": "16.1.1",
- "@next/swc-win32-arm64-msvc": "16.1.1",
- "@next/swc-win32-x64-msvc": "16.1.1",
+ "@next/swc-darwin-arm64": "16.1.6",
+ "@next/swc-darwin-x64": "16.1.6",
+ "@next/swc-linux-arm64-gnu": "16.1.6",
+ "@next/swc-linux-arm64-musl": "16.1.6",
+ "@next/swc-linux-x64-gnu": "16.1.6",
+ "@next/swc-linux-x64-musl": "16.1.6",
+ "@next/swc-win32-arm64-msvc": "16.1.6",
+ "@next/swc-win32-x64-msvc": "16.1.6",
"sharp": "^0.34.4"
},
"peerDependencies": {
@@ -10271,9 +10257,9 @@
"license": "MIT"
},
"node_modules/nypm": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.4.tgz",
- "integrity": "sha512-1TvCKjZyyklN+JJj2TS3P4uSQEInrM/HkkuSXsEzm1ApPgBffOn8gFguNnZf07r/1X6vlryfIqMUkJKQMzlZiw==",
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz",
+ "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==",
"devOptional": true,
"license": "MIT",
"dependencies": {
@@ -10289,9 +10275,9 @@
}
},
"node_modules/nypm/node_modules/citty": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.0.tgz",
- "integrity": "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA==",
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz",
+ "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==",
"devOptional": true,
"license": "MIT"
},
@@ -10588,7 +10574,6 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
"integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"pg-connection-string": "^2.9.1",
"pg-pool": "^3.10.1",
@@ -10817,17 +10802,16 @@
}
},
"node_modules/prisma": {
- "version": "7.3.0",
- "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.3.0.tgz",
- "integrity": "sha512-ApYSOLHfMN8WftJA+vL6XwAPOh/aZ0BgUyyKPwUFgjARmG6EBI9LzDPf6SWULQMSAxydV9qn5gLj037nPNlg2w==",
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.4.0.tgz",
+ "integrity": "sha512-n2xU9vSaH4uxZF/l2aKoGYtKtC7BL936jM9Q94Syk1zOD39t/5hjDUxMgaPkVRDX5wWEMsIqvzQxoebNIesOKw==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
- "peer": true,
"dependencies": {
- "@prisma/config": "7.3.0",
+ "@prisma/config": "7.4.0",
"@prisma/dev": "0.20.0",
- "@prisma/engines": "7.3.0",
+ "@prisma/engines": "7.4.0",
"@prisma/studio-core": "0.13.1",
"mysql2": "3.15.3",
"postgres": "3.4.7"
@@ -10976,7 +10960,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -10986,7 +10969,6 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"scheduler": "^0.27.0"
},
@@ -11005,7 +10987,6 @@
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.69.0.tgz",
"integrity": "sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==",
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -12180,7 +12161,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=12"
},
@@ -12496,7 +12476,6 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"devOptional": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -13079,7 +13058,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
"license": "MIT",
- "peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
diff --git a/package.json b/package.json
index 4ab9597..d7997e7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "coldbydefault-portfolio",
- "version": "5.3.18",
+ "version": "6.0.1",
"description": "Professional portfolio of Yazan Abo-Ayash (ColdByDefault™) - Full Stack Developer specializing in AI and automation, Next.js and modern web technologies.",
"keywords": [
"portfolio",
@@ -25,7 +25,7 @@
"vercel",
"modern-web-technologies"
],
- "author": "Yazan Abo-Ayash (ColdByDefault™) ",
+ "author": "Yazan Abo-Ayash (ColdByDefault™)",
"license": "Copyright 2026 Yazan Abo-Ayash. All Rights Reserved.",
"private": true,
"scripts": {
@@ -76,7 +76,7 @@
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.23.12",
"lucide-react": "^0.542.0",
- "next": "^16.0.10",
+ "next": "^16.1.6",
"next-intl": "^4.6.0",
"next-themes": "^0.4.6",
"react": "^19.2.3",
@@ -102,7 +102,7 @@
"@types/react-dom": "^19.2.3",
"@types/three": "^0.179.0",
"eslint": "^9.39.2",
- "eslint-config-next": "^16.0.10",
+ "eslint-config-next": "^16.1.6",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
diff --git a/temp-not-in-use/aboutPorto/PortoCardComponents.tsx.backup b/temp-not-in-use/aboutPorto/PortoCardComponents.tsx.backup
deleted file mode 100644
index e0a656a..0000000
--- a/temp-not-in-use/aboutPorto/PortoCardComponents.tsx.backup
+++ /dev/null
@@ -1,133 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-import React from "react";
-import { useTranslations } from "next-intl";
-import { Badge } from "@/components/ui/badge";
-import type {
- FeatureItemProps,
- DeviceType,
- PortoCardFeature,
-} from "@/types/aboutPorto";
-
-export const FeatureItem = React.memo(function FeatureItem({
- icon,
- title,
- description: _description,
- badges: _badges,
- compact = false,
-}: FeatureItemProps) {
- if (compact) {
- return (
-
-
- {icon}
-
-
-
- {title}
-
-
-
- );
- }
-
- return (
-
-
- {icon}
-
-
-
- {title}
-
-
-
- );
-});
-
-interface FeatureGridProps {
- features: PortoCardFeature[];
- deviceType: DeviceType;
- gridClasses: string;
-}
-
-export const FeatureGrid = React.memo(function FeatureGrid({
- features,
- deviceType,
- gridClasses,
-}: FeatureGridProps) {
- const t = useTranslations("PortfolioAbout.features");
- const isCompact = deviceType === "mobile";
-
- return (
-
- {features.map((feature, index) => (
-
- ))}
-
- );
-});
-
-interface TechHighlightsProps {
- techs: string[];
- deviceType: DeviceType;
-}
-
-export const TechHighlights = React.memo(function TechHighlights({
- techs,
- deviceType,
-}: TechHighlightsProps) {
- const t = useTranslations("PortfolioAbout");
-
- if (deviceType === "mobile") {
- return null; // Don't show on mobile to save space
- }
-
- return (
-
-
-
- {t("techHighlights")}
-
-
- {techs.map((tech, index) => (
-
- {tech}
-
- ))}
-
-
-
- );
-});
diff --git a/temp-not-in-use/aboutPorto/index.ts.backup b/temp-not-in-use/aboutPorto/index.ts.backup
deleted file mode 100644
index 348f398..0000000
--- a/temp-not-in-use/aboutPorto/index.ts.backup
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-export { default as PortoCard } from "./portoCard";
-export { default } from "./portoCard";
-export * from "./portoCard.utils";
-export * from "./PortoCardComponents";
diff --git a/temp-not-in-use/aboutPorto/portoCard.tsx.backup b/temp-not-in-use/aboutPorto/portoCard.tsx.backup
deleted file mode 100644
index b86194c..0000000
--- a/temp-not-in-use/aboutPorto/portoCard.tsx.backup
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-
-/** This component has types and data in portoCard.utils.ts
- * NOT as other components in @/types @/data
-*/
-
-"use client";
-
-import React from "react";
-import { useTranslations } from "next-intl";
-import Link from "next/link";
-import {
- Card,
- CardContent,
- CardDescription,
- CardFooter,
- CardHeader,
- CardTitle,
-} from "@/components/ui/card";
-import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
-import { Code2, ExternalLink, CheckCircle } from "lucide-react";
-import type { PortoCardProps } from "@/types/aboutPorto";
-import {
- useResponsiveConfig,
- getFeatureGridClasses,
- getTechStackHighlights,
- PortoCardUtils,
-} from "./portoCard.utils";
-import { FeatureGrid, TechHighlights } from "@/components/aboutPorto";
-import VersionDisplay from "@/components/VersionDisplay";
-
-export default React.memo(function PortoCard({ className }: PortoCardProps) {
- const t = useTranslations("PortfolioAbout");
- const { deviceType, containerClasses, cardClasses, featuresConfig } =
- useResponsiveConfig();
-
- // Memoize responsive configurations to prevent recalculation
- const gridClasses = React.useMemo(
- () => getFeatureGridClasses(deviceType),
- [deviceType]
- );
- const techHighlights = React.useMemo(
- () => getTechStackHighlights(deviceType),
- [deviceType]
- );
- const headerLayout = React.useMemo(
- () => PortoCardUtils.getHeaderLayout(deviceType),
- [deviceType]
- );
- const shouldShowTechHighlights = React.useMemo(
- () => PortoCardUtils.shouldShowSection("techHighlights", deviceType),
- [deviceType]
- );
- const descriptionLength = React.useMemo(
- () => PortoCardUtils.getDescriptionLength(deviceType),
- [deviceType]
- );
-
- // Memoize description processing to avoid repeated string operations
- const description = React.useMemo(() => {
- const fullDescription = t("description");
- if (descriptionLength === "short") {
- // Truncate description for mobile
- const sentences = fullDescription.split(". ");
- return sentences.slice(0, 2).join(". ") + ".";
- }
- return fullDescription;
- }, [t, descriptionLength]);
-
- return (
-
-
-
-
-
-
- {t("title")}
-
-
-
- {description}
-
-
- {/* Performance Badge */}
-
-
-
- {t("performanceBadge")}
-
-
-
-
-
-
-
- {t("featuresTitle")}
-
-
-
-
- {/* Tech Stack Highlights - Hidden on mobile */}
- {shouldShowTechHighlights && (
-
- )}
-
- {/* Read More Section - Compact on mobile */}
-
-
- {t("actionsTitle")}
-
-
-
-
-
-
-
-
-
- Portfolio{" "}
-
-
-
-
-
-
- );
-});
diff --git a/temp-not-in-use/aboutPorto/portoCard.utils.ts.backup b/temp-not-in-use/aboutPorto/portoCard.utils.ts.backup
deleted file mode 100644
index 47bc22d..0000000
--- a/temp-not-in-use/aboutPorto/portoCard.utils.ts.backup
+++ /dev/null
@@ -1,257 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-import React from "react";
-import {
- Atom,
- Database,
- Zap,
- Layout,
- GitBranch,
- Languages,
- FolderTree,
- Bot,
-} from "lucide-react";
-import { debounce } from "perfect-debounce";
-import type {
- DeviceType,
- PortoCardFeature,
- ResponsiveConfig,
-} from "@/types/aboutPorto";
-
-export function useResponsiveConfig(): ResponsiveConfig {
- const [deviceType, setDeviceType] = React.useState("desktop");
-
- React.useEffect(() => {
- const checkDeviceType = (): void => {
- const width = window.innerWidth;
- if (width < 768) {
- setDeviceType("mobile");
- } else if (width < 1024) {
- setDeviceType("tablet");
- } else {
- setDeviceType("desktop");
- }
- };
-
- // Debounce resize handler to improve performance
- const debouncedHandler = debounce(checkDeviceType, 150) as () => void;
-
- // Set initial device type
- checkDeviceType();
-
- window.addEventListener("resize", debouncedHandler);
- return () => {
- window.removeEventListener("resize", debouncedHandler);
- };
- }, []);
-
- // Memoize computed values to prevent unnecessary recalculations
- const containerClasses = React.useMemo(
- () => getContainerClasses(deviceType),
- [deviceType]
- );
- const cardClasses = React.useMemo(
- () => getCardClasses(deviceType),
- [deviceType]
- );
- const featuresConfig = React.useMemo(
- () => getFeaturesConfig(deviceType),
- [deviceType]
- );
-
- return React.useMemo(
- () => ({
- deviceType,
- containerClasses,
- cardClasses,
- featuresConfig,
- }),
- [deviceType, containerClasses, cardClasses, featuresConfig]
- );
-}
-
-/**
- * Gets container CSS classes based on device type
- */
-function getContainerClasses(deviceType: DeviceType): string {
- switch (deviceType) {
- case "mobile":
- return "container mx-auto px-4 py-6";
- case "tablet":
- return "container mx-auto px-6 py-8";
- case "desktop":
- return "container mx-auto px-4 py-8";
- default:
- return "container mx-auto px-4 py-8";
- }
-}
-
-/**
- * Gets card CSS classes based on device type
- */
-function getCardClasses(deviceType: DeviceType): string {
- switch (deviceType) {
- case "mobile":
- return "w-full mx-auto";
- case "tablet":
- return "w-full max-w-5xl mx-auto";
- case "desktop":
- return "w-full max-w-6xl mx-auto";
- default:
- return "w-full max-w-6xl mx-auto";
- }
-}
-
-/**
- * Gets features configuration based on device type
- */
-function getFeaturesConfig(deviceType: DeviceType): {
- features: PortoCardFeature[];
- showAll: boolean;
-} {
- const allFeatures: PortoCardFeature[] = [
- {
- key: "techStack",
- icon: React.createElement(Atom, { className: "h-4 w-4" }),
- badges: ["Next.js 15.5.1", "React 19", "TypeScript", "App Router"],
- priority: 1,
- },
- {
- key: "performance",
- icon: React.createElement(Zap, { className: "h-4 w-4" }),
- badges: ["95+ Lighthouse", "SEO 100/100", "A11y Optimized"],
- priority: 1,
- },
- {
- key: "cleanArchitecture",
- icon: React.createElement(FolderTree, { className: "h-4 w-4" }),
- badges: ["/lib", "/data", "/hooks", "/types"],
- priority: 2,
- },
- {
- key: "database",
- icon: React.createElement(Database, { className: "h-4 w-4" }),
- badges: ["PostgreSQL", "Prisma ORM", "Neon DB"],
- priority: 2,
- },
- {
- key: "aiChatbot",
- icon: React.createElement(Bot, { className: "h-4 w-4" }),
- badges: ["AI Assistant", "Portfolio Guide", "Interactive Help"],
- priority: 1,
- },
- {
- key: "mainFeatures",
- icon: React.createElement(Layout, { className: "h-4 w-4" }),
- badges: ["Blog System", "Media Gallery", "Content Library"],
- priority: 3,
- },
- {
- key: "techFeatures",
- icon: React.createElement(GitBranch, { className: "h-4 w-4" }),
- badges: ["MCP GitHub", "Live PageSpeed", "CI/CD Automation"],
- priority: 3,
- },
- {
- key: "localization",
- icon: React.createElement(Languages, { className: "h-4 w-4" }),
- badges: ["5 Languages", "Light/Dark Themes", "Auto-detection"],
- priority: 3,
- },
- ];
-
- switch (deviceType) {
- case "mobile":
- // Show only top priority features on mobile
- return {
- features: allFeatures.filter((f) => f.priority === 1),
- showAll: false,
- };
- case "tablet":
- // Show top 2 priority levels on tablet
- return {
- features: allFeatures.filter((f) => f.priority <= 2),
- showAll: false,
- };
- case "desktop":
- // Show all features on desktop
- return {
- features: allFeatures,
- showAll: true,
- };
- default:
- return {
- features: allFeatures,
- showAll: true,
- };
- }
-}
-
-/**
- * Gets grid configuration for features display
- */
-export function getFeatureGridClasses(deviceType: DeviceType): string {
- switch (deviceType) {
- case "mobile":
- return "grid grid-cols-1 gap-3";
- case "tablet":
- return "grid grid-cols-2 gap-4";
- case "desktop":
- return "grid grid-cols-3 gap-4";
- default:
- return "grid grid-cols-3 gap-4";
- }
-}
-
-/**
- * Gets the tech stack highlights for display
- */
-export function getTechStackHighlights(deviceType: DeviceType): string[] {
- const allTechs = [
- "Tailwind CSS",
- "shadcnUI",
- "Framer Motion",
- "Embla Carousel",
- "next-intl",
- "Vercel Edge",
- "Zod Validation",
- "ESLint 9.x",
- ];
-
- switch (deviceType) {
- case "mobile":
- return allTechs.slice(0, 4); // Show only 4 on mobile
- case "tablet":
- return allTechs.slice(0, 6); // Show 6 on tablet
- case "desktop":
- return allTechs; // Show all on desktop
- default:
- return allTechs;
- }
-}
-
-/**
- * Component-specific utilities
- */
-export const PortoCardUtils = {
- getHeaderLayout(deviceType: DeviceType): "vertical" | "horizontal" {
- return deviceType === "mobile" ? "vertical" : "horizontal";
- },
-
- shouldShowSection(
- section: "techHighlights" | "readMore",
- deviceType: DeviceType
- ): boolean {
- if (section === "techHighlights") {
- return deviceType !== "mobile"; // Hide tech highlights on mobile to reduce height
- }
- return true;
- },
-
- getDescriptionLength(deviceType: DeviceType): "short" | "full" {
- return deviceType === "mobile" ? "short" : "full";
- },
-};
diff --git a/temp-not-in-use/assets/cer2.png b/temp-not-in-use/assets/cer2.png
deleted file mode 100644
index 713abfd..0000000
Binary files a/temp-not-in-use/assets/cer2.png and /dev/null differ
diff --git a/temp-not-in-use/assets/githubC.png b/temp-not-in-use/assets/githubC.png
deleted file mode 100644
index 4b29c8d..0000000
Binary files a/temp-not-in-use/assets/githubC.png and /dev/null differ
diff --git a/temp-not-in-use/assets/htmlC.png b/temp-not-in-use/assets/htmlC.png
deleted file mode 100644
index 964ac15..0000000
Binary files a/temp-not-in-use/assets/htmlC.png and /dev/null differ
diff --git a/temp-not-in-use/assets/nodecer.jpg b/temp-not-in-use/assets/nodecer.jpg
deleted file mode 100644
index 390c06b..0000000
Binary files a/temp-not-in-use/assets/nodecer.jpg and /dev/null differ
diff --git a/temp-not-in-use/tech/Technologies.logic.ts.backup b/temp-not-in-use/tech/Technologies.logic.ts.backup
deleted file mode 100644
index 77899c5..0000000
--- a/temp-not-in-use/tech/Technologies.logic.ts.backup
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- *
- * BACKUP - Original Technologies.logic.ts file moved here for reference
-*/
-
-import { techGroups } from "@/data/tech";
-import type { KeyboardEvent } from "react";
-
-/**
- * Configuration object for carousel dimensions and layout
- */
-interface CarouselConfig {
- /** Height of the carousel container in pixels */
- readonly height: number;
- /** CSS grid classes for responsive layout */
- readonly gridClasses: string;
-}
-
-/**
- * Handler functions for card interactions
- */
-interface CardInteractionHandlers {
- /** Handle mouse enter event */
- readonly onMouseEnter: () => void;
- /** Handle mouse leave event */
- readonly onMouseLeave: () => void;
- /** Handle keyboard navigation */
- readonly onKeyDown: (e: KeyboardEvent) => void;
-}
-
-export function calculateCarouselConfig(
- cardsPerSlide: number,
- maxItems: number
-): CarouselConfig {
- // Responsive carousel height - smaller on mobile devices
- const baseHeight =
- cardsPerSlide === 1 ? 240 : cardsPerSlide === 2 ? 260 : 280;
- const itemHeight = cardsPerSlide === 1 ? 20 : 25; // Even tighter on mobile
- const height = Math.max(baseHeight, baseHeight + (maxItems - 4) * itemHeight);
-
- // Grid classes based on cards per slide
- const gridClasses =
- cardsPerSlide === 1
- ? "grid-cols-1"
- : cardsPerSlide === 2
- ? "grid-cols-2"
- : "grid-cols-3";
-
- return { height, gridClasses };
-}
-
-
-export function generateSlides(cardsPerSlide: number): (typeof techGroups)[] {
- const slides: (typeof techGroups)[] = [];
- for (let i = 0; i < techGroups.length; i += cardsPerSlide) {
- slides.push(techGroups.slice(i, i + cardsPerSlide));
- }
- return slides;
-}
-
-
-export function getMaxItemsCount(): number {
- return Math.max(...techGroups.map((group) => group.items.length));
-}
-
-export function createCardInteractionHandlers(
- categoryId: string,
- currentHoveredCard: string | null,
- setHoveredCard: (card: string | null) => void
-): CardInteractionHandlers {
- const isCurrentCardHovered = currentHoveredCard === categoryId;
-
- return {
- onMouseEnter: () => setHoveredCard(categoryId),
- onMouseLeave: () => setHoveredCard(null),
- onKeyDown: (e: KeyboardEvent) => {
- if (e.key === "Enter" || e.key === " ") {
- e.preventDefault();
- setHoveredCard(isCurrentCardHovered ? null : categoryId);
- }
- },
- };
-}
diff --git a/temp-not-in-use/tech/Technologies.tsx.backup b/temp-not-in-use/tech/Technologies.tsx.backup
deleted file mode 100644
index 780aff9..0000000
--- a/temp-not-in-use/tech/Technologies.tsx.backup
+++ /dev/null
@@ -1,213 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- *
- * BACKUP 1 - Carousel-based layout version
- * - Uses techGroups data structure
- * - Carousel with slides and navigation buttons
- * - useResponsiveCarousel hook for responsive card counts
- * - Complex hover state with animated overlays
- * - Technologies.logic helper functions
- * - More elaborate accessibility features
-*/
-
-"use client";
-
-import { motion } from "framer-motion";
-import type { techGroups } from "@/data/tech";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { useState } from "react";
-import { useTranslations } from "next-intl";
-import {
- getCardHoverClasses,
- getOverlayStyles,
-} from "@/components/visuals/card-animations";
-import {
- Carousel,
- CarouselContent,
- CarouselItem,
- CarouselNext,
- CarouselPrevious,
- type CarouselApi,
-} from "@/components/ui/carousel";
-import { useResponsiveCarousel } from "@/hooks/use-responsive-carousel";
-import {
- calculateCarouselConfig,
- generateSlides,
- getMaxItemsCount,
- createCardInteractionHandlers,
-} from "./Technologies.logic";
-
-export default function Technologies() {
- const [hoveredCard, setHoveredCard] = useState(null);
- const [currentSlide, setCurrentSlide] = useState(0);
- const t = useTranslations("Technologies");
- const tCategories = useTranslations("Technologies.categories");
- const { cardsPerSlide } = useResponsiveCarousel();
-
- // Calculate carousel configuration using logic functions
- const maxItems = getMaxItemsCount();
- const carouselConfig = calculateCarouselConfig(cardsPerSlide, maxItems);
- const slides = generateSlides(cardsPerSlide);
-
- const renderTechCard = (group: (typeof techGroups)[0]) => {
- const isCurrentCardHovered = hoveredCard === group.category;
- const cardHandlers = createCardInteractionHandlers(
- group.category,
- hoveredCard,
- setHoveredCard
- );
-
- return (
-
-
-
- {tCategories(group.categoryKey)}
-
-
-
-
- {group.items.map(({ name, Icon }) => (
-
-
-
- {name}
-
-
- ))}
-
-
-
-
- );
- };
-
- return (
-
-
-
-
- {t("title")}
-
-
-
- {/* Hidden instructions for screen readers */}
-
- Use arrow keys or navigation buttons to browse through technology
- categories. Press Enter or Space on cards to view details.
-
- {/* Live region for slide announcements */}
-
- Slide {currentSlide + 1} of {slides.length}
-
-
-
{
- if (api) {
- api.on("select", () => {
- setCurrentSlide(api.selectedScrollSnap());
- });
- }
- }}
- >
-
- {slides.map((slide, slideIndex) => (
-
-
- {slide.map((group) => renderTechCard(group))}
-
-
- ))}
-
-
- {/* Custom Navigation Buttons - Enhanced for better theming */}
-
-
-
-
-
-
-
-
- {t("manyMoreTechnologies")}
-
-
-
- );
-}
diff --git a/temp-not-in-use/tech/Technologies.tsx.backup2 b/temp-not-in-use/tech/Technologies.tsx.backup2
deleted file mode 100644
index a6badb3..0000000
--- a/temp-not-in-use/tech/Technologies.tsx.backup2
+++ /dev/null
@@ -1,120 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- *
- * BACKUP 2 - Simple vertical scroll layout version
- * - Uses serviceGroups data structure (with subcategories)
- * - No carousel - stacked vertical sections
- * - Simpler animation (fade + slide up)
- * - Nested subcategory cards
- * - Cleaner, more minimal code (~114 lines vs ~207)
-*/
-
-"use client";
-
-import { motion } from "framer-motion";
-import { serviceGroups } from "@/data/tech";
-import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
-import { useTranslations } from "next-intl";
-
-export default function Technologies() {
- const t = useTranslations("Technologies");
- const tCategories = useTranslations("Technologies.categories");
- const tDescriptions = useTranslations("Technologies.descriptions");
- const tSubCategories = useTranslations("Technologies.subCategories");
-
- return (
-
-
-
-
- {t("title")}
-
-
-
- {serviceGroups.map((service, index) => (
-
- {/* Service Header */}
-
-
- {tCategories(service.categoryKey)}
-
-
- {tDescriptions(service.descriptionKey)}
-
-
-
- {/* Subcategories Grid */}
-
- {service.subCategories.map((subCategory) => (
-
-
-
- {tSubCategories(subCategory.nameKey)}
-
-
-
-
- {subCategory.items.map(({ name, Icon }) => (
-
-
-
- {name}
-
-
- ))}
-
-
-
- ))}
-
-
- {/* Divider between services (except last) */}
- {index < serviceGroups.length - 1 && (
-
- )}
-
- ))}
-
-
-
-
- {t("manyMoreTechnologies")}
-
-
-
- );
-}
diff --git a/temp-not-in-use/tech/index.ts.backup b/temp-not-in-use/tech/index.ts.backup
deleted file mode 100644
index 20f89e7..0000000
--- a/temp-not-in-use/tech/index.ts.backup
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- *
- * BACKUP - Original index.ts file moved here for reference
-*/
-
-export { default as Technologies } from "./Technologies";
diff --git a/temp-not-in-use/tech/tech.ts.backup b/temp-not-in-use/tech/tech.ts.backup
deleted file mode 100644
index 7987946..0000000
--- a/temp-not-in-use/tech/tech.ts.backup
+++ /dev/null
@@ -1,244 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
- *
- * BACKUP - Original tech.ts file moved here for reference
-*/
-
-import {
- SiReact,
- SiNextdotjs,
- SiVite,
- SiTailwindcss,
- SiJavascript,
- SiTypescript,
- SiN8N,
- SiNodedotjs,
- SiPython,
- SiPrisma,
- SiPostgresql,
- SiSupabase,
- SiOllama,
- SiDocker,
- SiGooglecloud,
- SiFirebase,
- SiVercel,
- SiNetlify,
- SiGithubactions,
- SiLangchain,
- SiGit,
- SiGithub,
- SiBitbucket,
- SiJira,
- SiNotion,
- SiMiro,
- SiWebstorm,
- SiPycharm,
- SiMarkdown,
- SiLatex,
- SiGoogleanalytics,
-} from "react-icons/si";
-import {
- FaBrain,
- FaRobot,
- FaFigma,
- FaUsers,
- FaLightbulb,
- FaSearch,
- FaVideo,
- FaImage,
- FaBook,
- FaDatabase,
- FaCode,
- FaNetworkWired,
- FaCogs,
- FaLayerGroup,
- FaPuzzlePiece,
- FaShieldAlt,
- FaLock,
- FaKey,
- FaUserShield,
-} from "react-icons/fa";
-import { RiOpenaiFill, RiFlowChart } from "react-icons/ri";
-import { BiLogoVisualStudio } from "react-icons/bi";
-import { DiScrum } from "react-icons/di";
-import { FiServer } from "react-icons/fi";
-import { TbAutomation, TbVectorTriangle, TbBinaryTree } from "react-icons/tb";
-import {
- Box,
- Database,
- MessageCircle,
- Clock,
- FileText,
- Network,
- Monitor,
- MonitorSpeaker,
- Cpu,
- Workflow,
-} from "lucide-react";
-
-export interface TechItem {
- name: string;
- Icon: React.ComponentType<{ size?: number; className?: string }>;
-}
-
-export interface TechGroup {
- category: string;
- categoryKey: string;
- items: TechItem[];
-}
-
-export const techGroups: TechGroup[] = [
- {
- category: "UI & Frontend",
- categoryKey: "uiFrontend",
- items: [
- { name: "React", Icon: SiReact },
- { name: "Next.js", Icon: SiNextdotjs },
- { name: "Vite", Icon: SiVite },
- { name: "TailwindCSS", Icon: SiTailwindcss },
- { name: "JavaScript", Icon: SiJavascript },
- { name: "TypeScript", Icon: SiTypescript },
- { name: "shadcnUI", Icon: Box },
- ],
- },
- {
- category: "Backend & APIs",
- categoryKey: "backendApis",
- items: [
- { name: "Node.js", Icon: SiNodedotjs },
- { name: "Python", Icon: SiPython },
- { name: "Prisma", Icon: SiPrisma },
- { name: "REST APIs", Icon: FiServer },
- { name: "LangChain", Icon: SiLangchain },
- ],
- },
- {
- category: "Core Programming Concepts",
- categoryKey: "coreProgrammingConcepts",
- items: [
- { name: "Object-Oriented Programming", Icon: FaLayerGroup },
- { name: "Data Structures", Icon: TbBinaryTree },
- { name: "Algorithms", Icon: Cpu },
- { name: "Design Patterns", Icon: FaPuzzlePiece },
- ],
- },
- {
- category: "Architecture & System Design",
- categoryKey: "architectureSystemDesign",
- items: [
- { name: "Software Architecture Patterns", Icon: Workflow },
- { name: "Basic Networking", Icon: FaNetworkWired },
- { name: "System Design", Icon: FaCogs },
- { name: "Code Quality & Clean Code", Icon: FaCode },
- ],
- },
- {
- category: "Databases & Storage",
- categoryKey: "databasesStorage",
- items: [
- { name: "PostgreSQL", Icon: SiPostgresql },
- { name: "Supabase", Icon: SiSupabase },
- { name: "Neon", Icon: Database },
- { name: "Vector Databases", Icon: TbVectorTriangle },
- { name: "Data Stack", Icon: FaDatabase },
- ],
- },
- {
- category: "Infrastructure & DevOps",
- categoryKey: "infrastructureDevops",
- items: [
- { name: "Docker", Icon: SiDocker },
- { name: "Google Cloud", Icon: SiGooglecloud },
- { name: "Firebase", Icon: SiFirebase },
- { name: "Vercel", Icon: SiVercel },
- { name: "Netlify", Icon: SiNetlify },
- { name: "Windows OS", Icon: Monitor },
- ],
- },
- {
- category: "AI & Automation",
- categoryKey: "aiAutomation",
- items: [
- { name: "ChatGPT", Icon: RiOpenaiFill },
- { name: "LLM Integration", Icon: FaBrain },
- { name: "Ollama", Icon: SiOllama },
- { name: "RAG Systems", Icon: Network },
- { name: "MCPs", Icon: FileText },
- { name: "n8n", Icon: SiN8N },
- { name: "GitHub Actions", Icon: SiGithubactions },
- { name: "LangFlow", Icon: TbAutomation },
- { name: "Workflow Automation", Icon: FaRobot },
- ],
- },
- {
- category: "Development Workflow",
- categoryKey: "developmentWorkflow",
- items: [
- { name: "Git", Icon: SiGit },
- { name: "GitHub", Icon: SiGithub },
- { name: "Bitbucket", Icon: SiBitbucket },
- { name: "VSCode", Icon: BiLogoVisualStudio },
- { name: "WebStorm", Icon: SiWebstorm },
- { name: "PyCharm", Icon: SiPycharm },
- ],
- },
- {
- category: "Design & Creative",
- categoryKey: "designCreative",
- items: [
- { name: "Figma", Icon: FaFigma },
- { name: "Photo Editing", Icon: FaImage },
- { name: "Video Editing", Icon: FaVideo },
- ],
- },
- {
- category: "Business & Productivity Tools",
- categoryKey: "businessProductivity",
- items: [
- { name: "Jira", Icon: SiJira },
- { name: "Notion", Icon: SiNotion },
- { name: "Miro", Icon: SiMiro },
- { name: "SEO & Google Console", Icon: SiGoogleanalytics },
- ],
- },
- {
- category: "Documentation & Technical Writing",
- categoryKey: "documentationTechnicalWriting",
- items: [
- { name: "Diagrams & Flowcharts", Icon: RiFlowChart },
- { name: "LaTeX", Icon: SiLatex },
- { name: "Markdown", Icon: SiMarkdown },
- { name: "PowerPoint Presentations", Icon: MonitorSpeaker },
- ],
- },
- {
- category: "Professional Skills",
- categoryKey: "professionalSkills",
- items: [
- { name: "SCRUM/Agile", Icon: DiScrum },
- { name: "Research & Information Finding", Icon: FaSearch },
- { name: "Technical Documentation", Icon: FaBook },
- ],
- },
- {
- category: "Soft Skills",
- categoryKey: "softSkills",
- items: [
- { name: "Team Collaboration", Icon: FaUsers },
- { name: "Communication", Icon: MessageCircle },
- { name: "Problem Solving", Icon: FaLightbulb },
- { name: "Time Management", Icon: Clock },
- ],
- },
- {
- category: "Security & Privacy",
- categoryKey: "securityPrivacy",
- items: [
- { name: "Data Protection", Icon: FaShieldAlt },
- { name: "Authentication & Authorization", Icon: FaLock },
- { name: "API Security", Icon: FaKey },
- { name: "Privacy Compliance", Icon: FaUserShield },
- ],
- },
-];
diff --git a/temp-not-in-use/tech/translations.backup.json b/temp-not-in-use/tech/translations.backup.json
deleted file mode 100644
index 84ad08a..0000000
--- a/temp-not-in-use/tech/translations.backup.json
+++ /dev/null
@@ -1,117 +0,0 @@
-{
- "en": {
- "Technologies": {
- "title": "Skills, Technologies & Expertise",
- "manyMoreTechnologies": "...and many more skills & technologies in my toolkit",
- "categories": {
- "uiFrontend": "UI & Frontend",
- "backendApis": "Backend & APIs",
- "softwareEngineeringFundamentals": "Software Engineering Fundamentals",
- "coreProgrammingConcepts": "Core Programming Concepts",
- "architectureSystemDesign": "Architecture & System Design",
- "databasesStorage": "Databases & Storage",
- "infrastructureDevops": "Infrastructure & DevOps",
- "aiAutomation": "AI & Automation",
- "developmentWorkflow": "Development Workflow",
- "designCreative": "Design & Creative",
- "businessProductivity": "Business & Productivity Tools",
- "documentationTechnicalWriting": "Documentation & Technical Writing",
- "professionalSkills": "Professional Skills",
- "softSkills": "Soft Skills",
- "securityPrivacy": "Security & Privacy"
- }
- }
- },
- "de": {
- "Technologies": {
- "title": "Fähigkeiten, Technologien & Expertise",
- "manyMoreTechnologies": "...und viele weitere Fähigkeiten & Technologien in meinem Toolkit",
- "categories": {
- "uiFrontend": "UI & Frontend",
- "backendApis": "Backend & APIs",
- "softwareEngineeringFundamentals": "Software-Engineering Grundlagen",
- "coreProgrammingConcepts": "Programmierkonzepte",
- "architectureSystemDesign": "Architektur & Systemdesign",
- "databasesStorage": "Datenbanken & Speicher",
- "infrastructureDevops": "Infrastruktur & DevOps",
- "aiAutomation": "KI & Automatisierung",
- "developmentWorkflow": "Entwicklungsworkflow",
- "designCreative": "Design & Kreativ",
- "businessProductivity": "Business & Produktivitäts-Tools",
- "documentationTechnicalWriting": "Dokumentation & Technisches Schreiben",
- "professionalSkills": "Berufliche Fähigkeiten",
- "softSkills": "Soft Skills",
- "securityPrivacy": "Sicherheit & Datenschutz"
- }
- }
- },
- "es": {
- "Technologies": {
- "title": "Habilidades, Tecnologías y Experiencia",
- "manyMoreTechnologies": "...y muchas más habilidades y tecnologías en mi caja de herramientas",
- "categories": {
- "uiFrontend": "UI & Frontend",
- "backendApis": "Backend & APIs",
- "softwareEngineeringFundamentals": "Fundamentos de Ingeniería de Software",
- "coreProgrammingConcepts": "Conceptos de Programación",
- "architectureSystemDesign": "Arquitectura y Diseño de Sistemas",
- "databasesStorage": "Bases de datos & Almacenamiento",
- "infrastructureDevops": "Infraestructura & DevOps",
- "aiAutomation": "IA & Automatización",
- "developmentWorkflow": "Flujo de Desarrollo",
- "designCreative": "Diseño & Creatividad",
- "businessProductivity": "Herramientas de Negocio & Productividad",
- "documentationTechnicalWriting": "Documentación & Escritura Técnica",
- "professionalSkills": "Habilidades Profesionales",
- "softSkills": "Habilidades Blandas",
- "securityPrivacy": "Seguridad y Privacidad"
- }
- }
- },
- "fr": {
- "Technologies": {
- "title": "Compétences, Technologies et Expertise",
- "manyMoreTechnologies": "...et bien d'autres compétences et technologies dans ma boîte à outils",
- "categories": {
- "uiFrontend": "UI & Frontend",
- "backendApis": "Backend & APIs",
- "softwareEngineeringFundamentals": "Fondamentaux du Génie Logiciel",
- "coreProgrammingConcepts": "Concepts de Programmation",
- "architectureSystemDesign": "Architecture & Conception de Systèmes",
- "databasesStorage": "Bases de données & Stockage",
- "infrastructureDevops": "Infrastructure & DevOps",
- "aiAutomation": "IA & Automatisation",
- "developmentWorkflow": "Flux de Développement",
- "designCreative": "Design & Créativité",
- "businessProductivity": "Outils Business & Productivité",
- "documentationTechnicalWriting": "Documentation & Rédaction Technique",
- "professionalSkills": "Compétences Professionnelles",
- "softSkills": "Compétences Douces",
- "securityPrivacy": "Sécurité et Confidentialité"
- }
- }
- },
- "sv": {
- "Technologies": {
- "title": "Färdigheter, Teknologier & Expertis",
- "manyMoreTechnologies": "...och många fler färdigheter och teknologier i min verktygslåda",
- "categories": {
- "uiFrontend": "UI & Frontend",
- "backendApis": "Backend & API:er",
- "softwareEngineeringFundamentals": "Mjukvaruteknik Grunderna",
- "coreProgrammingConcepts": "Programmeringskoncept",
- "architectureSystemDesign": "Arkitektur & Systemdesign",
- "databasesStorage": "Databaser & Lagring",
- "infrastructureDevops": "Infrastruktur & DevOps",
- "aiAutomation": "AI & Automatisering",
- "developmentWorkflow": "Utvecklingsflöde",
- "designCreative": "Design & Kreativitet",
- "businessProductivity": "Business & Produktivitetsverktyg",
- "documentationTechnicalWriting": "Dokumentation & Teknisk Skrivande",
- "professionalSkills": "Professionella Färdigheter",
- "softSkills": "Mjuka Färdigheter",
- "securityPrivacy": "Säkerhet & Integritet"
- }
- }
- }
-}
diff --git a/temp-not-in-use/tech/use-responsive-carousel.ts.backup b/temp-not-in-use/tech/use-responsive-carousel.ts.backup
deleted file mode 100644
index e68bdfa..0000000
--- a/temp-not-in-use/tech/use-responsive-carousel.ts.backup
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-"use client";
-
-import { useState, useEffect } from "react";
-
-/**
- * Responsive breakpoint configuration for carousel display
- */
-interface ResponsiveCarouselConfig {
- /** Number of cards to display per slide */
- readonly cardsPerSlide: number;
- /** Whether the current view is mobile */
- readonly isMobile: boolean;
-}
-
-/**
- * Hook to manage responsive carousel behavior based on screen size
- * @returns Configuration object with cardsPerSlide and isMobile flags
- */
-export function useResponsiveCarousel(): ResponsiveCarouselConfig {
- const [cardsPerSlide, setCardsPerSlide] = useState(3);
- const [isMobile, setIsMobile] = useState(false);
-
- useEffect(() => {
- const checkScreenSize = (): void => {
- const width = window.innerWidth;
- if (width < 640) {
- // mobile
- setCardsPerSlide(1);
- setIsMobile(true);
- } else if (width < 1024) {
- // tablet
- setCardsPerSlide(2);
- setIsMobile(false);
- } else {
- // desktop
- setCardsPerSlide(3);
- setIsMobile(false);
- }
- };
-
- checkScreenSize();
- window.addEventListener("resize", checkScreenSize);
- return () => window.removeEventListener("resize", checkScreenSize);
- }, []);
-
- return { cardsPerSlide, isMobile };
-}
diff --git a/types/configs/pagespeed.ts b/types/configs/pagespeed.ts
deleted file mode 100644
index 045bcfe..0000000
--- a/types/configs/pagespeed.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * PageSpeed Interface Types
- * @author ColdByDefault
- * @copyright 2026 ColdByDefault. All Rights Reserved.
-*/
-
-export interface PageSpeedMetrics {
- performance: number;
- accessibility: number;
- bestPractices: number;
- seo: number;
- pwa?: number;
-}
-
-export interface PageSpeedResult {
- url: string;
- strategy: "mobile" | "desktop";
- metrics: PageSpeedMetrics;
- loadingExperience?: {
- metrics: {
- FIRST_CONTENTFUL_PAINT_MS?: { percentile: number };
- FIRST_INPUT_DELAY_MS?: { percentile: number };
- LARGEST_CONTENTFUL_PAINT_MS?: { percentile: number };
- CUMULATIVE_LAYOUT_SHIFT_SCORE?: { percentile: number };
- };
- };
-}
-
-export interface PageSpeedApiResponse {
- error?: string;
- details?: string;
- url?: string;
- strategy?: string;
- metrics?: PageSpeedMetrics;
- loadingExperience?: PageSpeedResult["loadingExperience"];
- retryAfter?: number;
-}
-
-export interface PageSpeedInsightsProps {
- url?: string;
- showRefreshButton?: boolean;
- showBothStrategies?: boolean;
-}
-
-export interface PageSpeedLighthouseCategory {
- id: string;
- title: string;
- score: number | null;
-}
-
-export interface PageSpeedLighthouseResult {
- categories: {
- performance?: PageSpeedLighthouseCategory;
- accessibility?: PageSpeedLighthouseCategory;
- "best-practices"?: PageSpeedLighthouseCategory;
- seo?: PageSpeedLighthouseCategory;
- pwa?: PageSpeedLighthouseCategory;
- };
-}
-
-export interface PageSpeedApiRawResponse {
- id?: string;
- lighthouseResult?: PageSpeedLighthouseResult;
-}