From 823c722f94339032e7a720dd5ee478f792afbb78 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 1 Nov 2025 20:02:27 +0000
Subject: [PATCH 1/4] Initial plan
From 39f6fd22830a04757a3cdf592394ac5cc59fd77f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 1 Nov 2025 20:12:30 +0000
Subject: [PATCH 2/4] Refactor date formatting and data normalization utilities
- Create reusable date formatting utilities in lib/i18n.ts
- Create data normalization utilities in lib/utils.ts
- Update Blog.tsx, Projects.tsx, Dashboard.tsx, blog/[slug].tsx, and CommentsSection.tsx to use new date formatting utilities
- Update Contact.tsx and About.tsx to use new normalization utilities
- Reduce code duplication across 7 files
Co-authored-by: marcelo-m7 <117441129+marcelo-m7@users.noreply.github.com>
---
src/components/blog/CommentsSection.tsx | 24 ++----
src/lib/i18n.ts | 49 +++++++++++
src/lib/utils.ts | 107 ++++++++++++++++++++++++
src/pages/About.tsx | 62 ++++----------
src/pages/Blog.tsx | 19 +----
src/pages/Contact.tsx | 54 +-----------
src/pages/Dashboard.tsx | 51 +++--------
src/pages/Projects.tsx | 48 +++--------
src/pages/blog/[slug].tsx | 17 +---
9 files changed, 209 insertions(+), 222 deletions(-)
diff --git a/src/components/blog/CommentsSection.tsx b/src/components/blog/CommentsSection.tsx
index bd3aafe..d0e43de 100644
--- a/src/components/blog/CommentsSection.tsx
+++ b/src/components/blog/CommentsSection.tsx
@@ -11,7 +11,7 @@ import { Textarea } from '@/components/ui/textarea';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Loader2 } from 'lucide-react';
import { Link } from 'react-router-dom';
-import { getNormalizedLocale } from '@/lib/i18n';
+import { createDateFormatter } from '@/lib/i18n';
interface CommentsSectionProps {
postId: string;
@@ -47,26 +47,14 @@ const CommentsSection = ({ postId }: CommentsSectionProps) => {
const queryClient = useQueryClient();
const [newComment, setNewComment] = useState('');
- const normalizedLocale = useMemo(
- () => getNormalizedLocale(i18n.language),
+ const dateFormatter = useMemo(
+ () => createDateFormatter(i18n.language, {
+ dateStyle: 'medium',
+ timeStyle: 'short',
+ }),
[i18n.language]
);
- const dateFormatter = useMemo(() => {
- try {
- return new Intl.DateTimeFormat(normalizedLocale, {
- dateStyle: 'medium',
- timeStyle: 'short',
- });
- } catch (error) {
- console.error('Unsupported locale for comment date formatting', error);
- return new Intl.DateTimeFormat('en-US', {
- dateStyle: 'medium',
- timeStyle: 'short',
- });
- }
- }, [normalizedLocale]);
-
const {
data: comments,
isLoading,
diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts
index b4013a5..d2668a6 100644
--- a/src/lib/i18n.ts
+++ b/src/lib/i18n.ts
@@ -20,3 +20,52 @@ export const getNormalizedLocale = (language: string | undefined | null) => {
return language;
}
};
+
+/**
+ * Creates a date formatter with fallback handling
+ * @param locale - The locale to use for formatting
+ * @param options - Intl.DateTimeFormat options
+ * @returns A DateTimeFormat instance with fallback to en-US on error
+ */
+export const createDateFormatter = (
+ locale: string | undefined | null,
+ options: Intl.DateTimeFormatOptions = { dateStyle: 'medium' }
+): Intl.DateTimeFormat => {
+ const normalizedLocale = getNormalizedLocale(locale);
+
+ try {
+ return new Intl.DateTimeFormat(normalizedLocale, options);
+ } catch (error) {
+ console.error(`Unsupported locale for date formatting: ${normalizedLocale}`, error);
+ return new Intl.DateTimeFormat('en-US', options);
+ }
+};
+
+/**
+ * Formats a date string with fallback handling
+ * @param dateString - The date string to format
+ * @param formatter - The DateTimeFormat instance to use
+ * @param fallbackFormatter - Optional fallback formatter
+ * @returns Formatted date string or em-dash if invalid
+ */
+export const formatDate = (
+ dateString: string | null | undefined,
+ formatter: Intl.DateTimeFormat,
+ fallbackFormatter?: Intl.DateTimeFormat
+): string => {
+ if (!dateString) return '—';
+
+ try {
+ return formatter.format(new Date(dateString));
+ } catch (error) {
+ console.error('Error formatting date', error);
+ if (fallbackFormatter) {
+ try {
+ return fallbackFormatter.format(new Date(dateString));
+ } catch {
+ return '—';
+ }
+ }
+ return '—';
+ }
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 2819a83..75d7cde 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -4,3 +4,110 @@ import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+
+/**
+ * Type guard to check if value is a non-empty string
+ */
+const isNonEmptyString = (value: unknown): value is string =>
+ typeof value === 'string' && value.trim().length > 0;
+
+/**
+ * Normalizes a value to an array of strings
+ * Handles JSON strings, arrays, and objects with an 'options' property
+ * @param value - The value to normalize
+ * @returns An array of unique non-empty strings
+ */
+export const normalizeToStringArray = (value: unknown): string[] => {
+ if (value === null || value === undefined) {
+ return [];
+ }
+
+ let parsedValue = value;
+
+ // Try to parse JSON string
+ if (typeof parsedValue === 'string') {
+ const trimmedValue = parsedValue.trim();
+ if (!trimmedValue) {
+ return [];
+ }
+
+ try {
+ parsedValue = JSON.parse(trimmedValue);
+ } catch {
+ return [trimmedValue];
+ }
+ }
+
+ // Handle arrays
+ if (Array.isArray(parsedValue)) {
+ return Array.from(
+ new Set(
+ parsedValue
+ .filter(isNonEmptyString)
+ .map((item) => item.trim())
+ )
+ );
+ }
+
+ // Handle objects with 'options' or 'stats' property
+ if (typeof parsedValue === 'object' && parsedValue !== null) {
+ const record = parsedValue as Record;
+ if (Array.isArray(record.options)) {
+ return Array.from(
+ new Set(
+ record.options
+ .filter(isNonEmptyString)
+ .map((item) => item.trim())
+ )
+ );
+ }
+ }
+
+ return [];
+};
+
+/**
+ * Normalizes a value to an array of typed objects
+ * @param value - The value to normalize
+ * @param validator - Function to validate and transform each item
+ * @returns An array of validated objects
+ */
+export const normalizeToTypedArray = (
+ value: unknown,
+ validator: (item: unknown) => T | null
+): T[] => {
+ if (!value) {
+ return [];
+ }
+
+ let parsedValue: unknown = value;
+
+ // Try to parse JSON string
+ if (typeof parsedValue === 'string') {
+ try {
+ parsedValue = JSON.parse(parsedValue);
+ } catch {
+ return [];
+ }
+ }
+
+ // Handle arrays
+ if (Array.isArray(parsedValue)) {
+ return parsedValue
+ .map(validator)
+ .filter((item): item is T => item !== null);
+ }
+
+ // Handle objects with nested arrays
+ if (typeof parsedValue === 'object' && parsedValue !== null) {
+ const record = parsedValue as Record;
+ if (Array.isArray(record.stats)) {
+ return normalizeToTypedArray(record.stats, validator);
+ }
+ if (Array.isArray(record.options)) {
+ return normalizeToTypedArray(record.options, validator);
+ }
+ }
+
+ return [];
+};
diff --git a/src/pages/About.tsx b/src/pages/About.tsx
index cf2725e..7aaca54 100644
--- a/src/pages/About.tsx
+++ b/src/pages/About.tsx
@@ -9,6 +9,7 @@ import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { supabase } from '@/integrations/supabase';
import { useTranslation } from 'react-i18next';
+import { normalizeToTypedArray } from '@/lib/utils';
interface TeamMemberCard {
id: string;
@@ -22,51 +23,6 @@ interface AboutStat {
label: string;
}
-const normalizeAboutStats = (value: unknown): AboutStat[] => {
- if (!value) {
- return [];
- }
-
- let parsedValue: unknown = value;
-
- if (typeof parsedValue === 'string') {
- try {
- parsedValue = JSON.parse(parsedValue);
- } catch {
- return [];
- }
- }
-
- if (Array.isArray(parsedValue)) {
- return parsedValue
- .map((item) => {
- if (item && typeof item === 'object') {
- const record = item as Record;
- const number = record.number;
- const label = record.label;
-
- if ((typeof number === 'string' || typeof number === 'number') && typeof label === 'string') {
- return {
- number: number.toString(),
- label,
- } satisfies AboutStat;
- }
- }
- return null;
- })
- .filter((stat): stat is AboutStat => stat !== null);
- }
-
- if (typeof parsedValue === 'object' && parsedValue !== null) {
- const record = parsedValue as Record;
- if (Array.isArray(record.stats)) {
- return normalizeAboutStats(record.stats);
- }
- }
-
- return [];
-};
-
const About = () => {
const { t } = useTranslation();
const values = useMemo(
@@ -133,7 +89,21 @@ const About = () => {
if (error) throw error;
- return normalizeAboutStats(data?.value);
+ return normalizeToTypedArray(data?.value, (item) => {
+ if (item && typeof item === 'object') {
+ const record = item as Record;
+ const number = record.number;
+ const label = record.label;
+
+ if ((typeof number === 'string' || typeof number === 'number') && typeof label === 'string') {
+ return {
+ number: number.toString(),
+ label,
+ };
+ }
+ }
+ return null;
+ });
},
});
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx
index c4fe407..6f4b957 100644
--- a/src/pages/Blog.tsx
+++ b/src/pages/Blog.tsx
@@ -31,7 +31,7 @@ import {
PaginationEllipsis,
} from '@/components/ui/pagination';
import { cn } from '@/lib/utils';
-import { getNormalizedLocale } from '@/lib/i18n';
+import { createDateFormatter } from '@/lib/i18n';
import type { Database } from '@/integrations/supabase/types';
const FALLBACK_IMAGE =
@@ -94,24 +94,11 @@ const Blog = () => {
const { t, i18n } = useTranslation();
const [page, setPage] = useState(1);
- const normalizedLocale = useMemo(
- () => getNormalizedLocale(i18n.language),
+ const dateFormatter = useMemo(
+ () => createDateFormatter(i18n.language, { dateStyle: 'medium' }),
[i18n.language]
);
- const dateFormatter = useMemo(() => {
- try {
- return new Intl.DateTimeFormat(normalizedLocale, {
- dateStyle: 'medium',
- });
- } catch (error) {
- console.error('Unsupported locale for blog date formatting', error);
- return new Intl.DateTimeFormat('en-US', {
- dateStyle: 'medium',
- });
- }
- }, [normalizedLocale]);
-
const categories = useMemo(
() => BLOG_CATEGORY_KEYS.map((key) => t(key)),
[t]
diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx
index 25274d9..d4bca40 100644
--- a/src/pages/Contact.tsx
+++ b/src/pages/Contact.tsx
@@ -19,6 +19,7 @@ import {
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import { useQuery } from '@tanstack/react-query';
+import { normalizeToStringArray } from '@/lib/utils';
const PROJECT_TYPE_KEYS = [
'contact.projectTypes.customAssistant',
@@ -37,57 +38,6 @@ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const isValidatableField = (value: string): value is ValidatableField =>
(validatableFields as readonly string[]).includes(value);
-const isNonEmptyString = (value: unknown): value is string =>
- typeof value === 'string' && value.trim().length > 0;
-
-const normalizeProjectTypes = (value: unknown): string[] => {
- if (value === null || value === undefined) {
- return [];
- }
-
- let parsedValue = value;
-
- if (typeof parsedValue === 'string') {
- const trimmedValue = parsedValue.trim();
-
- if (!trimmedValue) {
- return [];
- }
-
- try {
- parsedValue = JSON.parse(trimmedValue);
- } catch {
- return [trimmedValue];
- }
- }
-
- if (Array.isArray(parsedValue)) {
- return Array.from(
- new Set(
- parsedValue
- .filter(isNonEmptyString)
- .map((option) => option.trim())
- )
- );
- }
-
- if (typeof parsedValue === 'object' && parsedValue !== null) {
- const record = parsedValue as Record;
-
- if (Array.isArray(record.options)) {
- return Array.from(
- new Set(
- record.options
- .filter(isNonEmptyString)
- .map((option) => option.trim())
- )
- );
- }
- }
-
- return [];
-};
-
const Contact = () => {
const { t } = useTranslation();
const [formData, setFormData] = useState({
@@ -121,7 +71,7 @@ const Contact = () => {
if (error) throw error;
- return normalizeProjectTypes(data?.value);
+ return normalizeToStringArray(data?.value);
},
staleTime: 1000 * 60 * 10,
});
diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx
index d9618bd..311b47c 100644
--- a/src/pages/Dashboard.tsx
+++ b/src/pages/Dashboard.tsx
@@ -26,7 +26,7 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import Loading from '@/components/Loading';
import { AlertTriangle, Loader2, LogOut, RefreshCcw } from 'lucide-react';
import { useTranslation } from 'react-i18next';
-import { getNormalizedLocale } from '@/lib/i18n';
+import { createDateFormatter, formatDate } from '@/lib/i18n';
type Profile = Database['public']['Tables']['profiles']['Row'];
type Lead = Database['public']['Tables']['leads']['Row'];
@@ -149,44 +149,17 @@ const Dashboard = () => {
void loadDashboard();
}, [loadDashboard]);
- const normalizedLocale = useMemo(
- () => getNormalizedLocale(i18n.language),
+ const dateTimeFormatter = useMemo(
+ () => createDateFormatter(i18n.language, {
+ dateStyle: 'short',
+ timeStyle: 'short',
+ }),
[i18n.language]
);
- const fallbackDateFormatter = useMemo(
- () =>
- new Intl.DateTimeFormat('en-US', {
- dateStyle: 'short',
- timeStyle: 'short',
- }),
- []
- );
-
- const dateTimeFormatter = useMemo(() => {
- try {
- return new Intl.DateTimeFormat(normalizedLocale, {
- dateStyle: 'short',
- timeStyle: 'short',
- });
- } catch (error) {
- console.error('Unsupported locale for dashboard date formatting', error);
- return fallbackDateFormatter;
- }
- }, [fallbackDateFormatter, normalizedLocale]);
-
- const formatDate = useCallback(
- (value: string | null | undefined) => {
- if (!value) return '—';
-
- try {
- return dateTimeFormatter.format(new Date(value));
- } catch (error) {
- console.error('Error formatting dashboard date', error);
- return fallbackDateFormatter.format(new Date(value));
- }
- },
- [dateTimeFormatter, fallbackDateFormatter]
+ const formatDashboardDate = useCallback(
+ (value: string | null | undefined) => formatDate(value, dateTimeFormatter),
+ [dateTimeFormatter]
);
const handleRefresh = () => {
@@ -350,7 +323,7 @@ const Dashboard = () => {
{t('dashboard.profile.updatedAt')}
- {formatDate(profile?.updated_at)}
+ {formatDashboardDate(profile?.updated_at)}
@@ -401,7 +374,7 @@ const Dashboard = () => {
{lead.project ?? '—'}
{lead.message}
- {formatDate(lead.created_at)}
+ {formatDashboardDate(lead.created_at)}
))}
@@ -451,7 +424,7 @@ const Dashboard = () => {
- {formatDate(subscriber.subscribed_at)}
+ {formatDashboardDate(subscriber.subscribed_at)}
))}
diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx
index c75d31f..503100c 100644
--- a/src/pages/Projects.tsx
+++ b/src/pages/Projects.tsx
@@ -25,7 +25,7 @@ import {
} from '@/lib/solutions';
import type { GitHubRepository } from '@/lib/solutions';
import type { SolutionContent } from '@/types/solutions';
-import { getNormalizedLocale } from '@/lib/i18n';
+import { createDateFormatter, formatDate } from '@/lib/i18n';
import {
SolutionCard,
sectionContainer,
@@ -52,44 +52,18 @@ const Projects = () => {
const memoizedFallbackSolutions = useMemo(() => getFallbackSolutions(), []);
- const normalizedLocale = useMemo(
- () => getNormalizedLocale(i18n.language),
+ const dateFormatter = useMemo(
+ () => createDateFormatter(i18n.language, {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ }),
[i18n.language]
);
- const fallbackDateFormatter = useMemo(
- () =>
- new Intl.DateTimeFormat('en-US', {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- }),
- []
- );
-
- const dateFormatter = useMemo(() => {
- try {
- return new Intl.DateTimeFormat(normalizedLocale, {
- year: 'numeric',
- month: 'short',
- day: 'numeric',
- });
- } catch (error) {
- console.error('Unsupported locale for project date formatting', error);
- return fallbackDateFormatter;
- }
- }, [fallbackDateFormatter, normalizedLocale]);
-
- const formatDate = useCallback(
- (dateString: string) => {
- try {
- return dateFormatter.format(new Date(dateString));
- } catch (error) {
- console.error('Error formatting project date', error);
- return fallbackDateFormatter.format(new Date(dateString));
- }
- },
- [dateFormatter, fallbackDateFormatter]
+ const formatProjectDate = useCallback(
+ (dateString: string) => formatDate(dateString, dateFormatter),
+ [dateFormatter]
);
const {
@@ -457,7 +431,7 @@ const Projects = () => {
- {formatDate(repo.created_at)}
+ {formatProjectDate(repo.created_at)}
diff --git a/src/pages/blog/[slug].tsx b/src/pages/blog/[slug].tsx
index b1e024f..989b7b9 100644
--- a/src/pages/blog/[slug].tsx
+++ b/src/pages/blog/[slug].tsx
@@ -8,7 +8,7 @@ import Meta from '@/components/Meta';
import NewsletterSection from '@/components/NewsletterSection';
import CommentsSection from '@/components/blog/CommentsSection';
import { supabase } from '@/integrations/supabase';
-import { getNormalizedLocale } from '@/lib/i18n';
+import { createDateFormatter } from '@/lib/i18n';
import type { Database } from '@/integrations/supabase/types';
import {
Breadcrumb,
@@ -213,22 +213,11 @@ const BlogPostPage = () => {
const { slug } = useParams<{ slug: string }>();
const { t, i18n } = useTranslation();
- const normalizedLocale = useMemo(
- () => getNormalizedLocale(i18n.language),
+ const dateFormatter = useMemo(
+ () => createDateFormatter(i18n.language, { dateStyle: 'long' }),
[i18n.language]
);
- const dateFormatter = useMemo(() => {
- try {
- return new Intl.DateTimeFormat(normalizedLocale, {
- dateStyle: 'long',
- });
- } catch (error) {
- console.error('Unsupported locale for blog post date formatting', error);
- return new Intl.DateTimeFormat('en-US', { dateStyle: 'long' });
- }
- }, [normalizedLocale]);
-
const {
data: post,
isLoading,
From f3372743614e7994b4999c2ebfe59ed30e0a7603 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 1 Nov 2025 20:16:56 +0000
Subject: [PATCH 3/4] Create reusable PageBreadcrumb component
- Add PageBreadcrumb component to reduce breadcrumb duplication
- Update Contact.tsx, Blog.tsx, Solutions.tsx, Projects.tsx to use PageBreadcrumb
- Update blog/[slug].tsx and solutions/[slug].tsx to use PageBreadcrumb with intermediate items
- Remove ~150 lines of duplicated breadcrumb code across 6 pages
Co-authored-by: marcelo-m7 <117441129+marcelo-m7@users.noreply.github.com>
---
src/components/PageBreadcrumb.tsx | 61 +++++++++++++++++++++++++++++++
src/pages/Blog.tsx | 25 +------------
src/pages/Contact.tsx | 42 ++-------------------
src/pages/Projects.tsx | 25 +------------
src/pages/Solutions.tsx | 25 +------------
src/pages/blog/[slug].tsx | 34 +++--------------
src/pages/solutions/[slug].tsx | 34 +++--------------
7 files changed, 80 insertions(+), 166 deletions(-)
create mode 100644 src/components/PageBreadcrumb.tsx
diff --git a/src/components/PageBreadcrumb.tsx b/src/components/PageBreadcrumb.tsx
new file mode 100644
index 0000000..be690e4
--- /dev/null
+++ b/src/components/PageBreadcrumb.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
+import {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from '@/components/ui/breadcrumb';
+
+interface PageBreadcrumbProps {
+ /**
+ * The current page title, will be shown as the last breadcrumb item
+ */
+ currentPage: string;
+ /**
+ * Optional additional breadcrumb items between home and current page
+ */
+ items?: Array<{
+ label: string;
+ href: string;
+ }>;
+}
+
+/**
+ * A reusable breadcrumb component for pages
+ * Always includes Home as the first item and current page as the last
+ */
+export const PageBreadcrumb = ({ currentPage, items = [] }: PageBreadcrumbProps) => {
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+
+ {t('navigation.home')}
+
+
+
+ {items.map((item, index) => (
+
+
+
+ {item.label}
+
+
+
+
+ ))}
+
+ {currentPage}
+
+
+
+
+ );
+};
diff --git a/src/pages/Blog.tsx b/src/pages/Blog.tsx
index 6f4b957..af76848 100644
--- a/src/pages/Blog.tsx
+++ b/src/pages/Blog.tsx
@@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button';
import Layout from '@/components/Layout';
import Meta from '@/components/Meta';
import NewsletterSection from '@/components/NewsletterSection';
+import { PageBreadcrumb } from '@/components/PageBreadcrumb';
import {
ArrowRight,
Clock,
@@ -15,14 +16,6 @@ import {
import { supabase } from '@/integrations/supabase';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
-import {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@/components/ui/breadcrumb';
import {
Pagination,
PaginationContent,
@@ -226,21 +219,7 @@ const Blog = () => {
ogDescription={t('blog.description')}
ogImage="/placeholder.svg"
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
- {t('navigation.blog')}
-
-
-
-
+
diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx
index d4bca40..4a28387 100644
--- a/src/pages/Contact.tsx
+++ b/src/pages/Contact.tsx
@@ -5,19 +5,11 @@ import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import Layout from '@/components/Layout';
import Meta from '@/components/Meta';
+import { PageBreadcrumb } from '@/components/PageBreadcrumb';
import { Mail, Phone, MapPin, Send, CheckCircle } from 'lucide-react';
import { supabase } from '@/integrations/supabase';
import { useToast } from '@/hooks/use-toast';
import { useTranslation } from 'react-i18next';
-import { Link } from 'react-router-dom';
-import {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@/components/ui/breadcrumb';
import { useQuery } from '@tanstack/react-query';
import { normalizeToStringArray } from '@/lib/utils';
@@ -261,21 +253,7 @@ const Contact = () => {
ogDescription={t('contact.description')}
ogImage="/placeholder.svg"
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
- {t('navigation.contact')}
-
-
-
-
+
@@ -308,21 +286,7 @@ const Contact = () => {
ogDescription={t('contact.description')}
ogImage="/placeholder.svg"
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
- {t('navigation.contact')}
-
-
-
-
+
{/* Hero Section */}
diff --git a/src/pages/Projects.tsx b/src/pages/Projects.tsx
index 503100c..c4516ae 100644
--- a/src/pages/Projects.tsx
+++ b/src/pages/Projects.tsx
@@ -3,17 +3,10 @@ import { useQuery } from '@tanstack/react-query';
import { Github, ExternalLink, Calendar, ArrowRight } from 'lucide-react';
import Layout from '../components/Layout';
import Meta from '@/components/Meta';
+import { PageBreadcrumb } from '@/components/PageBreadcrumb';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
-import {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@/components/ui/breadcrumb';
import { supabase } from '@/integrations/supabase';
import { useTranslation, Trans } from 'react-i18next';
import { Link } from 'react-router-dom';
@@ -228,21 +221,7 @@ const Projects = () => {
ogDescription={t('projects.description')}
ogImage="/placeholder.svg"
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
- {t('navigation.projects')}
-
-
-
-
+
diff --git a/src/pages/Solutions.tsx b/src/pages/Solutions.tsx
index 596f39f..8972bf6 100644
--- a/src/pages/Solutions.tsx
+++ b/src/pages/Solutions.tsx
@@ -3,15 +3,8 @@ import { useQuery } from '@tanstack/react-query';
import { Button } from '@/components/ui/button';
import Layout from '@/components/Layout';
import Meta from '@/components/Meta';
+import { PageBreadcrumb } from '@/components/PageBreadcrumb';
import { Link } from 'react-router-dom';
-import {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@/components/ui/breadcrumb';
import { ArrowRight } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { supabase } from '@/integrations/supabase';
@@ -109,21 +102,7 @@ const Solutions = () => {
ogDescription={t('solutionsPage.description')}
ogImage="/placeholder.svg"
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
- {t('navigation.solutions')}
-
-
-
-
+
{/* Hero Section */}
diff --git a/src/pages/blog/[slug].tsx b/src/pages/blog/[slug].tsx
index 989b7b9..b9600fb 100644
--- a/src/pages/blog/[slug].tsx
+++ b/src/pages/blog/[slug].tsx
@@ -7,17 +7,10 @@ import Layout from '@/components/Layout';
import Meta from '@/components/Meta';
import NewsletterSection from '@/components/NewsletterSection';
import CommentsSection from '@/components/blog/CommentsSection';
+import { PageBreadcrumb } from '@/components/PageBreadcrumb';
import { supabase } from '@/integrations/supabase';
import { createDateFormatter } from '@/lib/i18n';
import type { Database } from '@/integrations/supabase/types';
-import {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
-} from '@/components/ui/breadcrumb';
type BlogPost = Database['public']['Tables']['blog_posts']['Row'];
@@ -325,27 +318,10 @@ const BlogPostPage = () => {
ogDescription={metaDescription}
ogImage={metaImage}
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
-
- {t('navigation.blog')}
-
-
-
-
- {post.title}
-
-
-
-
+
{
ogDescription={displaySolution.description}
ogImage={displaySolution.imageUrl ?? '/placeholder.svg'}
/>
-
-
-
-
-
- {t('navigation.home')}
-
-
-
-
-
- {t('navigation.solutions')}
-
-
-
-
- {displaySolution.title}
-
-
-
-
+
From 4e23fad21476c5e7ea9046876161caee70ffde21 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 1 Nov 2025 20:19:30 +0000
Subject: [PATCH 4/4] Fix unused index parameter in PageBreadcrumb
- Remove unused index parameter from map callback in PageBreadcrumb component
Co-authored-by: marcelo-m7 <117441129+marcelo-m7@users.noreply.github.com>
---
src/components/PageBreadcrumb.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/PageBreadcrumb.tsx b/src/components/PageBreadcrumb.tsx
index be690e4..a4de8bd 100644
--- a/src/components/PageBreadcrumb.tsx
+++ b/src/components/PageBreadcrumb.tsx
@@ -41,7 +41,7 @@ export const PageBreadcrumb = ({ currentPage, items = [] }: PageBreadcrumbProps)
- {items.map((item, index) => (
+ {items.map((item) => (