Compare commits

...

5 Commits

Author SHA1 Message Date
8f05d06fb9 Fixed the AntD message warning (dynamic theme / context) 2025-12-17 17:18:31 +03:00
9548694f13 PreferencesSelector: fix responsive styles 2025-12-17 17:11:51 +03:00
2c84cbd1ca fix after antd updating 2025-12-17 16:46:11 +03:00
323a5665fe fix issue
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
2025-12-17 16:36:20 +03:00
adcab9eb3c Implemented tab title = current page 2025-12-17 15:26:19 +03:00
7 changed files with 129 additions and 36 deletions

View File

@@ -7,6 +7,10 @@ const nextConfig: NextConfig = {
protocol: "https",
hostname: "techmasters.space",
},
{
protocol: "https",
hostname: "tech-masters.guru",
},
],
},
};

View File

@@ -4,6 +4,8 @@ import { BellOutlined, UserOutlined } from "@ant-design/icons";
import { Avatar, Button, Menu, MenuProps, Typography } from "antd";
import { motion } from "framer-motion";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useEffect, useMemo } from "react";
import styles from "./Header.module.css";
const { Title } = Typography;
@@ -31,7 +33,41 @@ const items: MenuProps['items'] = [
},
];
const TITLE_SUFFIX = "Tech Masters";
const Header = () => {
const pathname = usePathname();
const selectedKey = useMemo(() => {
if (!pathname || pathname === "/") return "home";
const key = pathname.split("/")[1];
return key || "home";
}, [pathname]);
const pageTitle = useMemo(() => {
switch (selectedKey) {
case "home":
return "Home";
case "services":
return "Services";
case "projects":
return "Projects";
case "about":
return "About";
case "contact":
return "Contact";
case "testimonials":
return "Testimonials";
default:
return null;
}
}, [selectedKey]);
useEffect(() => {
if (!pageTitle) return;
document.title = `${pageTitle} | ${TITLE_SUFFIX}`;
}, [pageTitle]);
return (
<header className={styles.header}>
<div className={styles.headerContainer}>
@@ -68,7 +104,7 @@ const Header = () => {
mode="horizontal"
items={items}
className={styles.menu}
selectedKeys={['home']}
selectedKeys={[selectedKey]}
/>
</motion.div>
</nav>

View File

@@ -50,6 +50,7 @@ const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
onComplete,
}) => {
const [selectedPreferences, setSelectedPreferences] = useState<string[]>([]);
const [messageApi, contextHolder] = message.useMessage();
const handleTogglePreference = (id: string) => {
setSelectedPreferences((prev) =>
@@ -59,7 +60,7 @@ const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
const handleSubmit = () => {
if (selectedPreferences.length === 0) {
message.warning("Please select at least one preference");
messageApi.warning("Please select at least one preference");
return;
}
@@ -68,7 +69,7 @@ const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
"userPreferences",
JSON.stringify(selectedPreferences)
);
message.success("Preferences saved successfully!");
messageApi.success("Preferences saved successfully!");
onComplete();
};
@@ -79,6 +80,7 @@ const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{contextHolder}
<Card className={styles.card}>
<Title level={2}>Welcome to Tech Master</Title>
<Text>
@@ -86,7 +88,7 @@ const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
</Text>
<Space
direction="vertical"
orientation="vertical"
size="large"
className={styles.optionsContainer}
>

View File

@@ -11,6 +11,7 @@
align-items: center;
background: linear-gradient(135deg, #42475c 0%, #20222f 100%);
z-index: 1000;
padding: 16px;
}
.card {
@@ -21,6 +22,8 @@
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
max-height: calc(100vh - 32px);
overflow: auto;
}
.optionsContainer {
@@ -56,3 +59,39 @@
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.2);
}
@media (max-width: 600px) {
.container {
align-items: flex-start;
padding: 12px;
}
.card {
width: 100%;
max-width: 100%;
padding: 1rem;
border-radius: 14px;
max-height: calc(100vh - 24px);
}
.optionsContainer {
margin: 1rem 0;
}
.optionCard {
margin-bottom: 0.75rem;
}
.continueButton {
width: 100%;
min-width: 0;
height: 44px;
border-radius: 14px;
}
}
@media (max-width: 380px) {
.card {
padding: 0.85rem;
}
}

View File

@@ -22,13 +22,14 @@ const { TextArea } = Input;
export default function ContactPage() {
const [form] = Form.useForm();
const [submitting, setSubmitting] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const handleSubmit = async () => {
setSubmitting(true);
// Simulate API call
setTimeout(() => {
message.success("Your message has been sent successfully!");
messageApi.success("Your message has been sent successfully!");
form.resetFields();
setSubmitting(false);
}, 1500);
@@ -55,6 +56,7 @@ export default function ContactPage() {
return (
<main className={styles.main}>
{contextHolder}
{/* Hero Section */}
<section className={styles.heroSection}>
<div className={styles.heroBackground}>

View File

@@ -8,7 +8,7 @@ import {
ProjectOutlined,
RocketOutlined,
StarOutlined,
TeamOutlined
TeamOutlined,
} from "@ant-design/icons";
import { Card, Col, Row, Statistic, Typography } from "antd";
import { useEffect, useState } from "react";
@@ -22,21 +22,26 @@ import styles from "./page.module.css";
const { Title, Paragraph } = Typography;
export default function Home() {
const [userPreferences, setUserPreferences] = useState<string[]>(() => {
try {
const storedPreferences = localStorage.getItem("userPreferences");
return storedPreferences ? JSON.parse(storedPreferences) : [];
} catch {
return [];
}
});
const [showPreferences, setShowPreferences] = useState(
() => userPreferences.length === 0,
);
// Keep the initial render identical between server and client to avoid hydration mismatches.
// We'll read localStorage after mount.
const [, setUserPreferences] = useState<string[]>([]);
const [showPreferences, setShowPreferences] = useState(true);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Reading localStorage can only happen on the client; do it after mount.
try {
const storedPreferences = localStorage.getItem("userPreferences");
if (storedPreferences) {
/* eslint-disable react-hooks/set-state-in-effect */
setUserPreferences(JSON.parse(storedPreferences));
setShowPreferences(false);
/* eslint-enable react-hooks/set-state-in-effect */
}
} catch {
// ignore invalid JSON / access errors
}
// Simulate loading of resources
const timer = setTimeout(() => {
setLoading(false);
@@ -113,7 +118,8 @@ export default function Home() {
Our Services
</Title>
<Paragraph className={styles.sectionSubtitle}>
We offer comprehensive solutions to transform your digital presence and drive business growth
We offer comprehensive solutions to transform your digital
presence and drive business growth
</Paragraph>
</div>
@@ -127,7 +133,8 @@ export default function Home() {
Web Development
</Title>
<Paragraph className={styles.serviceDescription}>
Custom web applications built with modern technologies and best practices for optimal performance and user experience.
Custom web applications built with modern technologies and
best practices for optimal performance and user experience.
</Paragraph>
<ul className={styles.serviceFeatures}>
<li>Responsive Design</li>
@@ -147,7 +154,8 @@ export default function Home() {
Mobile Development
</Title>
<Paragraph className={styles.serviceDescription}>
Native and cross-platform mobile applications that deliver exceptional user experiences across all devices.
Native and cross-platform mobile applications that deliver
exceptional user experiences across all devices.
</Paragraph>
<ul className={styles.serviceFeatures}>
<li>iOS & Android Apps</li>
@@ -167,7 +175,8 @@ export default function Home() {
UI/UX Design
</Title>
<Paragraph className={styles.serviceDescription}>
User-centered design solutions that create intuitive, engaging, and conversion-focused digital experiences.
User-centered design solutions that create intuitive,
engaging, and conversion-focused digital experiences.
</Paragraph>
<ul className={styles.serviceFeatures}>
<li>User Research</li>
@@ -189,7 +198,8 @@ export default function Home() {
Our Impact
</Title>
<Paragraph className={styles.sectionSubtitle}>
Numbers that speak for themselves - our commitment to excellence in every project
Numbers that speak for themselves - our commitment to excellence
in every project
</Paragraph>
</div>

View File

@@ -5,7 +5,7 @@ import {
EyeOutlined,
LinkOutlined,
RocketOutlined,
UserOutlined
UserOutlined,
} from "@ant-design/icons";
import {
Badge,
@@ -403,8 +403,8 @@ export default function ProjectsPage() {
Featured <span className={styles.gradientText}>Work</span>
</Title>
<Paragraph className={styles.projectsSubtitle}>
Discover our latest projects and see how we&apos;ve helped businesses
achieve their digital transformation goals.
Discover our latest projects and see how we&apos;ve helped
businesses achieve their digital transformation goals.
</Paragraph>
</motion.div>