Skip to content

Commit 951c066

Browse files
committed
Digital Transformation Upgrade
1 parent c895061 commit 951c066

17 files changed

Lines changed: 613 additions & 203 deletions

app/training/page.jsx

Lines changed: 104 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { useState, useMemo, useEffect } from "react";
33
import { motion, AnimatePresence } from "framer-motion";
44
import {
55
FiArrowLeft, FiArrowRight, FiCheck, FiStar, FiBookOpen, FiAward, FiClock,
6-
FiImage, FiX, FiExternalLink,
6+
FiImage, FiX, FiExternalLink, FiGift,
77
} from "react-icons/fi";
88
import { profile, topics, formats, tierMeta, plans } from "@/data/trainingPlans";
9+
import { getIcon } from "@/components/icons";
910
import CustomCursor from "@/components/CustomCursor";
1011
import ScrollProgress from "@/components/ScrollProgress";
1112

12-
const TIER_ORDER = ["basics", "advanced", "pro", "expert"];
13+
const TIER_ORDER = ["basics", "advanced", "pro", "expert", "onrequest"];
1314

1415
function bookLink(topicLabel, formatLabel, tierLabel) {
1516
const msg = encodeURIComponent(
@@ -63,7 +64,7 @@ export default function TrainingPage() {
6364
const [topic, setTopic] = useState(topics[0].key);
6465
const [format, setFormat] = useState(topics[0].formats[0]);
6566
const [tier, setTier] = useState("basics");
66-
const [material, setMaterial] = useState(null); // { src, title } | null
67+
const [material, setMaterial] = useState(null);
6768

6869
const topicObj = topics.find((t) => t.key === topic);
6970
const availableFormats = topicObj.formats;
@@ -72,16 +73,23 @@ export default function TrainingPage() {
7273
[topic, format]
7374
);
7475

76+
const pickTopic = (k) => {
77+
const tObj = topics.find((x) => x.key === k);
78+
const newFormat = tObj.formats[0];
79+
const tiers = TIER_ORDER.filter((tt) => plans[k]?.[newFormat]?.[tt]);
80+
setTopic(k); setFormat(newFormat);
81+
setTier(tiers.includes(tier) ? tier : tiers[0]);
82+
};
7583
const pickFormat = (f) => {
7684
const tiers = TIER_ORDER.filter((tt) => plans[topic]?.[f]?.[tt]);
7785
setFormat(f);
7886
setTier(tiers.includes(tier) ? tier : tiers[0]);
7987
};
8088

8189
const plan = plans[topic]?.[format]?.[tier];
82-
const isCorporate = format === "corporate";
8390
const formatObj = formats[format];
84-
const cardTitle = `${topicObj.label} · ${formatObj.label} · ${tierMeta[tier].label}`;
91+
const isPhased = Boolean(plan?.isPhased);
92+
const cardTitle = `${topicObj.label} · ${formatObj.label} · ${tierMeta[tier]?.label ?? ""}`;
8593

8694
return (
8795
<>
@@ -96,42 +104,59 @@ export default function TrainingPage() {
96104
<a href="/" data-hover className="inline-flex items-center gap-2 text-sm font-semibold px-5 py-2.5 rounded-full border border-line hover:border-accent hover:text-accent transition-colors">
97105
<FiArrowLeft size={16} /> Back to home
98106
</a>
99-
<h1 className="text-3xl sm:text-5xl font-extrabold mt-6">Microsoft 365 <span className="text-gradient">Training</span></h1>
100-
<p className="text-muted mt-4 max-w-2xl">Choose your format & package. Preview the training material or book directly.</p>
107+
<h1 className="text-3xl sm:text-5xl font-extrabold mt-6">Training <span className="text-gradient">Catalogue</span></h1>
108+
<p className="text-muted mt-4 max-w-2xl">Choose an application, your format, then a package.</p>
101109
</div>
102110
</section>
103111

104112
<section className="py-12">
105113
<div className="container">
106-
{/* format */}
107-
<p className="text-xs font-mono text-accent mb-3">1 · CHOOSE YOUR FORMAT</p>
114+
{/* 1 · APPLICATION */}
115+
<p className="text-xs font-mono text-accent mb-3">1 · CHOOSE APPLICATION</p>
116+
<div className="flex flex-wrap gap-3">
117+
{topics.map((t) => {
118+
const Icon = getIcon(t.icon); const on = t.key === topic;
119+
return (
120+
<button key={t.key} data-hover onClick={() => pickTopic(t.key)}
121+
className={`flex items-center gap-2 px-5 py-3 rounded-2xl border text-sm font-bold transition-all ${on ? "border-accent bg-accent/10 text-accent" : "border-line text-muted hover:border-accent/50 hover:text-text"}`}>
122+
<Icon size={18} /> {t.label}
123+
</button>
124+
);
125+
})}
126+
</div>
127+
<p className="text-muted text-sm mt-2">{topicObj.blurb}</p>
128+
129+
{/* 2 · FORMAT */}
130+
<p className="text-xs font-mono text-accent mt-8 mb-3">2 · CHOOSE FORMAT</p>
108131
<div className="flex flex-wrap gap-3">
109132
{availableFormats.map((f) => {
110-
const on = f === format;
133+
const Icon = getIcon(formats[f].icon); const on = f === format;
111134
return (
112135
<button key={f} data-hover onClick={() => pickFormat(f)}
113-
className={`px-4 py-2.5 rounded-xl border text-sm font-semibold transition-all ${on ? "border-accent bg-accent/10 text-accent" : "border-line text-muted hover:border-accent/50 hover:text-text"}`}>
114-
{formats[f].label}
136+
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl border text-sm font-semibold transition-all ${on ? "border-accent bg-accent/10 text-accent" : "border-line text-muted hover:border-accent/50 hover:text-text"}`}>
137+
<Icon size={16} /> {formats[f].label}
115138
</button>
116139
);
117140
})}
118141
</div>
142+
<p className="text-muted text-sm mt-2">{formatObj.blurb}</p>
119143

120-
{/* tier */}
121-
<p className="text-xs font-mono text-accent mt-8 mb-3">2 · CHOOSE YOUR PACKAGE</p>
144+
{/* 3 · PACKAGE */}
145+
<p className="text-xs font-mono text-accent mt-8 mb-3">3 · CHOOSE PACKAGE</p>
122146
<div className="inline-flex flex-wrap gap-2 p-1.5 rounded-2xl border border-line bg-surface/50">
123147
{availableTiers.map((k) => {
124148
const on = k === tier;
125149
return (
126150
<button key={k} data-hover onClick={() => setTier(k)}
127151
className={`px-4 py-2.5 rounded-xl text-sm font-semibold transition-all ${on ? "bg-gradient-to-r from-accent to-accent2 text-bg shadow" : "text-muted hover:text-text"}`}>
128-
{tierMeta[k].label}<span className={`ml-1.5 text-[11px] font-mono ${on ? "text-bg/80" : "text-muted"}`}>{tierMeta[k].mins}</span>
152+
{tierMeta[k].label}
153+
<span className={`ml-1.5 text-[11px] font-mono ${on ? "text-bg/80" : "text-muted"}`}>{tierMeta[k].mins}</span>
129154
</button>
130155
);
131156
})}
132157
</div>
133158

134-
{/* detail card */}
159+
{/* DETAIL CARD */}
135160
<div className="mt-8">
136161
<AnimatePresence mode="wait">
137162
{plan && (
@@ -140,37 +165,83 @@ export default function TrainingPage() {
140165
{plan.popular && (
141166
<span className="absolute -top-3 left-8 text-xs font-semibold px-3 py-1 rounded-full bg-accent text-bg flex items-center gap-1"><FiStar size={12} /> Most Popular</span>
142167
)}
168+
143169
<div className="flex flex-wrap items-start justify-between gap-4">
144170
<div>
145171
<h2 className="text-2xl font-extrabold">{cardTitle}</h2>
146172
<p className="text-muted text-sm mt-1">{plan.ideal}</p>
147-
<p className="text-xs font-mono text-accent mt-2 flex items-center gap-1.5"><FiClock size={13} /> {tierMeta[tier].mins} · live online</p>
173+
<p className="text-xs font-mono text-accent mt-2 flex items-center gap-1.5"><FiClock size={13} /> {tierMeta[tier].mins}</p>
148174
</div>
149175
<div className="text-4xl font-extrabold text-gradient">{plan.price}</div>
150176
</div>
151177

152-
<div className="mt-6 grid md:grid-cols-3 gap-6">
153-
<div>
154-
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={13} /></span>{isCorporate ? "Deliverables" : "What's Included"}</h3>
155-
<ul className="space-y-2">{plan.includes.map((f) => (<li key={f} className="flex items-start gap-2 text-sm"><span className="mt-0.5 w-4 h-4 shrink-0 rounded bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={10} /></span><span className="text-muted">{f}</span></li>))}</ul>
156-
</div>
157-
<div>
158-
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiBookOpen size={13} /></span>{isCorporate ? "Phase Activities" : "What We'll Cover"}</h3>
159-
<ul className="space-y-2">{plan.modules.map((m, k) => (<li key={m} className="flex gap-2.5 text-sm"><span className="mt-0.5 w-5 h-5 shrink-0 rounded bg-accent/15 text-accent text-[10px] font-bold flex items-center justify-center">{k + 1}</span><span className="text-muted">{m}</span></li>))}</ul>
160-
</div>
161-
<div>
162-
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiAward size={13} /></span>Outcomes</h3>
163-
<ul className="space-y-2">{plan.outcomes.map((o) => (<li key={o} className="flex items-start gap-2 text-sm"><span className="mt-0.5 w-4 h-4 shrink-0 rounded bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={9} /></span><span className="text-muted">{o}</span></li>))}</ul>
178+
{/* ---------- CORPORATE: 4-PHASE VIEW ---------- */}
179+
{isPhased ? (
180+
<>
181+
{plan.summary && <p className="text-muted mt-5 max-w-3xl">{plan.summary}</p>}
182+
183+
<div className="mt-6">
184+
<div className="text-[11px] font-mono text-accent/70 mb-4">THE 4-PHASE PROGRAMME</div>
185+
<div className="grid sm:grid-cols-2 lg:grid-cols-4 gap-4">
186+
{plan.phases.map((p, i) => (
187+
<motion.div key={p.t} data-hover
188+
initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }}
189+
transition={{ delay: 0.1 + i * 0.08, duration: 0.35 }}
190+
whileHover={{ y: -4 }}
191+
className={`relative rounded-2xl p-5 border transition-colors ${p.free ? "border-emerald-500/50 bg-emerald-500/5" : "border-line bg-surface/60 hover:border-accent/60"}`}>
192+
{/* free badge */}
193+
{p.free && (
194+
<span className="absolute -top-3 left-4 text-[10px] font-bold px-2.5 py-1 rounded-full bg-emerald-500 text-white flex items-center gap-1">
195+
<FiGift size={11} /> FREE
196+
</span>
197+
)}
198+
<div className="flex items-center gap-2">
199+
<span className={`w-8 h-8 rounded-lg text-bg font-bold text-sm flex items-center justify-center ${p.free ? "bg-emerald-500" : "bg-gradient-to-br from-accent to-accent2"}`}>
200+
{p.n}
201+
</span>
202+
<h4 className="font-bold">{p.t}</h4>
203+
</div>
204+
<p className="text-[13px] text-muted mt-2 leading-relaxed">{p.d}</p>
205+
<ul className="mt-3 space-y-1.5">
206+
{p.acts.map((a) => (
207+
<li key={a} className="flex items-start gap-1.5 text-[11px] text-muted">
208+
<FiCheck size={11} className={`mt-0.5 shrink-0 ${p.free ? "text-emerald-500" : "text-accent"}`} /> {a}
209+
</li>
210+
))}
211+
</ul>
212+
{/* connector arrow */}
213+
{i < plan.phases.length - 1 && (
214+
<FiArrowRight className="hidden lg:block absolute -right-3 top-1/2 -translate-y-1/2 text-accent/40 z-10" size={16} />
215+
)}
216+
</motion.div>
217+
))}
218+
</div>
219+
</div>
220+
</>
221+
) : (
222+
/* ---------- STANDARD: includes / modules / outcomes ---------- */
223+
<div className="mt-6 grid md:grid-cols-3 gap-6">
224+
<div>
225+
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={13} /></span>What&apos;s Included</h3>
226+
<ul className="space-y-2">{plan.includes.map((f) => (<li key={f} className="flex items-start gap-2 text-sm"><span className="mt-0.5 w-4 h-4 shrink-0 rounded bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={10} /></span><span className="text-muted">{f}</span></li>))}</ul>
227+
</div>
228+
<div>
229+
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiBookOpen size={13} /></span>What We&apos;ll Cover</h3>
230+
<ul className="space-y-2">{plan.modules.map((m, k) => (<li key={m} className="flex gap-2.5 text-sm"><span className="mt-0.5 w-5 h-5 shrink-0 rounded bg-accent/15 text-accent text-[10px] font-bold flex items-center justify-center">{k + 1}</span><span className="text-muted">{m}</span></li>))}</ul>
231+
</div>
232+
<div>
233+
<h3 className="text-sm font-bold flex items-center gap-2 mb-3"><span className="w-6 h-6 rounded-md bg-accent/15 text-accent flex items-center justify-center"><FiAward size={13} /></span>Outcomes</h3>
234+
<ul className="space-y-2">{plan.outcomes.map((o) => (<li key={o} className="flex items-start gap-2 text-sm"><span className="mt-0.5 w-4 h-4 shrink-0 rounded bg-accent/15 text-accent flex items-center justify-center"><FiCheck size={9} /></span><span className="text-muted">{o}</span></li>))}</ul>
235+
</div>
164236
</div>
165-
</div>
237+
)}
166238

167-
{/* CTA row — Book + Training Material (if material exists) */}
239+
{/* CTA row */}
168240
<div className="mt-8 flex flex-wrap gap-4">
169241
<a href={bookLink(topicObj.label, formatObj.label, tierMeta[tier].label)} target="_blank" rel="noopener noreferrer" data-hover
170242
className="px-7 py-3.5 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition">
171-
{isCorporate ? "Request a proposal" : "Book this package"}
243+
{plan.cta || "Book this package"}
172244
</a>
173-
174245
{plan.material && (
175246
<button data-hover onClick={() => setMaterial({ src: plan.material, title: cardTitle })}
176247
className="px-7 py-3.5 rounded-full border border-accent text-accent font-semibold hover:bg-accent hover:text-bg transition-colors inline-flex items-center gap-2">

0 commit comments

Comments
 (0)