Initial commit 🌟
This commit is contained in:
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"hoverable"
|
||||||
|
]
|
||||||
|
}
|
||||||
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||||
16
eslint.config.mjs
Normal file
16
eslint.config.mjs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
});
|
||||||
|
|
||||||
|
const eslintConfig = [
|
||||||
|
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||||
|
];
|
||||||
|
|
||||||
|
export default eslintConfig;
|
||||||
7
next.config.ts
Normal file
7
next.config.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
/* config options here */
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
27
package.json
Normal file
27
package.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "antd-demo",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev --turbopack",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start",
|
||||||
|
"lint": "next lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"antd": "^5.24.6",
|
||||||
|
"framer-motion": "^12.6.3",
|
||||||
|
"next": "15.2.4",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"eslint": "^9",
|
||||||
|
"eslint-config-next": "15.2.4",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/file.svg
Normal file
1
public/file.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>
|
||||||
|
After Width: | Height: | Size: 391 B |
1
public/globe.svg
Normal file
1
public/globe.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
1
public/next.svg
Normal file
1
public/next.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/vercel.svg
Normal file
1
public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
|
||||||
|
After Width: | Height: | Size: 128 B |
1
public/window.svg
Normal file
1
public/window.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
|
||||||
|
After Width: | Height: | Size: 385 B |
242
src/app/components/Contact/Contact.tsx
Normal file
242
src/app/components/Contact/Contact.tsx
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
// File: src/components/home/ContactSection.tsx
|
||||||
|
|
||||||
|
import {
|
||||||
|
EnvironmentOutlined,
|
||||||
|
MailOutlined,
|
||||||
|
PhoneOutlined,
|
||||||
|
SendOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { Button, Col, Form, Input, message, Row, Typography } from "antd";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import Image from "next/image";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import styles from "./ContactSection.module.css";
|
||||||
|
|
||||||
|
const { Title, Paragraph, Text } = Typography;
|
||||||
|
const { TextArea } = Input;
|
||||||
|
|
||||||
|
const ContactSection: React.FC = () => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
setSubmitting(true);
|
||||||
|
|
||||||
|
// Simulate API call
|
||||||
|
setTimeout(() => {
|
||||||
|
message.success("Your message has been sent successfully!");
|
||||||
|
form.resetFields();
|
||||||
|
setSubmitting(false);
|
||||||
|
}, 1500);
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemVariants = {
|
||||||
|
hidden: { y: 20, opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.5 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.contactSection}>
|
||||||
|
<div className={styles.backgroundElements}>
|
||||||
|
<div className={styles.glowOrbTop}></div>
|
||||||
|
<div className={styles.glowOrbBottom}></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.container}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className={styles.sectionHeader}
|
||||||
|
>
|
||||||
|
<Title level={2} className={styles.sectionTitle}>
|
||||||
|
Get In Touch
|
||||||
|
</Title>
|
||||||
|
<Paragraph className={styles.sectionSubtitle}>
|
||||||
|
Ready to transform your ideas into reality? Contact us today.
|
||||||
|
</Paragraph>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<Row gutter={[48, 48]} className={styles.contactContent}>
|
||||||
|
<Col xs={24} lg={10}>
|
||||||
|
<motion.div
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className={styles.contactInfo}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
variants={itemVariants}
|
||||||
|
className={styles.contactInfoItem}
|
||||||
|
>
|
||||||
|
<div className={styles.iconWrapper}>
|
||||||
|
<MailOutlined className={styles.contactIcon} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text strong className={styles.contactLabel}>
|
||||||
|
Email
|
||||||
|
</Text>
|
||||||
|
<Text className={styles.contactValue}>
|
||||||
|
info@techmaster.com
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
variants={itemVariants}
|
||||||
|
className={styles.contactInfoItem}
|
||||||
|
>
|
||||||
|
<div className={styles.iconWrapper}>
|
||||||
|
<PhoneOutlined className={styles.contactIcon} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text strong className={styles.contactLabel}>
|
||||||
|
Phone
|
||||||
|
</Text>
|
||||||
|
<Text className={styles.contactValue}>+1 (555) 123-4567</Text>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
variants={itemVariants}
|
||||||
|
className={styles.contactInfoItem}
|
||||||
|
>
|
||||||
|
<div className={styles.iconWrapper}>
|
||||||
|
<EnvironmentOutlined className={styles.contactIcon} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text strong className={styles.contactLabel}>
|
||||||
|
Address
|
||||||
|
</Text>
|
||||||
|
<Text className={styles.contactValue}>
|
||||||
|
1234 Tech Boulevard, Innovation District
|
||||||
|
<br />
|
||||||
|
San Francisco, CA 94105
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
variants={itemVariants}
|
||||||
|
className={styles.mapContainer}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src="/api/placeholder/400/200"
|
||||||
|
alt="Office Location Map"
|
||||||
|
className={styles.mapImage}
|
||||||
|
width={400}
|
||||||
|
height={400}
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col xs={24} lg={14}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, x: 30 }}
|
||||||
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ duration: 0.6, delay: 0.2 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className={styles.contactForm}
|
||||||
|
>
|
||||||
|
<Title level={4} className={styles.formTitle}>
|
||||||
|
Send Us a Message
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
onFinish={handleSubmit}
|
||||||
|
className={styles.form}
|
||||||
|
>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col xs={24} sm={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="name"
|
||||||
|
label="Name"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "Please enter your name" },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input size="large" placeholder="Your name" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col xs={24} sm={12}>
|
||||||
|
<Form.Item
|
||||||
|
name="email"
|
||||||
|
label="Email"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "Please enter your email" },
|
||||||
|
{
|
||||||
|
type: "email",
|
||||||
|
message: "Please enter a valid email",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input size="large" placeholder="Your email" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="subject"
|
||||||
|
label="Subject"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "Please enter a subject" },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input size="large" placeholder="How can we help you?" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="message"
|
||||||
|
label="Message"
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "Please enter your message" },
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
rows={5}
|
||||||
|
placeholder="Tell us about your project..."
|
||||||
|
className={styles.messageInput}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
htmlType="submit"
|
||||||
|
size="large"
|
||||||
|
icon={<SendOutlined />}
|
||||||
|
loading={submitting}
|
||||||
|
className={styles.submitButton}
|
||||||
|
>
|
||||||
|
Send Message
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</motion.div>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactSection;
|
||||||
187
src/app/components/Contact/ContactSection.module.css
Normal file
187
src/app/components/Contact/ContactSection.module.css
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
/* File: src/components/home/ContactSection.module.css */
|
||||||
|
|
||||||
|
.contactSection {
|
||||||
|
padding: 100px 0;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.backgroundElements {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glowOrbTop {
|
||||||
|
position: absolute;
|
||||||
|
width: 400px;
|
||||||
|
height: 400px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(24, 144, 255, 0.15) 0%, rgba(24, 144, 255, 0.05) 50%, rgba(0, 0, 0, 0) 70%);
|
||||||
|
top: -200px;
|
||||||
|
left: -150px;
|
||||||
|
filter: blur(50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glowOrbBottom {
|
||||||
|
position: absolute;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(64, 169, 255, 0.15) 0%, rgba(64, 169, 255, 0.05) 50%, rgba(0, 0, 0, 0) 70%);
|
||||||
|
bottom: -250px;
|
||||||
|
right: -200px;
|
||||||
|
filter: blur(60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionSubtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #666;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactContent {
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 16px;
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.05);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactInfoItem {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconWrapper {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(135deg, #1890ff, #096dd9);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 4px 8px rgba(24, 144, 255, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactIcon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactLabel {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactValue {
|
||||||
|
color: #666;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapContainer {
|
||||||
|
margin-top: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapImage {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactForm {
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 32px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.formTitle {
|
||||||
|
margin-bottom: 24px !important;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageInput {
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitButton {
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 32px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submitButton:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.2);
|
||||||
|
background: linear-gradient(90deg, #40a9ff, #1890ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.contactContent {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contactForm {
|
||||||
|
padding: 24px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.contactSection {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapContainer {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
src/app/components/Header/Header.module.css
Normal file
74
src/app/components/Header/Header.module.css
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
background: rgba(255, 255, 255, 0.95);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
|
||||||
|
padding: 15px 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerContainer {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoText {
|
||||||
|
margin: 0 !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoHighlight {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navContainer {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
border-bottom: none !important;
|
||||||
|
background: transparent !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu li {
|
||||||
|
padding: 0 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaContainer {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ctaButton {
|
||||||
|
/* background: var(--primary) !important; */
|
||||||
|
border: none !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
padding: 0 25px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.navContainer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.headerContainer {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/app/components/Header/Header.tsx
Normal file
73
src/app/components/Header/Header.tsx
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Menu, MenuProps, Typography } from "antd";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import Link from "next/link";
|
||||||
|
import styles from "./Header.module.css";
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
const items: MenuProps['items'] = [
|
||||||
|
{
|
||||||
|
label: <Link href="/">Home</Link>,
|
||||||
|
key: 'home',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <Link href="/services">Services</Link>,
|
||||||
|
key: 'services',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <Link href="/projects">Projects</Link>,
|
||||||
|
key: 'projects',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <Link href="/about">About</Link>,
|
||||||
|
key: 'about',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: <Link href="/contact">Contact</Link>,
|
||||||
|
key: 'contact',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
return (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<div className={styles.headerContainer}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
className={styles.logoContainer}
|
||||||
|
>
|
||||||
|
<Link href="/">
|
||||||
|
<Title level={3} className={styles.logoText}>
|
||||||
|
Tech Master
|
||||||
|
</Title>
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<nav className={styles.navContainer}>
|
||||||
|
<Menu
|
||||||
|
mode="horizontal"
|
||||||
|
items={items}
|
||||||
|
className={styles.menu}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* <Space className={styles.ctaContainer}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
shape="round"
|
||||||
|
size="large"
|
||||||
|
className={styles.ctaButton}
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
</Button>
|
||||||
|
</Space> */}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
109
src/app/components/Hero/HeroSection.module.css
Normal file
109
src/app/components/Hero/HeroSection.module.css
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
.heroSection {
|
||||||
|
position: relative;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: linear-gradient(135deg, #0f0525 0%, #2a0b45 100%);
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heroContent {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background: rgba(110, 72, 170, 0.2);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid rgba(157, 80, 187, 0.5);
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: white !important;
|
||||||
|
font-size: 3.2rem !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
margin-bottom: 24px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
background: linear-gradient(90deg, #6e48aa, #9d50bb);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
max-width: 700px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonGroup {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolioButton {
|
||||||
|
background: linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%) !important;
|
||||||
|
border: none !important;
|
||||||
|
font-weight: 500 !important;
|
||||||
|
height: 50px !important;
|
||||||
|
padding: 0 30px !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectButton {
|
||||||
|
background: transparent !important;
|
||||||
|
border: 2px solid #9d50bb !important;
|
||||||
|
color: white !important;
|
||||||
|
height: 50px !important;
|
||||||
|
padding: 0 30px !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
transition: all 0.3s ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectButton:hover {
|
||||||
|
background: rgba(157, 80, 187, 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.title {
|
||||||
|
font-size: 2.2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonGroup {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.portfolioButton,
|
||||||
|
.projectButton {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 280px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
60
src/app/components/Hero/HeroSection.tsx
Normal file
60
src/app/components/Hero/HeroSection.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button, Typography } from "antd";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import styles from "./HeroSection.module.css";
|
||||||
|
import ParticleBackground from "./ParticleBackground";
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
const HeroSection = () => {
|
||||||
|
return (
|
||||||
|
<section className={styles.heroSection}>
|
||||||
|
<ParticleBackground />
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.8 }}
|
||||||
|
className={styles.heroContent}
|
||||||
|
>
|
||||||
|
<div className={styles.badge}>
|
||||||
|
<span>AWARD WINNING IT SOLUTIONS</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Title className={styles.title}>
|
||||||
|
Pioneering{" "}
|
||||||
|
<span className={styles.highlight}>Digital Transformation</span>
|
||||||
|
<br />
|
||||||
|
From Zero To Hero
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Text className={styles.description}>
|
||||||
|
We are an award-winning Dubai based technology agency, focused on
|
||||||
|
creating cutting-edge digital experiences for ambitious businesses and
|
||||||
|
enterprises.
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<div className={styles.buttonGroup}>
|
||||||
|
<motion.div whileHover={{ scale: 1.05 }}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
className={styles.portfolioButton}
|
||||||
|
>
|
||||||
|
VIEW OUR PORTFOLIO
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div whileHover={{ scale: 1.05 }}>
|
||||||
|
<Button size="large" className={styles.projectButton}>
|
||||||
|
START A PROJECT
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeroSection;
|
||||||
137
src/app/components/Hero/ParticleBackground.tsx
Normal file
137
src/app/components/Hero/ParticleBackground.tsx
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
particlesJS: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ParticleBackground = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window !== "undefined" && window.particlesJS) {
|
||||||
|
window.particlesJS("particles-js", {
|
||||||
|
particles: {
|
||||||
|
number: {
|
||||||
|
value: 80,
|
||||||
|
density: {
|
||||||
|
enable: true,
|
||||||
|
value_area: 800,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
value: "#ffffff",
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
type: "circle",
|
||||||
|
stroke: {
|
||||||
|
width: 0,
|
||||||
|
color: "#000000",
|
||||||
|
},
|
||||||
|
polygon: {
|
||||||
|
nb_sides: 5,
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
src: "img/github.svg",
|
||||||
|
width: 100,
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
opacity: {
|
||||||
|
value: 0.4,
|
||||||
|
random: true,
|
||||||
|
anim: {
|
||||||
|
enable: false,
|
||||||
|
speed: 1,
|
||||||
|
opacity_min: 0.1,
|
||||||
|
sync: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
value: 7,
|
||||||
|
random: true,
|
||||||
|
anim: {
|
||||||
|
enable: false,
|
||||||
|
speed: 40,
|
||||||
|
size_min: 0.1,
|
||||||
|
sync: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
line_linked: {
|
||||||
|
enable: true,
|
||||||
|
distance: 150,
|
||||||
|
color: "#ffffff",
|
||||||
|
opacity: 0.3,
|
||||||
|
width: 1,
|
||||||
|
},
|
||||||
|
move: {
|
||||||
|
enable: true,
|
||||||
|
speed: 2,
|
||||||
|
direction: "none",
|
||||||
|
random: true,
|
||||||
|
straight: false,
|
||||||
|
out_mode: "bounce",
|
||||||
|
bounce: false,
|
||||||
|
attract: {
|
||||||
|
enable: false,
|
||||||
|
rotateX: 600,
|
||||||
|
rotateY: 1200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interactivity: {
|
||||||
|
detect_on: "canvas",
|
||||||
|
events: {
|
||||||
|
onhover: {
|
||||||
|
enable: true,
|
||||||
|
mode: "grab",
|
||||||
|
},
|
||||||
|
onclick: {
|
||||||
|
enable: true,
|
||||||
|
mode: "push",
|
||||||
|
},
|
||||||
|
resize: true,
|
||||||
|
},
|
||||||
|
modes: {
|
||||||
|
grab: {
|
||||||
|
distance: 312,
|
||||||
|
line_linked: {
|
||||||
|
opacity: 0.7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bubble: {
|
||||||
|
distance: 400,
|
||||||
|
size: 40,
|
||||||
|
duration: 2,
|
||||||
|
opacity: 8,
|
||||||
|
speed: 3,
|
||||||
|
},
|
||||||
|
repulse: {
|
||||||
|
distance: 200,
|
||||||
|
duration: 0.4,
|
||||||
|
},
|
||||||
|
push: {
|
||||||
|
particles_nb: 4,
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
particles_nb: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
retina_detect: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div id="particles-js"></div>{" "}
|
||||||
|
<div className="count-particles">
|
||||||
|
<span className="js-count-particles">--</span> particles{" "}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ParticleBackground;
|
||||||
143
src/app/components/Preferences/Preferences.tsx
Normal file
143
src/app/components/Preferences/Preferences.tsx
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// File: src/components/PreferencesSelector.tsx
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button, Card, Checkbox, Flex, message, Space, Typography } from "antd";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import styles from "./PreferencesSelector.module.css";
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
type PreferenceOption = {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const preferenceOptions: PreferenceOption[] = [
|
||||||
|
{
|
||||||
|
id: "branding",
|
||||||
|
label: "Branding & Identity",
|
||||||
|
description: "Visual identity, logo design, brand guidelines",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ecommerce",
|
||||||
|
label: "E-commerce Solutions",
|
||||||
|
description: "Online stores, payment gateways, inventory management",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "seo",
|
||||||
|
label: "SEO & Digital Marketing",
|
||||||
|
description: "Search optimization, content strategy, analytics",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "mobile",
|
||||||
|
label: "Mobile Development",
|
||||||
|
description: "iOS/Android apps, cross-platform solutions",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "cloud",
|
||||||
|
label: "Cloud Solutions",
|
||||||
|
description: "Cloud infrastructure, migrations, DevOps",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "ai",
|
||||||
|
label: "AI & Machine Learning",
|
||||||
|
description: "Data analytics, predictive models, automation",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface PreferencesSelectorProps {
|
||||||
|
onComplete: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PreferencesSelector: React.FC<PreferencesSelectorProps> = ({
|
||||||
|
onComplete,
|
||||||
|
}) => {
|
||||||
|
const [selectedPreferences, setSelectedPreferences] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const handleTogglePreference = (id: string) => {
|
||||||
|
setSelectedPreferences((prev) =>
|
||||||
|
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
if (selectedPreferences.length === 0) {
|
||||||
|
message.warning("Please select at least one preference");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store preferences in local storage
|
||||||
|
localStorage.setItem(
|
||||||
|
"userPreferences",
|
||||||
|
JSON.stringify(selectedPreferences)
|
||||||
|
);
|
||||||
|
message.success("Preferences saved successfully!");
|
||||||
|
onComplete();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className={styles.container}
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<Card className={styles.card}>
|
||||||
|
<Title level={2}>Welcome to Tech Master</Title>
|
||||||
|
<Text>
|
||||||
|
Help us personalize your experience by selecting your interests:
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Space
|
||||||
|
direction="vertical"
|
||||||
|
size="large"
|
||||||
|
className={styles.optionsContainer}
|
||||||
|
>
|
||||||
|
{preferenceOptions.map((option) => (
|
||||||
|
<motion.div
|
||||||
|
key={option.id}
|
||||||
|
whileHover={{ scale: 1.02 }}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
className={`${styles.optionCard} ${
|
||||||
|
selectedPreferences.includes(option.id) ? styles.selected : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => handleTogglePreference(option.id)}
|
||||||
|
hoverable
|
||||||
|
>
|
||||||
|
<Flex align="center" gap="small">
|
||||||
|
<Checkbox
|
||||||
|
checked={selectedPreferences.includes(option.id)}
|
||||||
|
onChange={() => handleTogglePreference(option.id)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Text strong>{option.label}</Text>
|
||||||
|
<Text type="secondary" style={{ display: "block" }}>
|
||||||
|
{option.description}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Flex justify="center" className={styles.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleSubmit}
|
||||||
|
className={styles.continueButton}
|
||||||
|
>
|
||||||
|
Continue to Site
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PreferencesSelector;
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/* File: src/components/PreferencesSelector.module.css */
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #42475c 0%, #20222f 100%);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.optionsContainer {
|
||||||
|
margin: 2rem 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optionCard {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
border-color: #1890ff;
|
||||||
|
background-color: rgba(24, 144, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.continueButton {
|
||||||
|
min-width: 200px;
|
||||||
|
height: 48px;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.continueButton:hover {
|
||||||
|
background: linear-gradient(90deg, #40a9ff, #1890ff);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
265
src/app/components/ProjectsShowcase/ProjectsShowcase.tsx
Normal file
265
src/app/components/ProjectsShowcase/ProjectsShowcase.tsx
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
// File: src/components/home/ProjectsShowcase.tsx
|
||||||
|
|
||||||
|
import { EyeOutlined, LeftOutlined, RightOutlined } from "@ant-design/icons";
|
||||||
|
import { Badge, Button, Card, Col, Row, Tag, Typography } from "antd";
|
||||||
|
import { motion, useAnimation } from "framer-motion";
|
||||||
|
import Image from "next/image";
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import styles from "./ProjectsShowcaseSelector.module.css";
|
||||||
|
|
||||||
|
const { Title, Paragraph, Text } = Typography;
|
||||||
|
|
||||||
|
interface Project {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
imageUrl: string;
|
||||||
|
category: string;
|
||||||
|
technologies: string[];
|
||||||
|
featured: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sample project data
|
||||||
|
const projectsData: Project[] = [
|
||||||
|
{
|
||||||
|
id: "p1",
|
||||||
|
title: "FinTech Dashboard",
|
||||||
|
description:
|
||||||
|
"An AI-powered financial analytics platform with real-time data visualization and predictive insights.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "Web Application",
|
||||||
|
technologies: ["React", "Node.js", "TensorFlow", "AWS"],
|
||||||
|
featured: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p2",
|
||||||
|
title: "Healthcare Management System",
|
||||||
|
description:
|
||||||
|
"Comprehensive solution for managing patient records, appointments, and medical data with advanced security features.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "Enterprise Software",
|
||||||
|
technologies: ["Angular", ".NET Core", "SQL Server", "Azure"],
|
||||||
|
featured: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p3",
|
||||||
|
title: "E-commerce Marketplace",
|
||||||
|
description:
|
||||||
|
"Feature-rich online marketplace connecting vendors and customers with integrated payment processing and inventory management.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "E-commerce",
|
||||||
|
technologies: ["Next.js", "Stripe", "MongoDB", "GraphQL"],
|
||||||
|
featured: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p4",
|
||||||
|
title: "Smart City IoT Platform",
|
||||||
|
description:
|
||||||
|
"IoT ecosystem for urban monitoring and management, featuring real-time data collection and analytics.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "IoT",
|
||||||
|
technologies: ["Python", "MQTT", "Kubernetes", "TensorFlow"],
|
||||||
|
featured: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p5",
|
||||||
|
title: "Logistics Tracking System",
|
||||||
|
description:
|
||||||
|
"Real-time fleet management and package tracking solution with route optimization algorithms.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "Mobile & Web",
|
||||||
|
technologies: ["React Native", "Node.js", "PostgreSQL", "Google Maps API"],
|
||||||
|
featured: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p6",
|
||||||
|
title: "Virtual Learning Environment",
|
||||||
|
description:
|
||||||
|
"Interactive educational platform with personalized learning paths, video conferencing, and progress tracking.",
|
||||||
|
imageUrl: "/api/placeholder/600/400",
|
||||||
|
category: "Education",
|
||||||
|
technologies: ["Vue.js", "Django", "WebRTC", "Docker"],
|
||||||
|
featured: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ProjectsShowcase: React.FC = () => {
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const [visibleProjects, setVisibleProjects] = useState<Project[]>([]);
|
||||||
|
const controls = useAnimation();
|
||||||
|
|
||||||
|
// Determine number of projects to show based on screen width
|
||||||
|
const [projectsPerView, setProjectsPerView] = useState(3);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleResize = () => {
|
||||||
|
if (window.innerWidth < 768) {
|
||||||
|
setProjectsPerView(1);
|
||||||
|
} else if (window.innerWidth < 992) {
|
||||||
|
setProjectsPerView(2);
|
||||||
|
} else {
|
||||||
|
setProjectsPerView(3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set initial state
|
||||||
|
handleResize();
|
||||||
|
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Update visible projects whenever currentIndex or projectsPerView changes
|
||||||
|
setVisibleProjects(
|
||||||
|
projectsData.slice(currentIndex, currentIndex + projectsPerView)
|
||||||
|
);
|
||||||
|
}, [currentIndex, projectsPerView]);
|
||||||
|
|
||||||
|
const handleNext = async () => {
|
||||||
|
if (currentIndex + projectsPerView < projectsData.length) {
|
||||||
|
await controls.start({
|
||||||
|
x: "-100%",
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
});
|
||||||
|
setCurrentIndex((prev) => prev + 1);
|
||||||
|
controls.start({
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePrev = async () => {
|
||||||
|
if (currentIndex > 0) {
|
||||||
|
await controls.start({
|
||||||
|
x: "100%",
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
});
|
||||||
|
setCurrentIndex((prev) => prev - 1);
|
||||||
|
controls.start({
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.projectsSection}>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className={styles.sectionHeader}
|
||||||
|
>
|
||||||
|
<Title level={2} className={styles.sectionTitle}>
|
||||||
|
Our Work
|
||||||
|
</Title>
|
||||||
|
<Paragraph className={styles.sectionSubtitle}>
|
||||||
|
Transforming ideas into powerful digital solutions
|
||||||
|
</Paragraph>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className={styles.showcaseContainer}>
|
||||||
|
<motion.div
|
||||||
|
className={styles.projectsGrid}
|
||||||
|
animate={controls}
|
||||||
|
initial={{ opacity: 1, x: 0 }}
|
||||||
|
>
|
||||||
|
<Row gutter={[24, 24]}>
|
||||||
|
{visibleProjects.map((project) => (
|
||||||
|
<Col xs={24} md={24 / projectsPerView} key={project.id}>
|
||||||
|
<motion.div
|
||||||
|
whileHover={{ y: -10 }}
|
||||||
|
transition={{ type: "spring", stiffness: 300 }}
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
hoverable
|
||||||
|
cover={
|
||||||
|
<div className={styles.projectImageContainer}>
|
||||||
|
<Image
|
||||||
|
alt={project.title}
|
||||||
|
src={project.imageUrl}
|
||||||
|
className={styles.projectImage}
|
||||||
|
width={400}
|
||||||
|
height={400}
|
||||||
|
/>
|
||||||
|
<div className={styles.projectOverlay}>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
shape="circle"
|
||||||
|
icon={<EyeOutlined />}
|
||||||
|
className={styles.viewButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{project.featured && (
|
||||||
|
<Badge.Ribbon
|
||||||
|
text="Featured"
|
||||||
|
className={styles.featuredBadge}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
className={styles.projectCard}
|
||||||
|
>
|
||||||
|
<div className={styles.categoryTag}>
|
||||||
|
<Tag color="blue">{project.category}</Tag>
|
||||||
|
</div>
|
||||||
|
<Card.Meta
|
||||||
|
title={project.title}
|
||||||
|
description={project.description}
|
||||||
|
/>
|
||||||
|
<div className={styles.technologiesList}>
|
||||||
|
{project.technologies.map((tech) => (
|
||||||
|
<Tag key={tech} className={styles.techTag}>
|
||||||
|
{tech}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<div className={styles.navigationControls}>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
icon={<LeftOutlined />}
|
||||||
|
onClick={handlePrev}
|
||||||
|
disabled={currentIndex === 0}
|
||||||
|
className={styles.navButton}
|
||||||
|
/>
|
||||||
|
<Text className={styles.pageIndicator}>
|
||||||
|
{currentIndex + 1}-
|
||||||
|
{Math.min(currentIndex + projectsPerView, projectsData.length)} of{" "}
|
||||||
|
{projectsData.length}
|
||||||
|
</Text>
|
||||||
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
icon={<RightOutlined />}
|
||||||
|
onClick={handleNext}
|
||||||
|
disabled={currentIndex + projectsPerView >= projectsData.length}
|
||||||
|
className={styles.navButton}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.viewAllContainer}>
|
||||||
|
<Button type="primary" size="large" className={styles.viewAllButton}>
|
||||||
|
View All Projects
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectsShowcase;
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/* File: src/components/home/ProjectsShowcase.module.css */
|
||||||
|
|
||||||
|
.projectsSection {
|
||||||
|
padding: 100px 0;
|
||||||
|
background-color: #fff;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionSubtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #666;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showcaseContainer {
|
||||||
|
position: relative;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectsGrid {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectCard {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectImageContainer {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectImage {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectCard:hover .projectImage {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectOverlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectCard:hover .projectOverlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewButton {
|
||||||
|
background: #fff;
|
||||||
|
color: #1890ff;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewButton:hover {
|
||||||
|
background: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.categoryTag {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologiesList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.techTag {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.featuredBadge {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigationControls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navButton {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pageIndicator {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllButton {
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 32px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllButton:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.2);
|
||||||
|
background: linear-gradient(90deg, #40a9ff, #1890ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.projectsSection {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectImageContainer {
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
|
}
|
||||||
185
src/app/components/Services/Services.tsx
Normal file
185
src/app/components/Services/Services.tsx
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
// File: src/components/home/ServicesSection.tsx
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CloudOutlined,
|
||||||
|
CodeOutlined,
|
||||||
|
GlobalOutlined,
|
||||||
|
LineChartOutlined,
|
||||||
|
MobileOutlined,
|
||||||
|
RobotOutlined,
|
||||||
|
RocketOutlined,
|
||||||
|
ShoppingOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import { Button, Card, Col, Row, Typography } from 'antd';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import styles from './ServicesSelector.module.css';
|
||||||
|
|
||||||
|
const { Title, Paragraph } = Typography;
|
||||||
|
|
||||||
|
interface ServicesSectionProps {
|
||||||
|
userPreferences: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Service {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
preferenceMatches: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const allServices: Service[] = [
|
||||||
|
{
|
||||||
|
id: 'webdev',
|
||||||
|
title: 'Web Development',
|
||||||
|
description: 'Custom web applications with cutting-edge technologies and responsive design.',
|
||||||
|
icon: <CodeOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['ecommerce', 'cloud', 'seo']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'branding',
|
||||||
|
title: 'Branding & Identity',
|
||||||
|
description: 'Complete brand identity packages including logos, guidelines, and visual systems.',
|
||||||
|
icon: <RocketOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['branding', 'seo']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ecom',
|
||||||
|
title: 'E-commerce Solutions',
|
||||||
|
description: 'Full-stack e-commerce platforms with secure payment integration and inventory management.',
|
||||||
|
icon: <ShoppingOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['ecommerce', 'branding']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'seo',
|
||||||
|
title: 'SEO & Marketing',
|
||||||
|
description: 'Data-driven digital marketing strategies to improve visibility and drive conversions.',
|
||||||
|
icon: <LineChartOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['seo', 'branding']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'mobile',
|
||||||
|
title: 'Mobile Development',
|
||||||
|
description: 'Native and cross-platform mobile applications for iOS and Android.',
|
||||||
|
icon: <MobileOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['mobile', 'ai']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'cloud',
|
||||||
|
title: 'Cloud Solutions',
|
||||||
|
description: 'Scalable cloud infrastructures, migrations, and DevOps automation.',
|
||||||
|
icon: <CloudOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['cloud', 'ai']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ai',
|
||||||
|
title: 'AI & Machine Learning',
|
||||||
|
description: 'Custom AI solutions for automation, prediction, and data analysis.',
|
||||||
|
icon: <RobotOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['ai', 'cloud']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'global',
|
||||||
|
title: 'Global IT Consulting',
|
||||||
|
description: 'Strategic technology consulting to drive digital transformation and innovation.',
|
||||||
|
icon: <GlobalOutlined className={styles.serviceIcon} />,
|
||||||
|
preferenceMatches: ['cloud', 'branding', 'seo']
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const ServicesSection: React.FC<ServicesSectionProps> = ({ userPreferences }) => {
|
||||||
|
const [services, setServices] = useState<Service[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Sort services based on user preferences
|
||||||
|
if (userPreferences.length > 0) {
|
||||||
|
const sortedServices = [...allServices].sort((a, b) => {
|
||||||
|
const aMatches = a.preferenceMatches.filter(pref => userPreferences.includes(pref)).length;
|
||||||
|
const bMatches = b.preferenceMatches.filter(pref => userPreferences.includes(pref)).length;
|
||||||
|
return bMatches - aMatches;
|
||||||
|
});
|
||||||
|
setServices(sortedServices);
|
||||||
|
} else {
|
||||||
|
// If no preferences, show all services in default order
|
||||||
|
setServices(allServices);
|
||||||
|
}
|
||||||
|
}, [userPreferences]);
|
||||||
|
|
||||||
|
const containerVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const itemVariants = {
|
||||||
|
hidden: { y: 50, opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.5 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.servicesSection}>
|
||||||
|
<div className={styles.sectionBackground}>
|
||||||
|
<div className={styles.glowOrb}></div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 30 }}
|
||||||
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.6 }}
|
||||||
|
viewport={{ once: true }}
|
||||||
|
className={styles.sectionHeader}
|
||||||
|
>
|
||||||
|
<Title level={2} className={styles.sectionTitle}>Our Services</Title>
|
||||||
|
<Paragraph className={styles.sectionSubtitle}>
|
||||||
|
Innovative technology solutions customized for your business needs
|
||||||
|
</Paragraph>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
variants={containerVariants}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView="visible"
|
||||||
|
viewport={{ once: true }}
|
||||||
|
>
|
||||||
|
<Row gutter={[24, 24]}>
|
||||||
|
{services.map((service) => (
|
||||||
|
<Col xs={24} sm={12} lg={8} xl={6} key={service.id}>
|
||||||
|
<motion.div variants={itemVariants}>
|
||||||
|
<Card
|
||||||
|
hoverable
|
||||||
|
className={styles.serviceCard}
|
||||||
|
cover={
|
||||||
|
<div className={styles.serviceIconWrapper}>
|
||||||
|
{service.icon}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Card.Meta
|
||||||
|
title={service.title}
|
||||||
|
description={service.description}
|
||||||
|
/>
|
||||||
|
<Button type="link" className={styles.learnMoreBtn}>
|
||||||
|
Learn more
|
||||||
|
</Button>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServicesSection;
|
||||||
102
src/app/components/Services/ServicesSelector.module.css
Normal file
102
src/app/components/Services/ServicesSelector.module.css
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
/* File: src/components/home/ServicesSection.module.css */
|
||||||
|
|
||||||
|
.servicesSection {
|
||||||
|
position: relative;
|
||||||
|
padding: 100px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionBackground {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glowOrb {
|
||||||
|
position: absolute;
|
||||||
|
width: 500px;
|
||||||
|
height: 500px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: radial-gradient(circle, rgba(64, 169, 255, 0.2) 0%, rgba(24, 144, 255, 0.1) 50%, rgba(0, 0, 0, 0) 70%);
|
||||||
|
top: -150px;
|
||||||
|
right: -100px;
|
||||||
|
filter: blur(50px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionSubtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #666;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceCard {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceCard:hover {
|
||||||
|
transform: translateY(-8px);
|
||||||
|
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceIconWrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 120px;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #e4e7eb 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceIcon {
|
||||||
|
font-size: 3rem;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learnMoreBtn {
|
||||||
|
display: block;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.servicesSection {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
149
src/app/components/Testimonials/Testimonials.tsx
Normal file
149
src/app/components/Testimonials/Testimonials.tsx
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// File: src/components/TestimonialsSection/TestimonialsSection.tsx
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Avatar, Card, Carousel, Flex, Tag, Typography } from "antd";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import styles from "./TestimonialsSection.module.css";
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
type Testimonial = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
company: string;
|
||||||
|
avatar: string;
|
||||||
|
content: string;
|
||||||
|
rating: number;
|
||||||
|
project: string;
|
||||||
|
technologies: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const testimonials: Testimonial[] = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
name: "Ahmed Al-Maktoum",
|
||||||
|
role: "CTO",
|
||||||
|
company: "Dubai Tech Innovations",
|
||||||
|
avatar: "https://randomuser.me/api/portraits/men/32.jpg",
|
||||||
|
content:
|
||||||
|
"Tech Master transformed our e-commerce platform with their cutting-edge solutions. The team's expertise in AI integration helped us increase conversions by 40%.",
|
||||||
|
rating: 5,
|
||||||
|
project: "E-commerce AI Optimization",
|
||||||
|
technologies: ["AI", "React", "Node.js"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
name: "Sarah Johnson",
|
||||||
|
role: "Digital Director",
|
||||||
|
company: "Stellar Communications",
|
||||||
|
avatar: "https://randomuser.me/api/portraits/women/44.jpg",
|
||||||
|
content:
|
||||||
|
"Their cloud migration strategy saved us thousands in operational costs. The most reliable IT partner we've worked with in the region.",
|
||||||
|
rating: 5,
|
||||||
|
project: "Cloud Infrastructure Migration",
|
||||||
|
technologies: ["AWS", "Terraform", "Kubernetes"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
name: "Raj Patel",
|
||||||
|
role: "Founder",
|
||||||
|
company: "Neuralink Dubai",
|
||||||
|
avatar: "https://randomuser.me/api/portraits/men/67.jpg",
|
||||||
|
content:
|
||||||
|
"The blockchain solution they developed for our supply chain brought unprecedented transparency to our operations. Exceptional work!",
|
||||||
|
rating: 4,
|
||||||
|
project: "Blockchain Supply Chain",
|
||||||
|
technologies: ["Ethereum", "Solidity", "IPFS"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const TestimonialsSection = () => {
|
||||||
|
const renderStars = (rating: number) => {
|
||||||
|
return (
|
||||||
|
<Flex gap="small" style={{ marginTop: 8 }}>
|
||||||
|
{[...Array(5)].map((_, i) => (
|
||||||
|
<span
|
||||||
|
key={i}
|
||||||
|
className={i < rating ? styles.filledStar : styles.emptyStar}
|
||||||
|
>
|
||||||
|
★
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.testimonialsSection}>
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.sectionHeader}>
|
||||||
|
<Title level={2} className={styles.sectionTitle}>
|
||||||
|
Client Testimonials
|
||||||
|
</Title>
|
||||||
|
<Text className={styles.sectionSubtitle}>
|
||||||
|
Hear what industry leaders say about our transformative solutions
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.showcaseContainer}>
|
||||||
|
<Carousel
|
||||||
|
autoplay
|
||||||
|
dots={{ className: styles.carouselDots }}
|
||||||
|
className={styles.testimonialsCarousel}
|
||||||
|
>
|
||||||
|
{testimonials.map((testimonial) => (
|
||||||
|
<motion.div
|
||||||
|
key={testimonial.id}
|
||||||
|
className={styles.testimonialItem}
|
||||||
|
whileHover={{ y: -5 }}
|
||||||
|
>
|
||||||
|
<Card className={styles.testimonialCard}>
|
||||||
|
<Flex vertical gap={24}>
|
||||||
|
<Flex align="center" gap={16}>
|
||||||
|
<Avatar
|
||||||
|
src={testimonial.avatar}
|
||||||
|
size={64}
|
||||||
|
className={styles.avatar}
|
||||||
|
/>
|
||||||
|
<Flex vertical>
|
||||||
|
<Text strong className={styles.clientName}>
|
||||||
|
{testimonial.name}
|
||||||
|
</Text>
|
||||||
|
<Text type="secondary" className={styles.clientTitle}>
|
||||||
|
{testimonial.role}, {testimonial.company}
|
||||||
|
</Text>
|
||||||
|
{renderStars(testimonial.rating)}
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<Text className={styles.testimonialContent}>
|
||||||
|
{testimonial.content}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<div className={styles.projectInfo}>
|
||||||
|
<Text strong className={styles.projectLabel}>
|
||||||
|
Project:
|
||||||
|
</Text>
|
||||||
|
<Text>{testimonial.project}</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.technologiesList}>
|
||||||
|
{testimonial.technologies.map((tech) => (
|
||||||
|
<Tag key={tech} className={styles.techTag}>
|
||||||
|
{tech}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TestimonialsSection;
|
||||||
164
src/app/components/Testimonials/TestimonialsSection.module.css
Normal file
164
src/app/components/Testimonials/TestimonialsSection.module.css
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/* File: src/components/TestimonialsSection/TestimonialsSection.module.css */
|
||||||
|
.testimonialsSection {
|
||||||
|
padding: 100px 0;
|
||||||
|
background-color: #fff;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 16px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionHeader {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2.5rem !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionSubtitle {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #666;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showcaseContainer {
|
||||||
|
position: relative;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testimonialsCarousel {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testimonialItem {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testimonialCard {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
|
border: none;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientName {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientTitle {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testimonialContent {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #555;
|
||||||
|
line-height: 1.6;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectInfo {
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 16px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.projectLabel {
|
||||||
|
background: var(--primary);
|
||||||
|
-webkit-background-clip: text; /* For Safari */
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent !important; /* Hide original text color */
|
||||||
|
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.technologiesList {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.techTag {
|
||||||
|
margin: 0;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filledStar {
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emptyStar {
|
||||||
|
color: #d9d9d9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carouselDots li button {
|
||||||
|
background: #d9d9d9 !important;
|
||||||
|
width: 10px !important;
|
||||||
|
height: 10px !important;
|
||||||
|
border-radius: 50% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.carouselDots li.slick-active button {
|
||||||
|
background: #1890ff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllButton {
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 32px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-weight: 500;
|
||||||
|
background: linear-gradient(90deg, #1890ff, #096dd9);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.15);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewAllButton:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 16px rgba(24, 144, 255, 0.2);
|
||||||
|
background: linear-gradient(90deg, #40a9ff, #1890ff);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.testimonialsSection {
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testimonialCard {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/app/favicon.ico
Normal file
BIN
src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
86
src/app/globals.css
Normal file
86
src/app/globals.css
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
:root {
|
||||||
|
--background: #ffffff;
|
||||||
|
--foreground: #171717;
|
||||||
|
--primary: linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--background: #0a0a0a;
|
||||||
|
--foreground: #ededed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
max-width: 100vw;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--foreground);
|
||||||
|
background: var(--background);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
html {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
} /* ---- particles.js container ---- */
|
||||||
|
#particles-js {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.count-particles {
|
||||||
|
background: #000022;
|
||||||
|
position: absolute;
|
||||||
|
top: 48px;
|
||||||
|
left: 0;
|
||||||
|
width: 80px;
|
||||||
|
color: #13e8e9;
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-align: left;
|
||||||
|
text-indent: 4px;
|
||||||
|
line-height: 14px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.js-count-particles {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
#stats,
|
||||||
|
.count-particles {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
#stats {
|
||||||
|
border-radius: 3px 3px 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.count-particles {
|
||||||
|
border-radius: 0 0 3px 3px;
|
||||||
|
}
|
||||||
49
src/app/layout.tsx
Normal file
49
src/app/layout.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// src/app/layout.tsx
|
||||||
|
import { ConfigProvider } from "antd";
|
||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
|
import Header from "./components/Header/Header";
|
||||||
|
import "./globals.css";
|
||||||
|
import { themeConfig } from "./theme/themeConfig";
|
||||||
|
|
||||||
|
// Modern font (Inter + Orbitron backup)
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
const orbitron = {
|
||||||
|
className: "font-orbitron",
|
||||||
|
style:
|
||||||
|
"@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Tech Master | Dubai To Stars",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
{/* ThreeJS CDN */}
|
||||||
|
<script
|
||||||
|
src="http://cdn.jsdelivr.net/particles.js/2.0.0/particles.min.js"
|
||||||
|
async
|
||||||
|
></script>
|
||||||
|
<script
|
||||||
|
src="http://threejs.org/examples/js/libs/stats.min.js"
|
||||||
|
async
|
||||||
|
></script>
|
||||||
|
{/* Orbitron Font */}
|
||||||
|
<style>{orbitron.style}</style>
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
className={`${inter.className} bg-gradient-to-br from-[#0F0525] to-[#2A0B45]`}
|
||||||
|
>
|
||||||
|
<Header />
|
||||||
|
<ConfigProvider theme={themeConfig}>{children}</ConfigProvider>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
42
src/app/page.module.css
Normal file
42
src/app/page.module.css
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/* File: src/styles/Home.module.css */
|
||||||
|
|
||||||
|
.main {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loaderContainer {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background: linear-gradient(135deg, #42475C 0%, #20222F 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loaderText {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: white;
|
||||||
|
opacity: 0.8;
|
||||||
|
animation: pulse 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { opacity: 0.6; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
100% { opacity: 0.6; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding: 80px 16px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.section {
|
||||||
|
padding: 60px 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/app/page.tsx
Normal file
72
src/app/page.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// File: src/pages/index.tsx
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Spin, Typography } from "antd";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import ContactSection from "./components/Contact/Contact";
|
||||||
|
import HeroSection from "./components/Hero/HeroSection";
|
||||||
|
import PreferencesSelector from "./components/Preferences/Preferences";
|
||||||
|
import ProjectsShowcase from "./components/ProjectsShowcase/ProjectsShowcase";
|
||||||
|
import ServicesSection from "./components/Services/Services";
|
||||||
|
import TestimonialsSection from "./components/Testimonials/Testimonials";
|
||||||
|
import styles from "./page.module.css";
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [showPreferences, setShowPreferences] = useState(true);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [userPreferences, setUserPreferences] = useState<string[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if preferences already exist in localStorage
|
||||||
|
const storedPreferences = localStorage.getItem("userPreferences");
|
||||||
|
|
||||||
|
if (storedPreferences) {
|
||||||
|
setUserPreferences(JSON.parse(storedPreferences));
|
||||||
|
setShowPreferences(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate loading of resources
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
setLoading(false);
|
||||||
|
}, 1500);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handlePreferencesComplete = () => {
|
||||||
|
const storedPreferences = localStorage.getItem("userPreferences");
|
||||||
|
if (storedPreferences) {
|
||||||
|
setUserPreferences(JSON.parse(storedPreferences));
|
||||||
|
}
|
||||||
|
setShowPreferences(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className={styles.loaderContainer}>
|
||||||
|
<Spin size="large" />
|
||||||
|
<Title level={4} className={styles.loaderText}>
|
||||||
|
Preparing Tech Master Experience...
|
||||||
|
</Title>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showPreferences && (
|
||||||
|
<PreferencesSelector onComplete={handlePreferencesComplete} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<main className={styles.main}>
|
||||||
|
<HeroSection />
|
||||||
|
<ServicesSection userPreferences={userPreferences} />
|
||||||
|
<ProjectsShowcase />
|
||||||
|
<TestimonialsSection />
|
||||||
|
<ContactSection />
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
src/app/theme/themeConfig.ts
Normal file
45
src/app/theme/themeConfig.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// src/app/theme/themeConfig.ts
|
||||||
|
import type { ThemeConfig } from "antd";
|
||||||
|
|
||||||
|
// Cosmic Gradient Palette (Bright/Futuristic)
|
||||||
|
export const gradientColors = {
|
||||||
|
primary: "linear-gradient(135deg, #6e48aa 0%, #9d50bb 100%)",
|
||||||
|
secondary: "linear-gradient(135deg, #00C1D4 0%, #FF5F6D 100%)",
|
||||||
|
darkSpace: "linear-gradient(to right, #0F0525, #2A0B45)",
|
||||||
|
};
|
||||||
|
|
||||||
|
// AntD Theme Configuration
|
||||||
|
export const themeConfig: ThemeConfig = {
|
||||||
|
token: {
|
||||||
|
colorPrimary: "#6e48aa", // Cosmic purple
|
||||||
|
colorLink: "#9d50bb", // Nebula pink
|
||||||
|
fontFamily: "Inter, Orbitron, sans-serif",
|
||||||
|
borderRadius: 8,
|
||||||
|
// Futuristic button styles
|
||||||
|
controlHeight: 40,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Button: {
|
||||||
|
colorPrimary: gradientColors.primary,
|
||||||
|
colorPrimaryHover: "linear-gradient(135deg, #9d50bb 0%, #FF5F6D 100%)",
|
||||||
|
colorPrimaryActive: "#00C1D4",
|
||||||
|
lineWidth: 0,
|
||||||
|
},
|
||||||
|
Card: {
|
||||||
|
colorPrimary: gradientColors.primary,
|
||||||
|
boxShadow: "0 8px 32px rgba(110, 72, 170, 0.3)",
|
||||||
|
borderRadiusLG: 12,
|
||||||
|
},
|
||||||
|
Typography: {
|
||||||
|
colorPrimary: gradientColors.primary,
|
||||||
|
colorTextHeading: gradientColors.primary,
|
||||||
|
colorLink: gradientColors.primary, // Nebula pink
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// CSS-in-JS Utilities (For global use)
|
||||||
|
export const futuristicStyles = {
|
||||||
|
textGradient: `background: ${gradientColors.secondary}; -webkit-background-clip: text; -webkit-text-fill-color: transparent;`,
|
||||||
|
glassEffect: `background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1);`,
|
||||||
|
};
|
||||||
27
tsconfig.json
Normal file
27
tsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user