Compare commits
5 Commits
3bc52d60c3
...
8f05d06fb9
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f05d06fb9 | |||
| 9548694f13 | |||
| 2c84cbd1ca | |||
| 323a5665fe | |||
| adcab9eb3c |
@@ -7,6 +7,10 @@ const nextConfig: NextConfig = {
|
||||
protocol: "https",
|
||||
hostname: "techmasters.space",
|
||||
},
|
||||
{
|
||||
protocol: "https",
|
||||
hostname: "tech-masters.guru",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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've helped businesses
|
||||
achieve their digital transformation goals.
|
||||
Discover our latest projects and see how we've helped
|
||||
businesses achieve their digital transformation goals.
|
||||
</Paragraph>
|
||||
</motion.div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user