2 Commits

Author SHA1 Message Date
sanjidarimi 64770ddbcd fixed:fixed folder name queries and remove unnessary comment 2026-06-17 21:44:54 +06:00
sanjidarimi 370f93e1f7 refactor(theme):valid localstroage and organized theme codes 2026-06-17 21:25:44 +06:00
18 changed files with 109 additions and 99 deletions
@@ -2,7 +2,7 @@ import {
useDeleteProject,
useProjects,
useUpdateProject,
} from "@/hooks/queires/useProjects";
} from "@/hooks/queries/useProjects";
import { useState } from "react";
import { T_projects } from "@/types/projects.type";
+1 -1
View File
@@ -1,5 +1,5 @@
import { Button } from "@/components/ui/button";
import { useBlogs } from "@/hooks/queires/useBlogs";
import { useBlogs } from "@/hooks/queries/useBlogs";
import { motion } from "framer-motion";
import { ArrowRight, Calendar, Clock, User } from "lucide-react";
import { Link } from "react-router-dom";
+1 -18
View File
@@ -1,6 +1,6 @@
import logo from "@/assets/logo.webp";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/contexts/ThemeContext";
import { useTheme } from "@/hooks/useTheme";
import { AnimatePresence, motion } from "framer-motion";
import { Menu, Moon, Sun, X } from "lucide-react";
import { useEffect, useState } from "react";
@@ -28,7 +28,6 @@ export default function Navbar() {
const [isModalOpen, setIsModalOpen] = useState(false);
const { mode, accent, setAccent, toggleMode } = useTheme();
// Handle scroll effect
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
@@ -37,7 +36,6 @@ export default function Navbar() {
return () => window.removeEventListener("scroll", handleScroll);
}, []);
// Close mobile menu on resize
useEffect(() => {
const handleResize = () => {
if (window.innerWidth >= 1024) {
@@ -59,14 +57,11 @@ export default function Navbar() {
}`}
>
<div className="container mx-auto px-4 flex items-center justify-between">
{/* Logo */}
<Link to="/" className="flex items-center gap-2">
<img src={logo} alt="TechZaa" className="h-16 w-auto" />
</Link>
{/* Desktop Navigation */}
<div className="hidden lg:flex items-center gap-8">
{/* Nav Links */}
<ul className="flex items-center gap-6">
{navItems.map((item, index) => (
<motion.li
@@ -86,9 +81,7 @@ export default function Navbar() {
))}
</ul>
{/* Theme Controls */}
<div className="flex items-center gap-2">
{/* Accent Color Picker */}
<div className="flex items-center gap-1.5 rounded-full glass p-1">
{accentColors.map((color) => (
<motion.button
@@ -106,7 +99,6 @@ export default function Navbar() {
))}
</div>
{/* Dark/Light Mode Toggle */}
<motion.button
onClick={toggleMode}
className="p-1.5 rounded-full neon-glow transition-all"
@@ -140,7 +132,6 @@ export default function Navbar() {
</motion.button>
</div>
{/* CTA Button */}
<motion.div
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
@@ -155,7 +146,6 @@ export default function Navbar() {
</motion.div>
</div>
{/* Mobile Menu Button */}
<motion.button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="lg:hidden p-2 rounded-lg glass"
@@ -187,7 +177,6 @@ export default function Navbar() {
</motion.button>
</div>
{/* Mobile Menu */}
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
@@ -198,7 +187,6 @@ export default function Navbar() {
className="lg:hidden glass-strong mt-2 mx-4 rounded-2xl overflow-hidden"
>
<div className="p-6 space-y-6">
{/* Nav Links */}
<ul className="space-y-4">
{navItems.map((item, index) => (
<motion.li
@@ -218,9 +206,7 @@ export default function Navbar() {
))}
</ul>
{/* Theme Controls */}
<div className="flex items-center justify-between pt-4 border-t border-border">
{/* Accent Colors */}
<div className="flex items-center gap-2">
{accentColors.map((color) => (
<button
@@ -236,7 +222,6 @@ export default function Navbar() {
))}
</div>
{/* Mode Toggle */}
<button
onClick={toggleMode}
className="p-2 rounded-full glass"
@@ -250,7 +235,6 @@ export default function Navbar() {
</button>
</div>
{/* CTA Button */}
<Button
onClick={() => {
setIsMobileMenuOpen(false);
@@ -266,7 +250,6 @@ export default function Navbar() {
</AnimatePresence>
</motion.nav>
{/* Contact Modal */}
<ContactModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
+1 -6
View File
@@ -1,5 +1,5 @@
import { Button } from "@/components/ui/button";
import { useProjects } from "@/hooks/queires/useProjects";
import { useProjects } from "@/hooks/queries/useProjects";
import { motion, useInView } from "framer-motion";
import { ArrowRight } from "lucide-react";
import { useRef } from "react";
@@ -7,7 +7,6 @@ import { Link } from "react-router-dom";
import PremiumBadge from "../shared/PremiumBadge";
import { ProjectCard } from "./ProjectCard";
export default function ProjectsSection() {
const { data: projectsData } = useProjects({
fields: "category, title, image, liveUrl",
@@ -25,7 +24,6 @@ export default function ProjectsSection() {
ref={ref}
className="py-24 relative overflow-hidden bg-secondary/30"
>
<div className="absolute inset-0 opacity-5">
<div
className="absolute inset-0"
@@ -37,7 +35,6 @@ export default function ProjectsSection() {
</div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
@@ -56,7 +53,6 @@ export default function ProjectsSection() {
</p>
</motion.div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
{projects.map((project, index) => {
return (
@@ -70,7 +66,6 @@ export default function ProjectsSection() {
})}
</div>
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
+6 -6
View File
@@ -3,7 +3,7 @@ import { useRef } from "react";
import { Autoplay, Pagination } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import { useTeam } from "@/hooks/queires/useTeam";
import { useTeam } from "@/hooks/queries/useTeam";
import PremiumBadge from "../shared/PremiumBadge";
export default function TeamSection() {
@@ -17,23 +17,24 @@ export default function TeamSection() {
return (
<section id="team" ref={ref} className="pt-20 1relative overflow-hidden">
<div className="absolute inset-0 opacity-30">
<div className="absolute top-0 right-1/4 w-72 h-72 bg-neon-purple/10 rounded-full blur-[120px]" />
<div className="absolute bottom-0 left-1/4 w-64 h-64 bg-primary/10 rounded-full blur-xl" />
</div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-center mb-16"
>
<PremiumBadge text="Our experts"/>
<PremiumBadge text="Our experts" />
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold mb-4">
Meet Our <span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-accent-foreground">Team</span>
Meet Our{" "}
<span className="text-transparent bg-clip-text bg-gradient-to-r from-primary to-accent-foreground">
Team
</span>
</h2>
<p className="text-muted-foreground max-w-2xl mx-auto text-lg">
A passionate team of innovators, designers, and developers.
@@ -81,7 +82,6 @@ export default function TeamSection() {
className="absolute inset-0 w-full h-full object-cover object-top transition-transform duration-500 group-hover:scale-110"
/>
</div>
</div>
<h3 className="text-xl font-bold mb-1 group-hover:text-primary transition-colors">
+1 -1
View File
@@ -1,5 +1,5 @@
import { Button } from "@/components/ui/button";
import { useReviews } from "@/hooks/queires/useReviews";
import { useReviews } from "@/hooks/queries/useReviews";
import Autoplay from "embla-carousel-autoplay";
import useEmblaCarousel from "embla-carousel-react";
import { motion } from "framer-motion";
+3 -62
View File
@@ -1,63 +1,4 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { ThemeContextType } from "@/types/theme.interface";
import { createContext } from "react";
type ThemeMode = 'light' | 'dark';
type AccentTheme = 'blue' | 'purple' | 'green';
interface ThemeContextType {
mode: ThemeMode;
accent: AccentTheme;
setMode: (mode: ThemeMode) => void;
setAccent: (accent: AccentTheme) => void;
toggleMode: () => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>('dark');
const [accent, setAccentState] = useState<AccentTheme>('blue');
// Initialize theme from localStorage
useEffect(() => {
const savedMode = localStorage.getItem('techzaa-mode') as ThemeMode;
const savedAccent = localStorage.getItem('techzaa-accent') as AccentTheme;
if (savedMode) setModeState(savedMode);
if (savedAccent) setAccentState(savedAccent);
}, []);
// Apply theme classes to document
useEffect(() => {
const root = document.documentElement;
// Apply dark/light mode
root.classList.remove('light', 'dark');
root.classList.add(mode);
// Apply accent theme
root.classList.remove('theme-blue', 'theme-purple', 'theme-green');
root.classList.add(`theme-${accent}`);
// Save to localStorage
localStorage.setItem('techzaa-mode', mode);
localStorage.setItem('techzaa-accent', accent);
}, [mode, accent]);
const setMode = (newMode: ThemeMode) => setModeState(newMode);
const setAccent = (newAccent: AccentTheme) => setAccentState(newAccent);
const toggleMode = () => setModeState(prev => prev === 'dark' ? 'light' : 'dark');
return (
<ThemeContext.Provider value={{ mode, accent, setMode, setAccent, toggleMode }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}
export const ThemeContext = createContext<ThemeContextType | null>(null);
+15
View File
@@ -0,0 +1,15 @@
import { ThemeContext } from "@/contexts/ThemeContext";
import { ThemeContextType } from "@/types/theme.interface";
import { useContext } from "react";
export function useTheme(): ThemeContextType {
const context = useContext(ThemeContext);
if (!context) {
throw new Error(
"useTheme must be used within a ThemeProvider"
);
}
return context;
}
+1 -1
View File
@@ -5,7 +5,7 @@ import { motion } from "framer-motion";
import { ArrowLeft, Calendar, Clock, Search, User } from "lucide-react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { useBlogs } from "./../hooks/queires/useBlogs";
import { useBlogs } from "../hooks/queries/useBlogs";
const categories = [
"All",
+1 -1
View File
@@ -1,7 +1,7 @@
import PageTransition from "@/components/home/PageTransition";
import { Button } from "@/components/ui/button";
import { getRelatedPosts } from "@/data/blogData";
import { useBlogById } from "@/hooks/queires/useBlogs";
import { useBlogById } from "@/hooks/queries/useBlogs";
import { motion } from "framer-motion";
import {
ArrowLeft,
+1 -1
View File
@@ -1,6 +1,6 @@
import PageTransition from "@/components/home/PageTransition";
import { Button } from "@/components/ui/button";
import { useProjectById } from "@/hooks/queires/useProjects";
import { useProjectById } from "@/hooks/queries/useProjects";
import { motion, useReducedMotion } from "framer-motion";
import {
AlertCircle,
+1 -1
View File
@@ -1,7 +1,7 @@
import PageTransition from "@/components/home/PageTransition";
import { ProjectCard } from "@/components/home/ProjectCard";
import { Button } from "@/components/ui/button";
import { useProjects } from "@/hooks/queires/useProjects";
import { useProjects } from "@/hooks/queries/useProjects";
import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import {
ArrowLeft,
+66
View File
@@ -0,0 +1,66 @@
import { ThemeContext } from "@/contexts/ThemeContext";
import { ThemeMode, AccentTheme } from "@/types/theme.interface";
import { useState, useEffect, useCallback, useMemo } from "react";
const STORAGE_KEYS = {
mode: "techzaa-mode",
accent: "techzaa-accent",
} as const;
const isValidMode = (value: unknown): value is ThemeMode =>
value === "light" || value === "dark";
const isValidAccent = (value: unknown): value is AccentTheme =>
value === "blue" || value === "purple" || value === "green";
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [mode, setMode] = useState<ThemeMode>(() => {
if (typeof window === "undefined") return "dark";
const stored = localStorage.getItem(STORAGE_KEYS.mode);
return isValidMode(stored) ? stored : "dark";
});
const [accent, setAccent] = useState<AccentTheme>(() => {
if (typeof window === "undefined") return "blue";
const stored = localStorage.getItem(STORAGE_KEYS.accent);
return isValidAccent(stored) ? stored : "blue";
});
useEffect(() => {
if (typeof document === "undefined") return;
const root = document.documentElement;
root.classList.remove("light", "dark");
root.classList.add(mode);
root.classList.remove("theme-blue", "theme-purple", "theme-green");
root.classList.add(`theme-${accent}`);
localStorage.setItem(STORAGE_KEYS.mode, mode);
localStorage.setItem(STORAGE_KEYS.accent, accent);
}, [mode, accent]);
const toggleMode = useCallback(() => {
setMode((prev) => (prev === "dark" ? "light" : "dark"));
}, []);
const value = useMemo(
() => ({
mode,
accent,
setMode,
setAccent,
toggleMode,
}),
[mode, accent, toggleMode],
);
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
}
+10
View File
@@ -0,0 +1,10 @@
export type ThemeMode = "light" | "dark";
export type AccentTheme = "blue" | "purple" | "green";
export interface ThemeContextType {
mode: ThemeMode;
accent: AccentTheme;
setMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
setAccent: React.Dispatch<React.SetStateAction<AccentTheme>>;
toggleMode: () => void;
}