@@ -3,13 +3,14 @@ import { useState, useMemo, useEffect } from "react";
33import { motion , AnimatePresence } from "framer-motion" ;
44import {
55 FiArrowLeft , FiArrowRight , FiCheck , FiStar , FiBookOpen , FiAward , FiClock ,
6- FiImage , FiX , FiExternalLink ,
6+ FiImage , FiX , FiExternalLink , FiGift ,
77} from "react-icons/fi" ;
88import { profile , topics , formats , tierMeta , plans } from "@/data/trainingPlans" ;
9+ import { getIcon } from "@/components/icons" ;
910import CustomCursor from "@/components/CustomCursor" ;
1011import ScrollProgress from "@/components/ScrollProgress" ;
1112
12- const TIER_ORDER = [ "basics" , "advanced" , "pro" , "expert" ] ;
13+ const TIER_ORDER = [ "basics" , "advanced" , "pro" , "expert" , "onrequest" ] ;
1314
1415function 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'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'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