Skip to content

Commit 2730592

Browse files
feat: implement mobile restriction for dev mode, update contact form timestamps, and make portfolio fully dynamic via Admin Panel
1 parent 8503243 commit 2730592

17 files changed

Lines changed: 309 additions & 85 deletions

CONTACT_FORM_SETUP.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ function doPost(e) {
2727
rowData[1] = e.parameter.email || '';
2828
rowData[2] = e.parameter.subject || '';
2929
rowData[3] = e.parameter.message || '';
30-
rowData[4] = e.parameter.timestamp || new Date().toISOString();
30+
31+
// Format timestamp for Pakistan (GMT+5)
32+
// Format: Wed, 02 - 2026, 3:00 PM
33+
var now = new Date();
34+
var formattedDate = Utilities.formatDate(now, "GMT+5", "EEE, dd - yyyy, h:mm a");
35+
rowData[4] = formattedDate;
3136

3237
// Add row to sheet
3338
sheet.appendRow(rowData);

client/public/admin/config.yml

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ collections:
2727
- label: "Technologies"
2828
name: "technologies"
2929
widget: "list"
30-
field: { label: "Tech", name: "tech", widget: "string" }
30+
field: { label: "Tech", widget: "string" }
3131
- { label: "Live Demo URL", name: "liveUrl", widget: "string", required: false }
3232
- { label: "GitHub URL", name: "githubUrl", widget: "string", required: false }
3333
- label: "Stats"
@@ -122,6 +122,16 @@ collections:
122122
- { label: "Availability Text", name: "availabilityText", widget: "string", default: "Seeking Internship Opportunities" }
123123
- { label: "Hero Image", name: "image", widget: "image" }
124124
- { label: "Resume URL", name: "resumeUrl", widget: "string" }
125+
- label: "Typing Animations"
126+
name: "typingTexts"
127+
widget: "list"
128+
field: { label: "Text", widget: "string" }
129+
- label: "Navigation Items"
130+
name: "navItems"
131+
widget: "list"
132+
fields:
133+
- { label: "Label", name: "label", widget: "string" }
134+
- { label: "Anchor (e.g. #home)", name: "href", widget: "string" }
125135
- label: "Stats"
126136
name: "stats"
127137
widget: "object"
@@ -134,8 +144,22 @@ collections:
134144
label: "Story"
135145
file: "client/src/content/about.json"
136146
fields:
147+
- { label: "Full Name", name: "name", widget: "string" }
148+
- { label: "Professional Title", name: "title", widget: "string" }
149+
- { label: "Location", name: "location", widget: "string" }
150+
- { label: "Passion Statement", name: "passion", widget: "string" }
137151
- { label: "Bio", name: "bio", widget: "text" }
138152
- { label: "Available for Hire", name: "availableForHire", widget: "boolean", default: true }
153+
- label: "Badges"
154+
name: "badges"
155+
widget: "list"
156+
fields:
157+
- { label: "Label", name: "text", widget: "string" }
158+
- label: "Icon"
159+
name: "icon"
160+
widget: "select"
161+
options: ["Coffee", "Heart", "Zap", "Award", "Star", "Target", "TrendingUp"]
162+
- { label: "Color Class", name: "color", widget: "string", default: "text-orange-400" }
139163
- label: "Social Links"
140164
name: "social"
141165
widget: "object"
@@ -157,7 +181,7 @@ collections:
157181
- { label: "Company", name: "company", widget: "string" }
158182
- { label: "Duration", name: "duration", widget: "string" }
159183
- { label: "Description", name: "description", widget: "text" }
160-
- { label: "Technologies", name: "technologies", widget: "list", required: false }
184+
- label: "Technologies", name: "technologies", widget: "list", required: false, field: { label: "Tech", widget: "string" }
161185

162186
- name: "education"
163187
label: "Academic Background"
@@ -174,6 +198,30 @@ collections:
174198
- { label: "Details", name: "details", widget: "text" }
175199
- { label: "Achievement", name: "achievement", widget: "string" }
176200
- { label: "Status", name: "status", widget: "select", options: ["Current", "Completed"] }
201+
- label: "Key Achievements"
202+
name: "achievements"
203+
widget: "list"
204+
fields:
205+
- { label: "Text", name: "text", widget: "string" }
206+
- label: "Icon"
207+
name: "icon"
208+
widget: "select"
209+
options: ["Trophy", "Star", "Award", "Target", "TrendingUp"]
210+
- { label: "Color Class", name: "color", widget: "string", default: "text-orange-400" }
211+
212+
- name: "testimonials"
213+
label: "Testimonials"
214+
file: "client/src/content/testimonials.json"
215+
fields:
216+
- label: "Testimonials List"
217+
name: "testimonials"
218+
widget: "list"
219+
fields:
220+
- { label: "Name", name: "name", widget: "string" }
221+
- { label: "Role", name: "role", widget: "string" }
222+
- { label: "Feedback", name: "content", widget: "text" }
223+
- { label: "Avatar/Emoji", name: "avatar", widget: "string" }
224+
- { label: "Rating (1-5)", name: "rating", widget: "number", default: 5 }
177225

178226
- name: "team"
179227
label: "Team Section"
@@ -185,3 +233,11 @@ collections:
185233
- { label: "Description", name: "description", widget: "text" }
186234
- { label: "Team Name", name: "teamName", widget: "string" }
187235
- { label: "Team URL", name: "teamUrl", widget: "string" }
236+
237+
- name: "seo"
238+
label: "SEO Settings"
239+
file: "client/src/content/seo.json"
240+
fields:
241+
- { label: "Site Title", name: "title", widget: "string" }
242+
- { label: "Meta Description", name: "description", widget: "text" }
243+
- { label: "Keywords", name: "keywords", widget: "string" }

client/src/components/About.tsx

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
11
import { useState, useEffect } from "react";
22
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
3-
import { Github, Coffee, Heart, Zap, Linkedin } from "lucide-react";
3+
import { Github, Coffee, Heart, Zap, Linkedin, Award, Star, Target, TrendingUp } from "lucide-react";
44
import aboutData from "../content/about.json";
5-
import heroData from "../content/hero.json"; // Name/Title are in hero
5+
import heroData from "../content/hero.json";
6+
7+
const iconMap: Record<string, any> = {
8+
Coffee,
9+
Heart,
10+
Zap,
11+
Award,
12+
Star,
13+
Target,
14+
TrendingUp
15+
};
616

717
const userData = {
8-
name: heroData.headline,
9-
title: "Web Developer", // This isn't in JSON explicitly but "Web Developer" is common
10-
location: "Bhakkar, Pakistan", // Hardcoding or moving to JSON? Let's use hardcoded for now or add to about.json
11-
// Actually about.json only has bio/social.
12-
// Let's use standard placeholders or keep hardcoded simple things if strictly implied.
13-
// But user said "update any kind of information".
14-
// I should use variables from aboutData if possible or heroData.
18+
name: aboutData.name || heroData.headline,
19+
title: aboutData.title || "Web Developer",
20+
location: aboutData.location || "Bhakkar, Pakistan",
1521
bio: aboutData.bio,
1622
social: aboutData.social,
17-
availableForHire: aboutData.availableForHire
23+
availableForHire: aboutData.availableForHire,
24+
badges: aboutData.badges || [
25+
{ icon: "Coffee", text: "Tea Enthusiast", color: "text-orange-400" },
26+
{ icon: "Heart", text: "Open Source Learner", color: "text-red-400" },
27+
{ icon: "Zap", text: "Problem Solver", color: "text-yellow-400" }
28+
]
1829
};
1930

2031
export function About() {
@@ -44,18 +55,15 @@ export function About() {
4455
<p className="text-lg text-neutral-300 leading-relaxed">{userData.bio}</p>
4556

4657
<div className="flex flex-wrap gap-4">
47-
<div className="flex items-center space-x-2 bg-neutral-900/50 px-4 py-2 rounded-lg border border-neutral-800">
48-
<Coffee className="w-5 h-5 text-orange-400" />
49-
<span className="text-neutral-300">Tea Enthusiast</span>
50-
</div>
51-
<div className="flex items-center space-x-2 bg-neutral-900/50 px-4 py-2 rounded-lg border border-neutral-800">
52-
<Heart className="w-5 h-5 text-red-400" />
53-
<span className="text-neutral-300">Open Source Learner</span>
54-
</div>
55-
<div className="flex items-center space-x-2 bg-neutral-900/50 px-4 py-2 rounded-lg border border-neutral-800">
56-
<Zap className="w-5 h-5 text-yellow-400" />
57-
<span className="text-neutral-300">Problem Solver</span>
58-
</div>
58+
{userData.badges.map((badge, idx) => {
59+
const Icon = iconMap[badge.icon] || Zap;
60+
return (
61+
<div key={idx} className="flex items-center space-x-2 bg-neutral-900/50 px-4 py-2 rounded-lg border border-neutral-800">
62+
<Icon className={`w-5 h-5 ${badge.color}`} />
63+
<span className="text-neutral-300">{badge.text}</span>
64+
</div>
65+
);
66+
})}
5967
</div>
6068

6169
{/* Social Links - Only GitHub */}
@@ -110,7 +118,7 @@ export function About() {
110118
<span className="text-green-400">'JavaScript'</span>],
111119
</div>
112120
<div className="text-neutral-400 ml-4">
113-
passion: <span className="text-green-400">'Building amazing things'</span>
121+
passion: <span className="text-green-400">'{aboutData.passion || 'Building amazing things'}'</span>
114122
</div>
115123
<div className="text-purple-400">{"}"}</div>
116124
</div>

client/src/components/Contact.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { useToast } from "@/hooks/use-toast";
77
import { useIntersectionObserver } from "@/hooks/use-intersection-observer";
88
import { Mail, Github, MapPin, Send, Loader2, Phone, CheckCircle } from "lucide-react";
99

10+
import aboutData from "../content/about.json";
1011

1112
interface ContactFormData {
1213
name: string;
@@ -19,25 +20,25 @@ const contactInfo = [
1920
{
2021
icon: Mail,
2122
title: "Email",
22-
value: "mabdulrasheedtalal@gmail.com",
23-
link: "mailto:mabdulrasheedtalal@gmail.com"
23+
value: aboutData.social.email,
24+
link: `mailto:${aboutData.social.email}`
2425
},
2526
{
2627
icon: Phone,
2728
title: "Phone",
28-
value: "03361115907",
29-
link: "tel:03361115907"
29+
value: aboutData.social.phone,
30+
link: `tel:${aboutData.social.phone}`
3031
},
3132
{
3233
icon: Github,
3334
title: "GitHub",
34-
value: "@Abdul-Rasheed-Talal",
35-
link: "https://github.com/Abdul-Rasheed-Talal"
35+
value: `@${aboutData.social.github.split('/').pop()}`,
36+
link: aboutData.social.github
3637
},
3738
{
3839
icon: MapPin,
3940
title: "Location",
40-
value: "Bhakkar, Pakistan",
41+
value: aboutData.location,
4142
link: null
4243
}
4344
];

client/src/components/Education.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,24 @@ import educationJson from "../content/education.json";
55

66
const educationData = educationJson.education;
77

8-
const achievements = [
9-
{ icon: Trophy, text: "2nd Position in PBTE Board", color: "text-yellow-400" },
10-
{ icon: Star, text: "97% in DAE 1st Year", color: "text-orange-400" },
11-
{ icon: Award, text: "95% in Matriculation", color: "text-blue-400" },
12-
{ icon: Target, text: "5+ Projects Completed", color: "text-green-400" },
13-
];
8+
const iconMap: Record<string, any> = {
9+
GraduationCap,
10+
Award,
11+
Trophy,
12+
Star,
13+
Target,
14+
TrendingUp
15+
};
16+
17+
const achievements = (educationJson.achievements || [
18+
{ icon: "Trophy", text: "2nd Position in PBTE Board", color: "text-yellow-400" },
19+
{ icon: "Star", text: "97% in DAE 1st Year", color: "text-orange-400" },
20+
{ icon: "Award", text: "95% in Matriculation", color: "text-blue-400" },
21+
{ icon: "Target", text: "5+ Projects Completed", color: "text-green-400" },
22+
]).map(a => ({
23+
...a,
24+
icon: typeof a.icon === 'string' ? iconMap[a.icon] || Award : a.icon
25+
}));
1426

1527
export function Education() {
1628
const { ref, isIntersecting } = useIntersectionObserver({ threshold: 0.2 });

client/src/components/ExperimentLab.tsx

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export function ExperimentLab() {
1212
isCVModalOpen,
1313
openCVPreview,
1414
openDiagnostics,
15-
activeDevSidebar
15+
activeDevSidebar,
16+
isMobile
1617
} = useMode();
1718

1819
const experiments = [
@@ -32,7 +33,8 @@ export function ExperimentLab() {
3233
action: toggleMode,
3334
active: mode === 'developer',
3435
color: "from-blue-500 to-indigo-700",
35-
cta: mode === 'developer' ? "Exit Dev Mode" : "Enter Dev Mode"
36+
cta: isMobile ? "Desktop Only" : (mode === 'developer' ? "Exit Dev Mode" : "Enter Dev Mode"),
37+
disabled: isMobile
3638
},
3739
{
3840
title: "System Diagnostics",
@@ -41,7 +43,8 @@ export function ExperimentLab() {
4143
action: openDiagnostics,
4244
active: mode === 'developer' && activeDevSidebar === 'debug',
4345
color: "from-purple-500 to-pink-700",
44-
cta: "Open Diagnostics"
46+
cta: isMobile ? "Desktop Only" : "Open Diagnostics",
47+
disabled: isMobile
4548
},
4649
{
4750
title: "Premium CV Engine",
@@ -85,7 +88,7 @@ export function ExperimentLab() {
8588
initial={{ opacity: 0, y: 20 }}
8689
whileInView={{ opacity: 1, y: 0 }}
8790
transition={{ delay: idx * 0.1 }}
88-
className={`group relative p-8 rounded-3xl border border-neutral-800 transition-all duration-500 hover:border-neutral-600 overflow-hidden flex flex-col h-full bg-neutral-900/40 backdrop-blur-sm`}
91+
className={`group relative p-8 rounded-3xl border border-neutral-800 transition-all duration-500 hover:border-neutral-600 overflow-hidden flex flex-col h-full bg-neutral-900/40 backdrop-blur-sm ${(exp as any).disabled ? 'opacity-75' : ''}`}
8992
>
9093
{/* Card Background Gradient */}
9194
<div className={`absolute inset-0 opacity-0 group-hover:opacity-10 transition-opacity bg-gradient-to-br ${exp.color}`} />
@@ -96,17 +99,25 @@ export function ExperimentLab() {
9699

97100
<h3 className="text-xl font-bold mb-3 group-hover:text-white transition-colors">
98101
{exp.title}
102+
{isMobile && (exp as any).disabled && (
103+
<span className="ml-2 text-[10px] font-mono text-orange-500 uppercase tracking-tighter">Gated</span>
104+
)}
99105
</h3>
100106

101107
<p className="text-neutral-400 text-sm mb-8 flex-1 leading-relaxed">
102108
{exp.description}
109+
{isMobile && (exp as any).disabled && (
110+
<span className="block mt-2 text-xs text-orange-400/70 italic">Best experienced on desktop.</span>
111+
)}
103112
</p>
104113

105114
<button
106115
onClick={exp.action}
107116
className={`w-full py-3 rounded-xl font-bold text-sm transition-all duration-300 transform active:scale-95 flex items-center justify-center gap-2 ${exp.active
108117
? "bg-white text-black shadow-white/10"
109-
: "bg-neutral-800 text-neutral-300 hover:bg-neutral-700"
118+
: (exp as any).disabled
119+
? "bg-neutral-800/50 text-neutral-500 cursor-not-allowed"
120+
: "bg-neutral-800 text-neutral-300 hover:bg-neutral-700"
110121
}`}
111122
>
112123
{exp.active && <Rocket className="w-4 h-4 animate-bounce" />}

client/src/components/Footer.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import { Github, Mail, Heart, Linkedin } from "lucide-react";
22

33

4+
import aboutData from "../content/about.json";
5+
46
const socialLinks = [
57
{
68
icon: Github,
7-
href: "https://github.com/Abdul-Rasheed-Talal",
9+
href: aboutData.social.github,
810
label: "GitHub"
911
},
1012
{
1113
icon: Linkedin,
12-
href: "https://www.linkedin.com/in/abdulrasheedtalal/",
14+
href: aboutData.social.linkedin,
1315
label: "LinkedIn"
1416
},
1517
{
1618
icon: Mail,
17-
href: "mailto:mabdulrasheedtalal@gmail.com",
19+
href: `mailto:${aboutData.social.email}`,
1820
label: "Email"
1921
}
2022
];

client/src/components/Hero.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Github, Mail, MapPin, Phone, Download, Send, Linkedin } from "lucide-re
44
import heroData from "../content/hero.json";
55
import aboutData from "../content/about.json";
66

7-
const typingTexts = [
7+
const typingTexts = heroData.typingTexts || [
88
"Web Developer",
99
"CIT Student",
1010
"Problem Solver",

client/src/components/Navigation.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import { CVPreviewModal } from "./CVPreviewModal";
88
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
99
import { useToast } from "@/hooks/use-toast";
1010

11-
const navItems = [
11+
import heroData from "../content/hero.json";
12+
13+
const navItems = heroData.navItems || [
1214
{ href: "#home", label: "HOME" },
1315
{ href: "#projects", label: "PROJECTS" },
1416
{ href: "#skills", label: "EXPERTISE" },
@@ -21,7 +23,7 @@ export function Navigation() {
2123
const { mode, toggleMode, isCVModalOpen, setIsCVModalOpen } = useMode();
2224
const [activeSection, setActiveSection] = useState("home");
2325
const [isScrolled, setIsScrolled] = useState(false);
24-
const cvUrl = "/assets/Abdul-Rasheed-internship-CV.pdf";
26+
const cvUrl = heroData.resumeUrl || "/assets/Abdul-Rasheed-internship-CV.pdf";
2527
const { toast } = useToast();
2628

2729
// Check for mobile and disable dev mode
@@ -131,7 +133,7 @@ export function Navigation() {
131133
<span>Preview CV</span>
132134
</DropdownMenuItem>
133135
<DropdownMenuItem className="hover:bg-neutral-800 focus:bg-neutral-800 cursor-pointer py-2.5" asChild>
134-
<a href="/assets/Abdul-Rasheed-internship-CV.pdf" download className="flex items-center gap-2">
136+
<a href={cvUrl} download className="flex items-center gap-2">
135137
<Download className="w-4 h-4 text-orange-400" />
136138
<span>Download CV</span>
137139
</a>

0 commit comments

Comments
 (0)