CCCPaste

MediCycle Zip Create

# Creating project files and zipping them for download.
from pathlib import Path, PurePosixPath
import json, zipfile, os, textwrap

OUT_DIR = Path('/mnt/data/medicycle_app')
OUT_DIR.mkdir(parents=True, exist_ok=True)

# index.html
index_html = """


  
  
  MediCycle — Medicine Reminder
  
  
  
  


  

  
© VKY Soft — MediCycle • Local-only (no backend) • Works on mobile & PC
""" # css/style.css css = """:root { --bg: #ffffff; --text: #111827; --card: #f8fafc; } .dark { --bg: #0b1220; --text: #e6eef8; --card: #0f1724; } body { background: var(--bg); color: var(--text); } .card { background: var(--card); } .app-shell { min-height: 100vh; display:flex; flex-direction:column; } .logo { font-weight:700; letter-spacing:0.6px; } .small-muted { font-size:0.85rem; color:rgba(0,0,0,0.55); } .time-pill { font-weight:600; } .cursor-pointer { cursor:pointer; } @media (max-width:576px){ .desktop-only{ display:none } } /* Dark-mode tweaks */ :root.dark .small-muted { color: rgba(255,255,255,0.6); } """ # js/app.js - improved from user's code with passkey check and delete profile js = r"""// ---------- Data model and localStorage helpers ---------- const STORAGE_KEY = 'medicycle_data_v1'; function defaultData(){ return { profiles: [], // {id, name, age, passkey, medicines: [med objects]} history: [], // {profileId, medId, medName, timeTakenISO, dosage} currentProfileId: null }; } function loadData(){ try{ const raw = localStorage.getItem(STORAGE_KEY); if(!raw) return defaultData(); return JSON.parse(raw); }catch(e){ console.error('loadData', e); return defaultData(); } } function saveData(data){ localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } // Unique id helper function uid(prefix='id'){ return prefix + '_' + Math.random().toString(36).slice(2,9); } // ---------- DOM refs ---------- const screenProfile = document.getElementById('screen-profile'); const screenApp = document.getElementById('screen-app'); const screenAdd = document.getElementById('screen-add'); const createProfileBtn = document.getElementById('createProfileBtn'); const addProfileBtn = document.getElementById('addProfileBtn'); const profilesList = document.getElementById('profilesList'); const profileTitle = document.getElementById('profileTitle'); const profileAgeEl = document.getElementById('profileAge'); const profileIdEl = document.getElementById('profileId'); const switchProfileBtn = document.getElementById('switchProfileBtn'); const deleteProfileBtn = document.getElementById('deleteProfileBtn'); const addMedicineBtn = document.getElementById('addMedicineBtn'); const medListAll = document.getElementById('medListAll'); const medListToday = document.getElementById('medListToday'); const historyList = document.getElementById('historyList'); const medForm = document.getElementById('medForm'); const medName = document.getElementById('medName'); const medDosage = document.getElementById('medDosage'); const medTime = document.getElementById('medTime'); const medCycle = document.getElementById('medCycle'); const cycleDetails = document.getElementById('cycleDetails'); const saveMedBtn = document.getElementById('saveMedBtn'); const cancelMedBtn = document.getElementById('cancelMedBtn'); const addTitle = document.getElementById('addTitle'); const toggleTheme = document.getElementById('toggleTheme'); const exportBtn = document.getElementById('exportBtn'); const importFile = document.getElementById('importFile'); // Tabs document.querySelectorAll('#mainTabs .nav-link').forEach(a => { a.addEventListener('click', ()=>{ document.querySelectorAll('#mainTabs .nav-link').forEach(x=>x.classList.remove('active')); a.classList.add('active'); document.querySelectorAll('.tab-pane').forEach(p=>p.style.display='none'); document.getElementById('tab-'+a.dataset.tab).style.display='block'; if(a.dataset.tab === 'history') renderHistory(); if(a.dataset.tab === 'today') renderToday(); if(a.dataset.tab === 'all') renderAll(); }); }); // ---------- Profile creation / selection UI ---------- function showCreateProfileForm(){ // Use simple built-in prompts for quick UX const name = prompt('Name:'); if(!name) return; const age = prompt('Age:'); if(!age) return; const passkey = prompt('Passkey (used to protect profile):'); if(!passkey) return; const data = loadData(); const id = uid('profile'); data.profiles.push({ id, name, age, passkey, medicines: [] }); data.currentProfileId = id; saveData(data); initApp(); } function renderProfiles(){ const data = loadData(); profilesList.innerHTML = ''; if(data.profiles.length===0){ document.getElementById('no-data-screen').style.display='block'; document.getElementById('select-profile-screen').style.display='none'; } else { document.getElementById('no-data-screen').style.display='none'; document.getElementById('select-profile-screen').style.display='block'; data.profiles.forEach(p=>{ const div = document.createElement('div'); div.className = 'd-flex align-items-center justify-content-between mb-2 card p-2'; div.innerHTML = `
${escapeHtml(p.name)}
Age ${p.age}
`; profilesList.appendChild(div); }); document.querySelectorAll('.select-profile').forEach(b=> b.addEventListener('click', async ()=>{ const id = b.dataset.id; const data = loadData(); const profile = data.profiles.find(x=>x.id===id); if(!profile) return alert('Profile not found'); // ask passkey const pass = prompt('Enter passkey for profile "'+profile.name+'":'); if(pass === null) return; if(pass === profile.passkey){ data.currentProfileId = id; saveData(data); initApp(); } else { alert('Wrong passkey'); } })); document.querySelectorAll('.delete-profile').forEach(b=> b.addEventListener('click', (e)=> { const id = b.dataset.id; const data = loadData(); const profile = data.profiles.find(x=>x.id===id); if(!profile) return; if(confirm('Delete profile "'+profile.name+'"? This will remove all medicines and history for this profile.')) { // remove profile and related history data.profiles = data.profiles.filter(p=> p.id !== id); data.history = data.history.filter(h=> h.profileId !== id); if(data.currentProfileId === id) data.currentProfileId = null; saveData(data); initApp(); } })); } } // ---------- App init / rendering ---------- let data = loadData(); let currentProfile = null; let editingMedId = null; function initApp(){ data = loadData(); if(!data || !data.profiles || data.profiles.length===0){ // show create profile screen screenProfile.style.display='block'; screenApp.style.display='none'; screenAdd.style.display='none'; renderProfiles(); return; } // if a currentProfileId exists, require passkey before opening automatically if(data.currentProfileId){ const profile = data.profiles.find(p=> p.id === data.currentProfileId); if(profile){ const pass = prompt('Enter passkey to open profile "'+profile.name+'": (cancel to choose another profile)'); if(pass === profile.passkey){ currentProfile = profile; openAppForProfile(); return; } else { // wrong or cancelled -> clear currentProfileId and show selector data.currentProfileId = null; saveData(data); screenProfile.style.display='block'; screenApp.style.display='none'; screenAdd.style.display='none'; renderProfiles(); return; } } else { data.currentProfileId = null; saveData(data); initApp(); return; } } else { // no current profile set -> show selector screenProfile.style.display='block'; screenApp.style.display='none'; screenAdd.style.display='none'; renderProfiles(); return; } } function openAppForProfile(){ // show dashboard screenProfile.style.display='none'; screenApp.style.display='block'; screenAdd.style.display='none'; profileTitle.textContent = currentProfile.name; profileAgeEl.textContent = currentProfile.age; profileIdEl.textContent = currentProfile.id; renderAll(); renderToday(); } // ---------- Render lists ---------- function renderAll(){ medListAll.innerHTML = ''; if(!currentProfile || currentProfile.medicines.length===0){ medListAll.innerHTML = '
No medicines added yet
'; return; } currentProfile.medicines.forEach(m=>{ const card = document.createElement('div'); card.className='card p-2 mb-2'; card.innerHTML = `
${escapeHtml(m.name)}
${escapeHtml(m.dosage)} • ${m.food==='after'? 'After Food':'Before Food'}
Time: ${m.time} • Cycle: ${cycleLabel(m)}
`; medListAll.appendChild(card); }); document.querySelectorAll('.edit-med').forEach(b=> b.addEventListener('click', ()=> openEditMedicine(b.dataset.id))); document.querySelectorAll('.del-med').forEach(b=> b.addEventListener('click', ()=> deleteMed(b.dataset.id))); } function renderToday(){ medListToday.innerHTML = ''; if(!currentProfile) return; const today = new Date(); const todays = currentProfile.medicines.filter(m=> isMedicineForDate(m, today)); if(todays.length===0){ medListToday.innerHTML = '
No medicines scheduled for today
'; return; } todays.forEach(m=>{ const card = document.createElement('div'); card.className='card p-2 mb-2 d-flex justify-content-between align-items-center'; // check if already taken today const taken = wasTakenToday(currentProfile.id, m.id); card.innerHTML = `
${escapeHtml(m.name)} ${taken? 'Taken': ''}
${escapeHtml(m.dosage)} • ${m.time} • ${m.food==='after'?'After Food':'Before Food'}
`; medListToday.appendChild(card); }); document.querySelectorAll('.mark-taken').forEach(b=> b.addEventListener('click', ()=> markTaken(b.dataset.id))); } function renderHistory(){ historyList.innerHTML = ''; const data = loadData(); const list = data.history.filter(h=> h.profileId === currentProfile.id).sort((a,b)=> new Date(b.timeTakenISO)-new Date(a.timeTakenISO)); if(list.length===0) { historyList.innerHTML = '
No history yet
'; return; } list.forEach(h=>{ const el = document.createElement('div'); el.className='card p-2 mb-2'; el.innerHTML = `
${escapeHtml(h.medName)}
${escapeHtml(h.dosage)} • ${new Date(h.timeTakenISO).toLocaleString()}
`; historyList.appendChild(el); }); document.querySelectorAll('.btn-copy').forEach(b=> b.addEventListener('click', ()=> navigator.clipboard.writeText(b.dataset.iso))); } // ---------- Helpers: cycle label, isMedicineForDate, taken checks ---------- function cycleLabel(m){ if(m.cycle==='daily') return 'Daily'; if(m.cycle==='monthly') return 'Monthly on ' + (m.monthDay||'?'); if(m.cycle==='weekly') return 'Weekly on ' + (m.weekDays? m.weekDays.join(', '):'?'); return ''; } function isMedicineForDate(m, date){ if(!m) return false; if(m.cycle==='daily') return true; if(m.cycle==='monthly'){ const d = Number(m.monthDay); return d === date.getDate(); } if(m.cycle==='weekly'){ const names = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; return m.weekDays && m.weekDays.includes(names[date.getDay()]); } return false; } function wasTakenToday(profileId, medId){ const d = loadData(); const today = (new Date()).toDateString(); return d.history.some(h=> h.profileId===profileId && h.medId===medId && new Date(h.timeTakenISO).toDateString()===today); } // ---------- Add/Edit/Delete medicine ---------- function openAddMedicine(){ editingMedId = null; addTitle.textContent = 'Add Medicine'; medForm.reset(); cycleDetails.innerHTML=''; medCycle.value='daily'; renderCycleDetails(); screenProfile.style.display='none'; screenApp.style.display='none'; screenAdd.style.display='block'; } function openEditMedicine(id){ const med = currentProfile.medicines.find(x=> x.id===id); if(!med) return; editingMedId = id; addTitle.textContent = 'Edit Medicine'; medName.value = med.name; medDosage.value = med.dosage; medTime.value = med.time; document.querySelectorAll('input[name="food"]').forEach(r=> r.checked = (r.value === med.food)); medCycle.value = med.cycle; renderCycleDetails(med); screenProfile.style.display='none'; screenApp.style.display='none'; screenAdd.style.display='block'; } function deleteMed(id){ if(!confirm('Delete this medicine?')) return; const data = loadData(); const profile = data.profiles.find(p=> p.id===data.currentProfileId); profile.medicines = profile.medicines.filter(m=> m.id!==id); saveData(data); initApp(); } medCycle.addEventListener('change', ()=> renderCycleDetails()); function renderCycleDetails(med=null){ const v = medCycle.value; if(v==='daily'){ cycleDetails.innerHTML = '
Will repeat every day
'; return; } if(v==='monthly'){ const val = med? (med.monthDay||'') : ''; cycleDetails.innerHTML = ``; return; } if(v==='weekly'){ const names = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; const selected = med && med.weekDays ? med.weekDays : []; cycleDetails.innerHTML = '' + names.map(n=> `
`).join(''); return; } } medForm.addEventListener('submit', (e)=>{ e.preventDefault(); saveMedicine(); }); cancelMedBtn.addEventListener('click', ()=> { screenAdd.style.display='none'; screenApp.style.display='block'; }); function saveMedicine(){ const name = medName.value.trim(); const dosage = medDosage.value.trim(); const time = medTime.value; const food = document.querySelector('input[name="food"]:checked').value; const cycle = medCycle.value; const data = loadData(); const profile = data.profiles.find(p=> p.id === data.currentProfileId); let medObj = editingMedId ? profile.medicines.find(m=> m.id===editingMedId) : null; if(!medObj){ medObj = { id: uid('med'), name, dosage, time, food, cycle }; profile.medicines.push(medObj); } else { medObj.name = name; medObj.dosage = dosage; medObj.time = time; medObj.food = food; medObj.cycle = cycle; } if(cycle==='monthly'){ medObj.monthDay = Number(document.getElementById('monthDay').value); } if(cycle==='weekly'){ medObj.weekDays = Array.from(document.querySelectorAll('.week-day:checked')).map(x=>x.value); } saveData(data); screenAdd.style.display='none'; screenApp.style.display='block'; initApp(); } // ---------- Mark taken ---------- function markTaken(medId){ const data = loadData(); const profile = data.profiles.find(p=> p.id===data.currentProfileId); const med = profile.medicines.find(m=> m.id===medId); data.history.push({ profileId: profile.id, medId: med.id, medName: med.name, dosage: med.dosage, timeTakenISO: new Date().toISOString() }); saveData(data); renderToday(); } // ---------- Export / Import ---------- exportBtn.addEventListener('click', ()=>{ const data = loadData(); const blob = new Blob([JSON.stringify(data, null, 2)], {type:'application/json'}); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'medicycle_data.json'; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }); importFile.addEventListener('change', async (e)=>{ const f = e.target.files[0]; if(!f) return; try{ const txt = await f.text(); const imported = JSON.parse(txt); if(!imported.profiles) throw new Error('Invalid file'); if(confirm('This will replace current data. Proceed?')){ localStorage.setItem(STORAGE_KEY, JSON.stringify(imported)); alert('Imported. Reloading view.'); initApp(); } }catch(err){ alert('Failed to import: '+err.message); } importFile.value=''; }); // ---------- Notifications ---------- let notifiedSet = new Set(); // in-memory, resets on reload function requestNotifPermission(){ if('Notification' in window) Notification.requestPermission().then(perm=> console.log('notification permission', perm)); } function checkReminders(){ const now = new Date(); const hhmm = now.toTimeString().slice(0,5); const data = loadData(); if(!data.currentProfileId) return; const profile = data.profiles.find(p=> p.id===data.currentProfileId); if(!profile) return; profile.medicines.forEach(m=>{ if(m.time === hhmm && isMedicineForDate(m, now)){ const key = profile.id + '|' + m.id + '|' + now.toDateString() + '|' + hhmm; if(notifiedSet.has(key)) return; notifiedSet.add(key); // show notification if('Notification' in window && Notification.permission === 'granted'){ new Notification('MediCycle: ' + m.name, { body: `${m.dosage} • ${m.food==='after'? 'After Food':'Before Food'}` }); } else { // fallback: console log console.log('Reminder:', m.name, m.dosage); } } }); } // check every 20 seconds setInterval(checkReminders, 20000); // ---------- Theme toggle ---------- function applyTheme(isDark){ if(isDark) document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark'); localStorage.setItem('medicycle_theme', isDark? 'dark':'light'); } toggleTheme.addEventListener('click', ()=>{ const isDark = document.documentElement.classList.toggle('dark'); localStorage.setItem('medicycle_theme', isDark? 'dark':'light'); }); // apply persisted theme (function(){ const t = localStorage.getItem('medicycle_theme'); if(t==='dark') applyTheme(true); else applyTheme(false); })(); // ---------- Utility: escapeHtml ---------- function escapeHtml(s){ return String(s).replace(/&/g,'&').replace(//g,'>'); } // ---------- Buttons wiring ---------- createProfileBtn.addEventListener('click', showCreateProfileForm); addProfileBtn.addEventListener('click', showCreateProfileForm); switchProfileBtn.addEventListener('click', ()=>{ data = loadData(); data.currentProfileId=null; saveData(data); initApp(); }); addMedicineBtn.addEventListener('click', openAddMedicine); deleteProfileBtn.addEventListener('click', ()=> { if(!currentProfile) return; if(!confirm('Delete current profile "'+currentProfile.name+'"? This removes all medicines and history for this profile.')) return; const data = loadData(); data.profiles = data.profiles.filter(p=> p.id !== currentProfile.id); data.history = data.history.filter(h=> h.profileId !== currentProfile.id); data.currentProfileId = null; saveData(data); currentProfile = null; initApp(); }); // Request notification permissions on first interaction (polite) document.addEventListener('click', ()=>{ if(window.Notification && Notification.permission === 'default') requestNotifPermission(); }, { once:true }); // Initialize initApp(); """ # README readme = """MediCycle — Medicine Reminder (Local-only webapp) Files: - index.html - css/style.css - js/app.js Features implemented: - Multiple profiles with passkey protection (prompt on open). - Create/Edit/Delete medicine routines (daily / monthly / weekly). - "Today" tab shows only medicines scheduled for today; you can mark them as taken. - History tab shows taken records per profile. - Export / Import JSON (local file). - Dark / Light theme toggle (persisted). - Browser notifications (requests permission once). - All data stored in browser localStorage, no backend. How to use: 1. Open index.html in a browser (supports mobile & desktop). 2. Create a profile (name, age, passkey). 3. Add medicines and schedules. 4. Use Export to backup, Import to restore (replaces current data). """ # write files (OUT_DIR / 'index.html').write_text(index_html, encoding='utf-8') css_dir = OUT_DIR / 'css' css_dir.mkdir(exist_ok=True) (css_dir / 'style.css').write_text(css, encoding='utf-8') js_dir = OUT_DIR / 'js' js_dir.mkdir(exist_ok=True) (js_dir / 'app.js').write_text(js, encoding='utf-8') (OUT_DIR / 'README.txt').write_text(readme, encoding='utf-8') # create zip zip_path = Path('/mnt/data/medicycle_app.zip') with zipfile.ZipFile(zip_path, 'w', compression=zipfile.ZIP_DEFLATED) as z: for p in OUT_DIR.rglob('*'): z.write(p, arcname=str(p.relative_to(OUT_DIR.parent))) print("Created:", zip_path) print("Files included:") for p in OUT_DIR.rglob('*'): print("-", p.relative_to(OUT_DIR))