هذا الالتزام موجود في:
Your Name
2025-10-10 15:53:32 +00:00
الأصل 9230fd8e4e 7810e18241
التزام b9c53be445
7 ملفات معدلة مع 309 إضافات و99 حذوفات

عرض الملف

@@ -3,40 +3,58 @@
@tailwind utilities;
.container {
width: 100%;
/* max-width: 1500px; */
margin: 0 auto;
padding-left: 1rem;
padding-right: 1rem;
box-sizing: border-box;
}
width: 100%;
/* max-width: 1500px; */
margin: 0 auto;
padding-left: 1rem;
padding-right: 1rem;
box-sizing: border-box;
}
@media (max-width: 1299px) {
.container {
max-width: 100%;
}
@media (max-width: 1299px) {
.container {
max-width: 100%;
}
}
@media (min-width: 1300px) {
.container {
max-width: 1300px;
}
@media (min-width: 1300px) {
.container {
max-width: 1300px;
}
@media (min-width: 1600px) {
.container {
max-width: 1600px;
}
}
}
.recharts-surface {
cursor: pointer !important;
outline: none !important;
border: none !important;
box-shadow: none !important;
focus:outline-none !important;
focus:ring-0 !important;
focus:border-none !important;
}
@media (min-width: 1500px) {
.container {
max-width: 1500px;
}
}
@media (min-width: 1600px) {
.container {
max-width: 1600px;
}
}
.recharts-surface {
cursor: pointer !important;
outline: none !important;
border: none !important;
box-shadow: none !important;
focus: outline-none !important;
focus: ring-0 !important;
focus: border-none !important;
}
@media (min-width: 1024px) and (max-width: 1250px) {
.pr-m-5 {
padding-right: 5px;
}
}
@media (min-width: 1024px) and (max-width: 1296px) {
.w-m-40 {
width: 40% !important;
}
}

عرض الملف

@@ -95,14 +95,14 @@ const CampaignTracker = ({ handleMenuClick }: { handleMenuClick: () => void }) =
</div>
{/* Progress Sections */}
<div className="space-y-6">
<div className="space-y-8">
{progressSections.map((section, index) => {
const progressPercentage = (section.current / section.total) * 100
return (
<div key={index} className="space-y-1">
{/* Section Title and Progress */}
<div className="flex items-center justify-between">
<div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-semibold text-gray-900">
{section.title}: {section.current}/{section.total}
</h3>

عرض الملف

@@ -9,13 +9,16 @@ import {
Info,
ArrowUp,
Link,
ChevronDown
ChevronDown,
MapPin,
Sparkles
} from "lucide-react";
import MetricCard from "./shared/MetricCard";
import UrlDropdown from "./shared/UrlDropdown";
import HeaderPage from "./shared/HeaderPage";
import Google from "../assets/icons/google-original-logo.svg";
import Gemini from "../assets/icons/gemini.svg";
import Gemini2 from "../assets/icons/gemini2.svg";
import Perplexity from "../assets/icons/perplexity-dark-logo.svg";
import Vector from "../assets/icons/Vector.png";
import Vector2 from "../assets/icons/Vector2.png";
@@ -236,6 +239,103 @@ const CARDS = [
}
];
const competitors = [
{
domain: "Cherrybekaertla.com",
googleRank: 1,
mapPins: 7,
hasAi: true,
hasCircle: true,
hasDiamond: true,
hasTree: true,
hasLLM: true,
tags: [
{
content: [
{ icon: "Google", value: "1" }
],
color: "#4285F4"
},
{
content: [
{ icon: "Map", value: "7", }
],
color: "#4285F4"
},
{
content: [
{ icon: "Ai" },
{ icon: "Perplexity" },
{ icon: "GPT" }
]
}
]
},
{
domain: "Gerbercompany.com",
googleRank: 1,
mapPins: 7,
hasAi: true,
hasCircle: true,
hasDiamond: true,
hasTree: true,
hasLLM: true,
tags: [
{
content: [
{ icon: "Google", value: "1" }
],
color: "#4285F4"
},
{
content: [
{ icon: "Map", value: "7", }
],
color: "#4285F4"
},
{
content: [
{ icon: "Ai" },
{ icon: "Perplexity" },
{ icon: "GPT" }
]
}
]
},
{
domain: "Mysite.com",
googleRank: 1,
mapPins: 7,
hasAi: true,
hasCircle: true,
hasDiamond: true,
hasTree: true,
hasLLM: true,
tags: [
{
content: [
{ icon: "Google", value: "1" }
],
color: "#4285F4"
},
{
content: [
{ icon: "Map", value: "7", }
],
color: "#4285F4"
},
{
content: [
{ icon: "Ai" },
{ icon: "Perplexity" },
{ icon: "GPT" }
]
}
]
},
]
const pillClass = (val: string) =>
val === 'Done' ? 'bg-green-100 text-green-700 font-bold'
: val === 'In Progress' ? 'bg-blue-100 text-blue-700 font-bold'
@@ -247,6 +347,7 @@ export default function DashboardGrid() {
const [activeCard, setActiveCard] = useState<string | null>(null);
const [hoverState, setHoverState] = useState<HoverState>({ cardId: null, ringIndex: null });
const [isHovered, setIsHovered] = useState(false);
const [showKeywordAnalysis, setShowKeywordAnalysis] = useState<string | null>(null);
const handleCardClick = (campaignName: string, paused: boolean) => {
if (paused) return;
@@ -275,6 +376,14 @@ export default function DashboardGrid() {
setHoverState({ cardId, ringIndex });
};
const handleResumeClick = (campaignName: string) => {
setShowKeywordAnalysis(campaignName);
};
const handleCloseKeywordAnalysis = () => {
setShowKeywordAnalysis(null);
};
const TagComponent = ({ content, color = "#4285F4", faded = false }: Tag & { faded?: boolean }) => {
const getIconContent = (item: TagContent) => {
switch (item.icon) {
@@ -282,7 +391,7 @@ export default function DashboardGrid() {
return (
<>
<img src={Google} alt="Google" className="w-5 h-5" />
{item.value && <span className="text-black font-bold text-md">{item.value}</span>}
{item.value && <span className="text-[#17171B] font-semibold text-md">{item.value}</span>}
{item.extra && (
<span className={`ml-1 text-md ${item.extra.startsWith('+') ? 'text-green-600' : 'text-red-600'} font-bold`}>
{item.extra}
@@ -293,7 +402,7 @@ export default function DashboardGrid() {
case "Map":
return (
<>
<img src={Map} alt="Map" className="w-5 h-5" />
<img src={Map} alt="Map" className="w-5 h-6" />
{item.value && <span className="text-black font-bold text-md">{item.value}</span>}
{item.extra && (
<span className={`ml-1 text-md ${item.extra.startsWith('+') ? 'text-green-600' : 'text-red-600'} font-bold`}>
@@ -303,11 +412,41 @@ export default function DashboardGrid() {
</>
);
case "Ai":
return <img src={Gemini} alt="AI" className="w-5 h-5" />;
return (
<>
<img src={Gemini2} alt="AI" className="w-5 h-5" />
{item.value && <span className="text-black font-bold text-md">{item.value}</span>}
{item.extra && (
<span className={`ml-1 text-md ${item.extra.startsWith('+') ? 'text-green-600' : 'text-red-600'} font-bold`}>
{item.extra}
</span>
)}
</>
)
case "GPT":
return <img src={GPT} alt="GPT" className="w-5 h-5" />;
return (
<>
<img src={GPT} alt="GPT" className="w-5 h-5" />
{item.value && <span className="text-black font-bold text-md">{item.value}</span>}
{item.extra && (
<span className={`ml-1 text-md ${item.extra.startsWith('+') ? 'text-green-600' : 'text-red-600'} font-bold`}>
{item.extra}
</span>
)}
</>
)
case "Perplexity":
return <img src={Perplexity} alt="Perplexity" className="w-5 h-5" />;
return (
<>
<img src={Perplexity} alt="Perplexity" className="w-5 h-5" />
{item.value && <span className="text-black font-bold text-md">{item.value}</span>}
{item.extra && (
<span className={`ml-1 text-md ${item.extra.startsWith('+') ? 'text-green-600' : 'text-red-600'} font-bold`}>
{item.extra}
</span>
)}
</>
)
default:
return item.text || null;
}
@@ -401,59 +540,119 @@ export default function DashboardGrid() {
<div className={`relative w-full h-full transition-transform duration-700 ease-in-out [transform-style:preserve-3d] ${selectedCampaigns.includes(card.title) ? '[transform:rotateY(180deg)]' : ''
}`}>
{/* Front of card */}
<div className={`absolute w-full h-full [backface-visibility:hidden] bg-white rounded-2xl shadow-lg border transition-all duration-300 ease-in-out cursor-pointer ${card.faded ? "opacity-60" : ""}
${selectedCampaigns.includes(card.title) ? 'shadow-2xl scale-[1.02]' : 'hover:scale-105 hover:shadow-xl'
<div className={`absolute w-full h-full [backface-visibility:hidden] bg-white rounded-2xl border transition-all duration-300 ease-in-out cursor-pointer ${card.faded ? "opacity- 60" : ""}
${selectedCampaigns.includes(card.title) ? 'shadow-md scale-[1.02]' : 'hover:scale-135 hover:shadow-md'
}`}
style={{
transform: selectedCampaigns.includes(card.title)
? 'translateY(-5px) scale(1.02)'
: 'translateY(0px) scale(1)'
}}>
<div className="p-5 flex flex-col items-center h-full">
{card.badge && (
<div className="absolute top-0 right-0 overflow-hidden w-32 h-32 pointer-events-none">
<div className="absolute top-6 right-[-100px] w-[320px] bg-[#0CAA75] text-white font-semibold py-4 flex items-center justify-center gap-3 rotate-45 shadow-lg">
<div className="flex items-center justify-center gap-1 text-xs font-bold whitespace-nowrap">
<svg width="14" height="11" viewBox="0 0 14 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.20122 0.94856L9.60703 1.3343C11.8086 1.58456 12.9094 1.70875 13.0543 2.33535C13.1991 2.96195 12.2508 3.50763 10.3588 4.59994L10.18 4.70343C9.4979 5.09764 9.15638 5.29427 9.06888 5.6358L9.06417 5.65367C8.98326 5.99802 9.18836 6.35365 9.59857 7.06304C10.3371 8.34164 10.7069 8.98046 10.4434 9.42077L10.4293 9.44241C10.1527 9.87331 9.43957 9.7924 8.0095 9.63058L4.60462 9.2439C2.40307 8.99552 1.30323 8.87039 1.15834 8.24379C1.01345 7.61719 1.96088 7.07057 3.85289 5.97826L4.03165 5.87477C4.71376 5.48244 5.05434 5.28486 5.14372 4.94334L5.14842 4.92547C5.22839 4.58206 5.02329 4.22643 4.61403 3.5161C3.87547 2.2375 3.50573 1.59868 3.76916 1.15837L3.78327 1.13673C4.058 0.705826 4.77303 0.786737 6.20122 0.94856Z" stroke="white" strokeWidth="1.625" />
</svg>
{
card.paused && showKeywordAnalysis === card.title
?
<div className="bg-[#f6f8fe] rounded-xl shadow-sm border border-gray-200 p-6 px-20 h-full">
{/* New Keyword Badge */}
<span>{card.badge}</span>
<div className="flex justify-center mb-4">
<div className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm font-medium text-[#197232]" style={{ background: "linear-gradient(90deg, #d6fbe2 30%, #edf9d5 100%)" }}>
<Sparkles className="w-4 h-4" />
NEW High-Value Keyword Found
</div>
</div>
{/* Keyword Title */}
<h2 className="text-2xl font-bold text-gray-900 text-center mb-6">Tax Prep Attorney Los Angeles</h2>
{/* Competitors List */}
<div className="space-y-4 mb-6">
{competitors.map((competitor, index) => (
<div key={index} className="space-y-2">
<div className="text-md text-gray-600 font-bold">{competitor.domain}</div>
<div className="flex items-center gap-3 flex-wrap">
{competitor.tags.map((tag, i) => (
<TagComponent
key={i}
{...tag}
faded={card.faded}
/>
))}
</div>
</div>
))}
</div>
{/* Bottom CTA */}
<div className="text-center">
<p className="text-md text-gray-500 ">Your competitors own this keyword.</p>
<p className="text-md font-semibold text-[#2B2D3B] mb-3">Add it to capture visibility and sales.</p>
<button className="inline-flex items-center gap-2 px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors">
<span className="text-lg">+</span>
Add Keyword
</button>
</div>
</div>
)}
<MultiRingCircularProgress
card={card}
hoveredIndex={hoverState.cardId === card.title ? hoverState.ringIndex : null}
onHover={(index) => handleRingHover(card.title, index)}
cardId={card.title}
/>
<div className={`mt-1 font-bold text-lg text-center ${card.faded ? "text-gray-400" : "text-gray-900"}`}>
{card.title}
</div>
{/* Tags */}
<div className="mt-2 flex flex-row gap-2 flex-wrap justify-center">
{card.tags.map((tag, i) => (
<TagComponent
key={i}
{...tag}
faded={card.faded}
:
<div className="p-5 flex flex-col items-center h-full">
{card.badge && (
<div className="absolute top-0 right-0 overflow-hidden w-32 h-32 pointer-events-none">
<div className="absolute top-6 right-[-100px] w-[320px] bg-[#0CAA75] text-white font-semibold py-4 flex items-center justify-center gap-3 rotate-45 shadow-lg">
<div className="flex items-center justify-center gap-1 text-xs font-bold whitespace-nowrap">
<svg width="14" height="11" viewBox="0 0 14 11" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.20122 0.94856L9.60703 1.3343C11.8086 1.58456 12.9094 1.70875 13.0543 2.33535C13.1991 2.96195 12.2508 3.50763 10.3588 4.59994L10.18 4.70343C9.4979 5.09764 9.15638 5.29427 9.06888 5.6358L9.06417 5.65367C8.98326 5.99802 9.18836 6.35365 9.59857 7.06304C10.3371 8.34164 10.7069 8.98046 10.4434 9.42077L10.4293 9.44241C10.1527 9.87331 9.43957 9.7924 8.0095 9.63058L4.60462 9.2439C2.40307 8.99552 1.30323 8.87039 1.15834 8.24379C1.01345 7.61719 1.96088 7.07057 3.85289 5.97826L4.03165 5.87477C4.71376 5.48244 5.05434 5.28486 5.14372 4.94334L5.14842 4.92547C5.22839 4.58206 5.02329 4.22643 4.61403 3.5161C3.87547 2.2375 3.50573 1.59868 3.76916 1.15837L3.78327 1.13673C4.058 0.705826 4.77303 0.786737 6.20122 0.94856Z" stroke="white" strokeWidth="1.625" />
</svg>
<span>{card.badge}</span>
</div>
</div>
</div>
)}
<MultiRingCircularProgress
card={card}
hoveredIndex={hoverState.cardId === card.title ? hoverState.ringIndex : null}
onHover={(index) => handleRingHover(card.title, index)}
cardId={card.title}
/>
))}
</div>
{card.actionLink && (
<div className="mt-3 text-blue-600 text-sm cursor-pointer hover:underline transition-all duration-300 hover:text-blue-800">
{card.actionLink}
<div className={`mt-1 font-bold text-lg text-center ${card.faded ? "text-gray-400" : "text-gray-900"}`}>
{card.title}
</div>
{/* Tags */}
<div className="mt-2 flex flex-row gap-2 flex-wrap justify-center">
{card.tags.map((tag, i) => (
<TagComponent
key={i}
{...tag}
faded={card.faded}
/>
))}
</div>
{card.actionLink && (
<div className="mt-3 text-blue-600 text-sm cursor-pointer hover:underline transition-all duration-300 hover:text-blue-800">
{card.actionLink}
</div>
)}
{card.paused && (
<div className="mt-1 text-center">
<div className="text-gray-400 text-sm">Campaign paused</div>
<button
onClick={(e) => {
e.stopPropagation();
handleResumeClick(card.title);
}}
className="text-blue-600 text-sm hover:underline transition-all duration-300 hover:text-blue-800"
>
Resume
</button>
</div>
)
}
</div>
)}
{card.paused && (
<div className="mt-1 text-center">
<div className="text-gray-400 text-sm">Campaign paused</div>
<button className="text-blue-600 text-sm hover:underline transition-all duration-300 hover:text-blue-800">Resume</button>
</div>
)}
</div>
}
</div>
{/* Back of card */}
@@ -536,6 +735,7 @@ export default function DashboardGrid() {
</div>
))}
</div>
</>
);
}

عرض الملف

@@ -64,6 +64,7 @@ export default function MultiRingCircularProgress({ card, hoveredIndex, onHover,
onMouseEnter={() => onHover(index)}
onMouseLeave={() => onHover(null)}
fill={getHoverColor(ring, index)}
className="focus:outline-none focus:ring-0 focus:border-none"
// pointerEvents={'none'}
>
<Cell

عرض الملف

@@ -28,13 +28,14 @@ const BacklinkTypeDropdown = ({ options, title, subTitle }: { options: Record<st
return (
<div className='relative' ref={dropdownRef}>
<button
className='border rounded-lg px-3 py-1 bg-white text-gray-500 font-medium flex items-center text-md'
className={`border rounded-lg px-3 py-1 bg-white text-gray-500 font-medium flex items-center text-md ${dropdownOpen ? "border-2 border-solid border-blue-500" : ""}`}
onClick={() => setDropdownOpen(v => !v)}
>
{title} <ChevronDown className='ml-2' />
{title}
<ChevronDown className='ml-2' />
</button>
{dropdownOpen && (
<div className='absolute z-10 bg-white shadow-lg border rounded-lg top-[110%] right-0 px-4 py-3 min-w-[150px]'>
<div className={`absolute z-10 bg-white shadow-lg border rounded-lg top-[110%] right-0 px-4 py-3 min-w-[150px] `}>
<div className='font-semibold text-md text-gray-700 mb-2'>{subTitle}:</div>
{Object.keys(checkedRings).map((ring, idx) => (
<label

عرض الملف

@@ -35,7 +35,7 @@ function ProgressMetrics() {
<div className="w-[100%]">
<div className="flex items-center justify-between">
<div className="text-lg 3xl:text-4xl font-bold text-gray-900 mb-1">60%</div>
<div className="text-lg 3xl:text-3xl font-bold text-gray-900 mb-1">120/250 <span className="text-gray-500">links</span></div>
<div className="text-md 3xl:text-3xl font-bold text-gray-900 mb-1">120/250 <span className="text-gray-500">links</span></div>
</div>
<div className="text-xs 2xl:text-sm text-gray-500 mb-2">until content publishing + LLM seeding unlocked</div>
{/* Progress bar */}
@@ -49,7 +49,7 @@ function ProgressMetrics() {
</div>
<div className="flex items-center md:w-1/3 w-full md:border-r-2 border-0 border-solid border-gray-300 2xl:gap-12 lg:gap-6 gap-3">
<div className="flex items-center md:w-1/3 w-full md:border-r-2 border-0 border-solid border-gray-300 2xl:gap-12 lg:gap-3 gap-3 pr-m-5 w-m-40">
{/* 75% Keyword Visibility */}
<div className="w-[30%]">
<div className="text-xl 2xl:text-3xl font-bold text-gray-900 mb-1">75%</div>
@@ -57,16 +57,12 @@ function ProgressMetrics() {
</div>
<div className="flex items-center gap-3 w-[100%]">
<div className="flex items-center gap-3 flex-wrap">
<div className="flex items-center gap-3 ">
<MetricCard
img={Google} tooltipText="Google" valueChange="+1" size="sm" disabled />
<MetricCard
img={Map} tooltipText="Map" valueChange="+1" size="sm" disabled value="Map" />
{/* <MetricCard
img={Gemini} tooltipText="ChatGPT" valueChange="+1" size="sm" extraStyle="border-2 border-solid border-gray-300 rounded-lg " /> */}
<MetricCard
img={ChatGPT} tooltipText="ChatGPT" valueChange="+1" size="sm" />
@@ -100,7 +96,7 @@ function ProgressMetrics() {
<circle cx="96" cy="15" r="2" fill="#10B981" />
</svg>
</div>
<div className="text-right">
<div className="text-right">
<div className="flex items-center justify-start gap-1 mb-1">
<img src={Google} className="w-5 h-5 mr-2" alt="" />
<span className="text-lg font-bold text-gray-900">7</span>

عرض الملف

@@ -1,17 +1,11 @@
import React, { useState } from 'react';
import Vector from "../assets/icons/Vector.png"
import Vector2 from "../assets/icons/Vector2.png"
import BacklinkTypeDropdown from '../components/Portfolio/BacklinkTypeDropdown';
import RoadmapSidebar from '../components/Strategy/RoadmapSidebar';
import HeaderPage from '../components/shared/HeaderPage';
import ContainerPage from '../components/shared/ContainerPage';
import Amazing from "../assets/icons/amazon.png"
import Amazing from "../assets/icons/amazon.svg"
import { CircleCheck } from 'lucide-react';
// import Pinterest from "../assets/icons/pinterest.png"
// import AppleMusic from "../assets/icons/apple-music.png"
// import Substack from "../assets/icons/substack.png"
// import Pin from "../assets/icons/pin.png"
import Progress from "../assets/icons/InProgress.svg"
import No from "../assets/icons/NA.svg"
import Share2 from "../assets/icons/Share2.png"
@@ -108,7 +102,7 @@ function Portfolio() {
<BacklinkTypeDropdown options={{ DA: true, DR: true, Traffic: true }} title="Traffic" subTitle="Show rings:" />
<button className='bg-[#fff] text-gray-500 border px-3 sm:px-4 py-1 rounded-lg text-sm sm:text-md font-bold transition-colors flex items-center gap-2'>
<button className='bg-[#fff] text-gray-500 border px-3 sm:px-4 py-2 rounded-lg text-sm sm:text-md font-bold transition-colors flex items-center gap-2'>
<img src={Share2} alt="" className='w-4 h-4' />
<span>Export</span>
</button>