Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a7a080c61 | |||
| a3afa63717 | |||
| aa7ee3f19e | |||
| e1ed111c55 | |||
| 24544eb680 |
@@ -13,7 +13,7 @@ export default function ManageProject() {
|
|||||||
const { data: projectsData, isLoading } = useProjects();
|
const { data: projectsData, isLoading } = useProjects();
|
||||||
const updateMutation = useUpdateProject();
|
const updateMutation = useUpdateProject();
|
||||||
const deleteMutation = useDeleteProject();
|
const deleteMutation = useDeleteProject();
|
||||||
console.log("project data", projectsData?.data.data.result)
|
|
||||||
const [selectedProject, setSelectedProject] = useState<T_projects | null>(
|
const [selectedProject, setSelectedProject] = useState<T_projects | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export default function BlogSection() {
|
|||||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-primary/5 to-transparent" />
|
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-primary/5 to-transparent" />
|
||||||
|
|
||||||
<div className="container mx-auto px-4 relative z-10">
|
<div className="container mx-auto px-4 relative z-10">
|
||||||
{/* Section Header */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import logo from "@/assets/logo.webp";
|
import logo from "@/assets/logo.webp";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Facebook, Github, Gitlab, Instagram, Linkedin, Mail, Twitter } from "lucide-react";
|
import { Facebook, Gitlab, Instagram, Linkedin } from "lucide-react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
const quickLinks = [
|
const quickLinks = [
|
||||||
@@ -24,7 +24,11 @@ const socials = [
|
|||||||
href: "https://www.linkedin.com/company/techzaa",
|
href: "https://www.linkedin.com/company/techzaa",
|
||||||
label: "LinkedIn",
|
label: "LinkedIn",
|
||||||
},
|
},
|
||||||
{ icon: Facebook, href: "https://www.facebook.com/techzaaalpha", label: "Facebook" },
|
{
|
||||||
|
icon: Facebook,
|
||||||
|
href: "https://www.facebook.com/techzaaalpha",
|
||||||
|
label: "Facebook",
|
||||||
|
},
|
||||||
{ icon: Gitlab, href: "https://gitlab.techzaa.tech", label: "Gitlab" },
|
{ icon: Gitlab, href: "https://gitlab.techzaa.tech", label: "Gitlab" },
|
||||||
{ icon: Instagram, href: "#", label: "Instagram" },
|
{ icon: Instagram, href: "#", label: "Instagram" },
|
||||||
];
|
];
|
||||||
@@ -32,16 +36,16 @@ const socials = [
|
|||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="relative overflow-hidden bg-secondary/50 py-20">
|
<footer className="relative overflow-hidden bg-secondary/50 py-20">
|
||||||
{/* Animated gradient divider */}
|
|
||||||
<div className="absolute top-0 left-0 right-0 h-1 gradient-primary-animated" />
|
<div className="absolute top-0 left-0 right-0 h-1 gradient-primary-animated" />
|
||||||
|
|
||||||
{/* Background decoration */}
|
|
||||||
<div className="absolute inset-0 opacity-20">
|
<div className="absolute inset-0 opacity-20">
|
||||||
<div className="absolute bottom-0 left-0 w-96 h-96 bg-primary/10 rounded-full blur-[150px]" />
|
<div className="absolute bottom-0 left-0 w-96 h-96 bg-primary/10 rounded-full blur-[150px]" />
|
||||||
<div className="absolute top-0 right-0 w-72 h-72 bg-neon-purple/10 rounded-full blur-[120px]" />
|
<div className="absolute top-0 right-0 w-72 h-72 bg-neon-purple/10 rounded-full blur-[120px]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Circuit pattern background */}
|
|
||||||
<div className="absolute inset-0 opacity-5">
|
<div className="absolute inset-0 opacity-5">
|
||||||
<svg
|
<svg
|
||||||
className="w-full h-full"
|
className="w-full h-full"
|
||||||
@@ -70,7 +74,7 @@ export default function Footer() {
|
|||||||
|
|
||||||
<div className="container mx-auto px-4 relative z-10">
|
<div className="container mx-auto px-4 relative z-10">
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-12 mb-16">
|
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-12 mb-16">
|
||||||
{/* Brand */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
@@ -84,11 +88,9 @@ export default function Footer() {
|
|||||||
Transforming ideas into powerful digital solutions. We build the
|
Transforming ideas into powerful digital solutions. We build the
|
||||||
future with technology.
|
future with technology.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Quick Links */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
@@ -111,7 +113,7 @@ export default function Footer() {
|
|||||||
</ul>
|
</ul>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Services */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
@@ -157,7 +159,7 @@ export default function Footer() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-muted-foreground text-sm">
|
<p className="text-muted-foreground text-sm">
|
||||||
Rangpur, Bangladesh
|
Rangpur, Bangladesh
|
||||||
<br />
|
<br />
|
||||||
techzaa.alpha@gmail.com
|
techzaa.alpha@gmail.com
|
||||||
<br />
|
<br />
|
||||||
@@ -179,13 +181,22 @@ export default function Footer() {
|
|||||||
© {new Date().getFullYear()} TechZaa. All rights reserved.
|
© {new Date().getFullYear()} TechZaa. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
||||||
<Link to="/privacy" className="hover:text-primary transition-colors">
|
<Link
|
||||||
|
to="/privacy"
|
||||||
|
className="hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
Privacy Policy
|
Privacy Policy
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/privacy" className="hover:text-primary transition-colors">
|
<Link
|
||||||
|
to="/privacy"
|
||||||
|
className="hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
Terms of Service
|
Terms of Service
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/privacy" className="hover:text-primary transition-colors">
|
<Link
|
||||||
|
to="/privacy"
|
||||||
|
className="hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
Cookie Policy
|
Cookie Policy
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,21 +7,15 @@ import { Link } from "react-router-dom";
|
|||||||
import PremiumBadge from "../shared/PremiumBadge";
|
import PremiumBadge from "../shared/PremiumBadge";
|
||||||
import { ProjectCard } from "./ProjectCard";
|
import { ProjectCard } from "./ProjectCard";
|
||||||
|
|
||||||
const gradientColors = [
|
|
||||||
"from-neon-blue/90",
|
|
||||||
"from-neon-purple/90",
|
|
||||||
"from-neon-green/90",
|
|
||||||
"from-neon-pink/90",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ProjectsSection() {
|
export default function ProjectsSection() {
|
||||||
const { data: projectsData } = useProjects({
|
const { data: projectsData } = useProjects({
|
||||||
fields: "category, title, image, liveUrl",
|
fields: "category, title, image, liveUrl",
|
||||||
limit: 6,
|
limit: 6,
|
||||||
});
|
});
|
||||||
console.log(projectsData?.data.data);
|
|
||||||
const projects = projectsData?.data.data || [];
|
const projects = projectsData?.data.data || [];
|
||||||
console.log("project from homepage", projects);
|
|
||||||
const ref = useRef<HTMLElement>(null);
|
const ref = useRef<HTMLElement>(null);
|
||||||
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
const isInView = useInView(ref, { once: true, margin: "-100px" });
|
||||||
|
|
||||||
@@ -31,7 +25,7 @@ export default function ProjectsSection() {
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className="py-24 relative overflow-hidden bg-secondary/30"
|
className="py-24 relative overflow-hidden bg-secondary/30"
|
||||||
>
|
>
|
||||||
{/* Background Pattern */}
|
|
||||||
<div className="absolute inset-0 opacity-5">
|
<div className="absolute inset-0 opacity-5">
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0"
|
className="absolute inset-0"
|
||||||
@@ -43,7 +37,7 @@ export default function ProjectsSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container mx-auto px-4 relative z-10">
|
<div className="container mx-auto px-4 relative z-10">
|
||||||
{/* Section Header */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
@@ -62,7 +56,7 @@ export default function ProjectsSection() {
|
|||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Projects Grid */}
|
|
||||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
|
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8 mb-16">
|
||||||
{projects.map((project, index) => {
|
{projects.map((project, index) => {
|
||||||
return (
|
return (
|
||||||
@@ -76,7 +70,7 @@ export default function ProjectsSection() {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* See All Button */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 30 }}
|
initial={{ opacity: 0, y: 30 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { motion, useInView } from "framer-motion";
|
import { motion, useInView } from "framer-motion";
|
||||||
import { Github, Linkedin, Twitter } from "lucide-react";
|
|
||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { Autoplay, Pagination } from "swiper/modules";
|
import { Autoplay, Pagination } from "swiper/modules";
|
||||||
import { Swiper, SwiperSlide } from "swiper/react";
|
import { Swiper, SwiperSlide } from "swiper/react";
|
||||||
|
|
||||||
import { useTeam } from "@/hooks/queires/useTeam";
|
import { useTeam } from "@/hooks/queires/useTeam";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import PremiumBadge from "../shared/PremiumBadge";
|
import PremiumBadge from "../shared/PremiumBadge";
|
||||||
|
|
||||||
export default function TeamSection() {
|
export default function TeamSection() {
|
||||||
@@ -19,14 +17,14 @@ export default function TeamSection() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="team" ref={ref} className="pt-20 1relative overflow-hidden">
|
<section id="team" ref={ref} className="pt-20 1relative overflow-hidden">
|
||||||
{/* Background decoration */}
|
|
||||||
<div className="absolute inset-0 opacity-30">
|
<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 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 className="absolute bottom-0 left-1/4 w-64 h-64 bg-primary/10 rounded-full blur-xl" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="container mx-auto px-4 relative z-10">
|
<div className="container mx-auto px-4 relative z-10">
|
||||||
{/* Section Header */}
|
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||||
@@ -42,7 +40,6 @@ export default function TeamSection() {
|
|||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Team Slider Container */}
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Swiper
|
<Swiper
|
||||||
direction={"horizontal"}
|
direction={"horizontal"}
|
||||||
|
|||||||
@@ -1,303 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import * as RechartsPrimitive from "recharts";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
// Format: { THEME_NAME: CSS_SELECTOR }
|
|
||||||
const THEMES = { light: "", dark: ".dark" } as const;
|
|
||||||
|
|
||||||
export type ChartConfig = {
|
|
||||||
[k in string]: {
|
|
||||||
label?: React.ReactNode;
|
|
||||||
icon?: React.ComponentType;
|
|
||||||
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
|
|
||||||
};
|
|
||||||
|
|
||||||
type ChartContextProps = {
|
|
||||||
config: ChartConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ChartContext = React.createContext<ChartContextProps | null>(null);
|
|
||||||
|
|
||||||
function useChart() {
|
|
||||||
const context = React.useContext(ChartContext);
|
|
||||||
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useChart must be used within a <ChartContainer />");
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ChartContainer = React.forwardRef<
|
|
||||||
HTMLDivElement,
|
|
||||||
React.ComponentProps<"div"> & {
|
|
||||||
config: ChartConfig;
|
|
||||||
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"];
|
|
||||||
}
|
|
||||||
>(({ id, className, children, config, ...props }, ref) => {
|
|
||||||
const uniqueId = React.useId();
|
|
||||||
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ChartContext.Provider value={{ config }}>
|
|
||||||
<div
|
|
||||||
data-chart={chartId}
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<ChartStyle id={chartId} config={config} />
|
|
||||||
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
|
|
||||||
</div>
|
|
||||||
</ChartContext.Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ChartContainer.displayName = "Chart";
|
|
||||||
|
|
||||||
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
|
|
||||||
const colorConfig = Object.entries(config).filter(([_, config]) => config.theme || config.color);
|
|
||||||
|
|
||||||
if (!colorConfig.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<style
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: Object.entries(THEMES)
|
|
||||||
.map(
|
|
||||||
([theme, prefix]) => `
|
|
||||||
${prefix} [data-chart=${id}] {
|
|
||||||
${colorConfig
|
|
||||||
.map(([key, itemConfig]) => {
|
|
||||||
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
|
|
||||||
return color ? ` --color-${key}: ${color};` : null;
|
|
||||||
})
|
|
||||||
.join("\n")}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
.join("\n"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ChartTooltip = RechartsPrimitive.Tooltip;
|
|
||||||
|
|
||||||
const ChartTooltipContent = React.forwardRef<
|
|
||||||
HTMLDivElement,
|
|
||||||
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
|
|
||||||
React.ComponentProps<"div"> & {
|
|
||||||
hideLabel?: boolean;
|
|
||||||
hideIndicator?: boolean;
|
|
||||||
indicator?: "line" | "dot" | "dashed";
|
|
||||||
nameKey?: string;
|
|
||||||
labelKey?: string;
|
|
||||||
}
|
|
||||||
>(
|
|
||||||
(
|
|
||||||
{
|
|
||||||
active,
|
|
||||||
payload,
|
|
||||||
className,
|
|
||||||
indicator = "dot",
|
|
||||||
hideLabel = false,
|
|
||||||
hideIndicator = false,
|
|
||||||
label,
|
|
||||||
labelFormatter,
|
|
||||||
labelClassName,
|
|
||||||
formatter,
|
|
||||||
color,
|
|
||||||
nameKey,
|
|
||||||
labelKey,
|
|
||||||
},
|
|
||||||
ref,
|
|
||||||
) => {
|
|
||||||
const { config } = useChart();
|
|
||||||
|
|
||||||
const tooltipLabel = React.useMemo(() => {
|
|
||||||
if (hideLabel || !payload?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [item] = payload;
|
|
||||||
const key = `${labelKey || item.dataKey || item.name || "value"}`;
|
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
||||||
const value =
|
|
||||||
!labelKey && typeof label === "string"
|
|
||||||
? config[label as keyof typeof config]?.label || label
|
|
||||||
: itemConfig?.label;
|
|
||||||
|
|
||||||
if (labelFormatter) {
|
|
||||||
return <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div className={cn("font-medium", labelClassName)}>{value}</div>;
|
|
||||||
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey]);
|
|
||||||
|
|
||||||
if (!active || !payload?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nestLabel = payload.length === 1 && indicator !== "dot";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
className={cn(
|
|
||||||
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!nestLabel ? tooltipLabel : null}
|
|
||||||
<div className="grid gap-1.5">
|
|
||||||
{payload.map((item, index) => {
|
|
||||||
const key = `${nameKey || item.name || item.dataKey || "value"}`;
|
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
||||||
const indicatorColor = color || item.payload.fill || item.color;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={item.dataKey}
|
|
||||||
className={cn(
|
|
||||||
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
|
|
||||||
indicator === "dot" && "items-center",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{formatter && item?.value !== undefined && item.name ? (
|
|
||||||
formatter(item.value, item.name, item, index, item.payload)
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{itemConfig?.icon ? (
|
|
||||||
<itemConfig.icon />
|
|
||||||
) : (
|
|
||||||
!hideIndicator && (
|
|
||||||
<div
|
|
||||||
className={cn("shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]", {
|
|
||||||
"h-2.5 w-2.5": indicator === "dot",
|
|
||||||
"w-1": indicator === "line",
|
|
||||||
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
|
||||||
"my-0.5": nestLabel && indicator === "dashed",
|
|
||||||
})}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
"--color-bg": indicatorColor,
|
|
||||||
"--color-border": indicatorColor,
|
|
||||||
} as React.CSSProperties
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"flex flex-1 justify-between leading-none",
|
|
||||||
nestLabel ? "items-end" : "items-center",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="grid gap-1.5">
|
|
||||||
{nestLabel ? tooltipLabel : null}
|
|
||||||
<span className="text-muted-foreground">{itemConfig?.label || item.name}</span>
|
|
||||||
</div>
|
|
||||||
{item.value && (
|
|
||||||
<span className="font-mono font-medium tabular-nums text-foreground">
|
|
||||||
{item.value.toLocaleString()}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
ChartTooltipContent.displayName = "ChartTooltip";
|
|
||||||
|
|
||||||
const ChartLegend = RechartsPrimitive.Legend;
|
|
||||||
|
|
||||||
const ChartLegendContent = React.forwardRef<
|
|
||||||
HTMLDivElement,
|
|
||||||
React.ComponentProps<"div"> &
|
|
||||||
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
|
|
||||||
hideIcon?: boolean;
|
|
||||||
nameKey?: string;
|
|
||||||
}
|
|
||||||
>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
|
|
||||||
const { config } = useChart();
|
|
||||||
|
|
||||||
if (!payload?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
className={cn("flex items-center justify-center gap-4", verticalAlign === "top" ? "pb-3" : "pt-3", className)}
|
|
||||||
>
|
|
||||||
{payload.map((item) => {
|
|
||||||
const key = `${nameKey || item.dataKey || "value"}`;
|
|
||||||
const itemConfig = getPayloadConfigFromPayload(config, item, key);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={item.value}
|
|
||||||
className={cn("flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground")}
|
|
||||||
>
|
|
||||||
{itemConfig?.icon && !hideIcon ? (
|
|
||||||
<itemConfig.icon />
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className="h-2 w-2 shrink-0 rounded-[2px]"
|
|
||||||
style={{
|
|
||||||
backgroundColor: item.color,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{itemConfig?.label}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
ChartLegendContent.displayName = "ChartLegend";
|
|
||||||
|
|
||||||
// Helper to extract item config from a payload.
|
|
||||||
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
|
|
||||||
if (typeof payload !== "object" || payload === null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const payloadPayload =
|
|
||||||
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
|
|
||||||
? payload.payload
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
let configLabelKey: string = key;
|
|
||||||
|
|
||||||
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
|
|
||||||
configLabelKey = payload[key as keyof typeof payload] as string;
|
|
||||||
} else if (
|
|
||||||
payloadPayload &&
|
|
||||||
key in payloadPayload &&
|
|
||||||
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
|
||||||
) {
|
|
||||||
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
|
|
||||||
}
|
|
||||||
|
|
||||||
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle };
|
|
||||||
@@ -16,11 +16,9 @@ import ReactMarkdown from "react-markdown";
|
|||||||
import { Link, useNavigate, useParams } from "react-router-dom";
|
import { Link, useNavigate, useParams } from "react-router-dom";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
export default function BlogArticle() {
|
export default function BlogArticle() {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
const { data } = useBlogById(id);
|
const { data } = useBlogById(id);
|
||||||
|
|
||||||
const post = data?.data.data;
|
const post = data?.data.data;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
// const post = getPostBySlug(id || '');
|
// const post = getPostBySlug(id || '');
|
||||||
@@ -337,24 +335,24 @@ export default function BlogArticle() {
|
|||||||
<p className="text-muted-foreground mb-4">{post.author.bio}</p>
|
<p className="text-muted-foreground mb-4">{post.author.bio}</p>
|
||||||
<div className="flex gap-3 justify-center md:justify-start">
|
<div className="flex gap-3 justify-center md:justify-start">
|
||||||
{post.author.twitter && (
|
{post.author.twitter && (
|
||||||
<Link
|
<a
|
||||||
to={`https://twitter.com/${post.author.twitter}`}
|
href={`https://twitter.com/${post.author.twitter}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="p-2 rounded-full glass hover:neon-glow transition-all"
|
className="p-2 rounded-full glass hover:neon-glow transition-all"
|
||||||
>
|
>
|
||||||
<Twitter className="w-5 h-5" />
|
<Twitter className="w-5 h-5" />
|
||||||
</Link>
|
</a>
|
||||||
)}
|
)}
|
||||||
{post.author.linkedin && (
|
{post.author.linkedin && (
|
||||||
<Link
|
<a
|
||||||
to={`https://linkedin.com/in/${post.author.linkedin}`}
|
href={`https://linkedin.com/in/${post.author.linkedin}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="p-2 rounded-full glass hover:neon-glow transition-all"
|
className="p-2 rounded-full glass hover:neon-glow transition-all"
|
||||||
>
|
>
|
||||||
<Linkedin className="w-5 h-5" />
|
<Linkedin className="w-5 h-5" />
|
||||||
</Link>
|
</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,18 +1,13 @@
|
|||||||
import { Link, useLocation } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useEffect } from "react";
|
|
||||||
|
|
||||||
const NotFound = () => {
|
const NotFound = () => {
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.error("404 Error: User attempted to access non-existent route:", location.pathname);
|
|
||||||
}, [location.pathname]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-muted">
|
<div className="flex min-h-screen items-center justify-center bg-muted">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h1 className="mb-4 text-4xl font-bold">404</h1>
|
<h1 className="mb-4 text-4xl font-bold">404</h1>
|
||||||
<p className="mb-4 text-xl text-muted-foreground">Oops! Page not found</p>
|
<p className="mb-4 text-xl text-muted-foreground">
|
||||||
|
Oops! Page not found.
|
||||||
|
</p>
|
||||||
<Link to="/" className="text-primary underline hover:text-primary/90">
|
<Link to="/" className="text-primary underline hover:text-primary/90">
|
||||||
Return to Home
|
Return to Home
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ export default function Projects() {
|
|||||||
<div className="min-h-screen bg-background text-foreground antialiased selection:bg-primary/20">
|
<div className="min-h-screen bg-background text-foreground antialiased selection:bg-primary/20">
|
||||||
<section className="pt-32 pb-12 relative overflow-hidden border-b border-border/40">
|
<section className="pt-32 pb-12 relative overflow-hidden border-b border-border/40">
|
||||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||||
|
|
||||||
{/* Back Arrow Wrapper - Kept clean on left side */}
|
{/* Back Arrow Wrapper - Kept clean on left side */}
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, x: shouldReduceMotion ? 0 : -10 }}
|
initial={{ opacity: 0, x: shouldReduceMotion ? 0 : -10 }}
|
||||||
@@ -98,7 +97,6 @@ export default function Projects() {
|
|||||||
</Button>
|
</Button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Core Header Content Module - Flex-centered alignment */}
|
|
||||||
<div className="w-full flex flex-col items-center justify-center text-center">
|
<div className="w-full flex flex-col items-center justify-center text-center">
|
||||||
<div className="max-w-3xl mb-12">
|
<div className="max-w-3xl mb-12">
|
||||||
<motion.h1
|
<motion.h1
|
||||||
@@ -166,7 +164,6 @@ export default function Projects() {
|
|||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -268,4 +265,4 @@ export default function Projects() {
|
|||||||
</div>
|
</div>
|
||||||
</PageTransition>
|
</PageTransition>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user