CoolFace
Datasetpublic

dshunt/next18-anniversary-patch

The Next 18 — anniversary-intimacy completion patch Drop-in files that finish the anniversary-intimacy app on branch couple of dolonhunt/romantic-partners-app. They close the three critical gaps found in review: survey/writing answers lost on refresh, the partner reveal never wired into any flow, and no day locking — plus onboarding, games played-tracking, honest media cards, and the Capacitor CLI/core version alignment. Contents File Goes to Action… See the full description on the dataset page: https://huggingface.co/datasets/dshunt/next18-anniversary-patch.

sourceHugging Faceupdated 15d agoView on Hugging Face
0likes53downloads
CourseTimeline.tsx145 linesDownload Raw Back to root
1import { motion, AnimatePresence } from 'framer-motion';2import { useState } from 'react';3import { ChevronDown, ChevronUp, CheckCircle2, Lock, Play } from 'lucide-react';4import { courseDays } from '../data/courseData';5 6interface CourseTimelineProps {7  onBeginDay: (day: number) => void;8  onCompleteDay: (day: number) => void;9  completedDays: Set<number>;10}11 12const typeColors: Record<string,string> = {13  game:'from-amber-500/80 to-orange-400/80', survey:'from-blue-500/80 to-cyan-400/80',14  prompt:'from-purple-500/80 to-pink-400/80', learning:'from-emerald-500/80 to-teal-400/80',15  media:'from-rose-500/80 to-red-400/80', reflection:'from-indigo-500/80 to-violet-400/80',16};17const typeLabels: Record<string,string> = { game:'Game', survey:'Survey', prompt:'Writing', learning:'Learning', media:'Media', reflection:'Reflection' };18 19export default function CourseTimeline({ onBeginDay, onCompleteDay, completedDays }: CourseTimelineProps) {20  const [expandedDay, setExpandedDay] = useState<number|null>(null);21  const [week, setWeek] = useState<1|2>(1);22  const days = courseDays.filter(d => week===1 ? d.day<=7 : d.day>7);23  const isLocked = (d: number) => d > 1 && !completedDays.has(d - 1);24 25  return (26    <section id="timeline" className="section-breathe px-4 md:px-8 max-w-5xl mx-auto">27      {/* Header */}28      <motion.div initial={{opacity:0,y:40}} whileInView={{opacity:1,y:0}} viewport={{once:true}} transition={{duration:0.8}}29        className="text-center mb-12 md:mb-20">30        <h2 className="text-3xl md:text-6xl font-light font-display text-gradient-champagne mb-4 md:mb-6 tracking-[0.03em]">Your 14-Day Journey</h2>31        <p className="text-champagne/20 text-sm md:text-base max-w-md mx-auto font-light leading-relaxed tracking-wide">32          Two weeks of escalating exploration — each day unlocks when the previous one is complete33        </p>34      </motion.div>35 36      {/* Week toggle */}37      <div className="flex justify-center mb-10 md:mb-14">38        <div className="glass rounded-full p-1 flex gap-1">39          {([1,2] as const).map(w => (40            <button key={w} onClick={()=>{setWeek(w);setExpandedDay(null)}}41              className={`px-5 md:px-8 py-3 md:py-4 rounded-full text-xs md:text-sm font-medium tracking-[0.1em] uppercase transition-all duration-500 min-w-[110px] md:min-w-[140px] ${42                week===w ? 'btn-primary rounded-full shadow-lg shadow-ember/20' : 'text-champagne/25 hover:text-champagne/40'43              }`}>44              Week {w}45            </button>46          ))}47        </div>48      </div>49 50      {/* Progress */}51      <div className="mb-10 md:mb-14">52        <div className="flex justify-between text-[10px] text-champagne/15 mb-3 tracking-[0.15em] uppercase">53          <span>{completedDays.size} of 14 completed</span>54          <span>{Math.round((completedDays.size/14)*100)}%</span>55        </div>56        <div className="h-1 rounded-full overflow-hidden" style={{background:'rgba(43,27,46,0.3)'}}>57          <motion.div className="h-full rounded-full" style={{background:'linear-gradient(90deg, #9B2335, #B76E79, #E8C9A0)'}}58            initial={{width:0}} animate={{width:`${(completedDays.size/14)*100}%`}} transition={{duration:1.2, ease:[0.23,1,0.32,1]}} />59        </div>60      </div>61 62      {/* Days */}63      <div className="space-y-4 md:space-y-5">64        <AnimatePresence mode="wait">65          {days.map((day, i) => {66            const open = expandedDay===day.day;67            const done = completedDays.has(day.day);68            const locked = isLocked(day.day);69            return (70              <motion.div key={day.day} initial={{opacity:0,y:24}} animate={{opacity:1,y:0}} exit={{opacity:0}}71                transition={{duration:0.4, delay:i*0.05}}72                className={`noir-card overflow-hidden ${locked?'opacity-40':''}`}>73 74                <button className="w-full p-5 md:p-7 text-left flex items-center gap-4 md:gap-5"75                  onClick={()=>{ if(!locked) setExpandedDay(open?null:day.day); }}>76                  <div className="w-14 h-14 md:w-16 md:h-16 rounded-2xl flex items-center justify-center text-2xl md:text-3xl shrink-0 transition-all duration-500"77                    style={done ? {background:'linear-gradient(135deg, #B76E79, #E8C9A0)'}78                      : locked ? {background:'rgba(43,27,46,0.3)', border:'1px solid rgba(183,110,121,0.06)'}79                      : {background:'rgba(43,27,46,0.3)', border:'1px solid rgba(183,110,121,0.12)'}}>80                    {done ? <CheckCircle2 className="w-7 h-7 md:w-8 md:h-8" style={{color:'#2B1B2E'}}/>81                      : locked ? <Lock className="w-5 h-5 text-champagne/30"/>82                      : day.icon}83                  </div>84 85                  <div className="flex-1 min-w-0">86                    <div className="flex items-center gap-2 mb-1.5">87                      <span className="text-[9px] md:text-[10px] font-semibold text-champagne/15 uppercase tracking-[0.18em]">Day {day.day}</span>88                      <span className={`px-2 py-0.5 rounded-full text-[8px] md:text-[9px] font-semibold bg-gradient-to-r ${typeColors[day.type]} text-white/80`}>89                        {typeLabels[day.type]}90                      </span>91                    </div>92                    <h3 className="text-base md:text-xl font-display font-normal text-champagne/70 mb-0.5">{day.title}</h3>93                    <p className="text-[11px] md:text-xs text-champagne/15 font-light">94                      {locked ? `🔒 Complete Day ${day.day-1} to unlock` : day.subtitle}95                    </p>96                  </div>97 98                  <div className="shrink-0 text-champagne/10 p-1">99                    {locked ? <Lock className="w-4 h-4"/> : open ? <ChevronUp className="w-4 h-4"/> : <ChevronDown className="w-4 h-4" />}100                  </div>101                </button>102 103                <AnimatePresence>{open && (104                  <motion.div initial={{height:0,opacity:0}} animate={{height:'auto',opacity:1}} exit={{height:0,opacity:0}}105                    transition={{duration:0.4}} className="overflow-hidden">106                    <div className="px-5 md:px-7 pb-6 md:pb-8 pt-3 border-t" style={{borderColor:'rgba(183,110,121,0.04)'}}>107                      <p className="text-xs md:text-sm text-champagne/25 mb-6 leading-[1.8] font-light">{day.description}</p>108                      <div className="space-y-3 mb-6">109                        {day.content.map((item,j) => (110                          <motion.div key={j} initial={{opacity:0,x:-6}} animate={{opacity:1,x:0}} transition={{delay:j*0.04}}111                            className="flex items-start gap-3 text-xs md:text-sm">112                            <span className="text-rose-gold/20 mt-1 shrink-0 text-[8px]">◆</span>113                            <span className="text-champagne/30 font-light">{item}</span>114                          </motion.div>115                        ))}116                      </div>117                      {day.tips && (118                        <div className="rounded-2xl p-4 md:p-5 mb-5" style={{background:'rgba(74,14,31,0.12)', border:'1px solid rgba(155,35,53,0.06)'}}>119                          <p className="text-[9px] font-semibold text-rose-gold/30 uppercase tracking-[0.18em] mb-2">Tips</p>120                          {day.tips.map((t,j)=> <p key={j} className="text-[11px] text-champagne/20 mb-1.5 font-light">💡 {t}</p>)}121                        </div>122                      )}123                      <div className="flex gap-3">124                        <button onClick={()=>onBeginDay(day.day)}125                          className="btn-magnetic btn-primary flex-1 py-3.5 md:py-4 rounded-xl text-xs md:text-sm tracking-[0.08em] uppercase flex items-center justify-center gap-2">126                          <Play className="w-3.5 h-3.5"/>{done ? 'Review Day' : 'Begin Day'}127                        </button>128                        {!done && (129                          <button onClick={()=>onCompleteDay(day.day)}130                            className="btn-magnetic btn-ghost px-5 py-3.5 md:py-4 rounded-xl text-xs md:text-sm uppercase">131                            ✓ Complete132                          </button>133                        )}134                      </div>135                    </div>136                  </motion.div>137                )}</AnimatePresence>138              </motion.div>139            );140          })}141        </AnimatePresence>142      </div>143    </section>144  );145}