feat: Remove Muslim and Jewish options, keep only Christian (KJV) and Secular quotes
This commit is contained in:
parent
bf9da84553
commit
7dd5e6359a
BIN
public/icons/apple-touch-icon.png
Normal file
BIN
public/icons/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 413 KiB |
BIN
public/icons/icon-512.png
Normal file
BIN
public/icons/icon-512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 413 KiB |
29
public/manifest.json
Normal file
29
public/manifest.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "QuitTraq - Track Your Progress",
|
||||||
|
"short_name": "QuitTraq",
|
||||||
|
"description": "Track your nicotine and marijuana usage to quit smoking",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#0a0a0f",
|
||||||
|
"theme_color": "#8b5cf6",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/icon-512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categories": [
|
||||||
|
"health",
|
||||||
|
"lifestyle",
|
||||||
|
"medical"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,10 +1,27 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { Providers } from "@/components/Providers";
|
import { Providers } from "@/components/Providers";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "QuitTraq - Track Your Journey to Quit Smoking",
|
title: "QuitTraq - Track Your Journey to Quit Smoking",
|
||||||
description: "Track and manage your smoking habits, set goals, and quit safely with personalized plans.",
|
description: "Track and manage your smoking habits, set goals, and quit safely with personalized plans.",
|
||||||
|
manifest: "/manifest.json",
|
||||||
|
appleWebApp: {
|
||||||
|
capable: true,
|
||||||
|
statusBarStyle: "black-translucent",
|
||||||
|
title: "QuitTraq",
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
apple: "/icons/apple-touch-icon.png",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const viewport: Viewport = {
|
||||||
|
themeColor: "#8b5cf6",
|
||||||
|
width: "device-width",
|
||||||
|
initialScale: 1,
|
||||||
|
maximumScale: 1,
|
||||||
|
userScalable: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@ -18,6 +35,9 @@ export default function RootLayout({
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet" />
|
||||||
|
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
|
<meta name="mobile-web-app-capable" content="yes" />
|
||||||
</head>
|
</head>
|
||||||
<body className="antialiased">
|
<body className="antialiased">
|
||||||
<Providers>{children}</Providers>
|
<Providers>{children}</Providers>
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import {
|
|||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { savePreferencesAsync, getPreferences, UserPreferences } from '@/lib/storage';
|
import { savePreferencesAsync, getPreferences, UserPreferences } from '@/lib/storage';
|
||||||
|
|
||||||
type Religion = 'christian' | 'muslim' | 'jewish' | 'secular';
|
type Religion = 'christian' | 'secular';
|
||||||
|
|
||||||
interface Verse {
|
interface Verse {
|
||||||
text: string;
|
text: string;
|
||||||
@ -41,50 +41,6 @@ const VERSES: Record<Religion, Verse[]> = {
|
|||||||
{ text: "Therefore if any man be in Christ, he is a new creature: old things are passed away; behold, all things are become new.", source: "2 Corinthians 5:17 (KJV)" },
|
{ text: "Therefore if any man be in Christ, he is a new creature: old things are passed away; behold, all things are become new.", source: "2 Corinthians 5:17 (KJV)" },
|
||||||
{ text: "The LORD shall fight for you, and ye shall hold your peace.", source: "Exodus 14:14 (KJV)" },
|
{ text: "The LORD shall fight for you, and ye shall hold your peace.", source: "Exodus 14:14 (KJV)" },
|
||||||
],
|
],
|
||||||
muslim: [
|
|
||||||
{ text: "Indeed, with hardship [will be] ease.", source: "Surah Ash-Sharh 94:6" },
|
|
||||||
{ text: "Allah does not burden a soul beyond that it can bear.", source: "Surah Al-Baqarah 2:286" },
|
|
||||||
{ text: "So remember Me; I will remember you.", source: "Surah Al-Baqarah 2:152" },
|
|
||||||
{ text: "And whosoever fears Allah... He will make a way for him to get out (from every difficulty).", source: "Surah At-Talaq 65:2" },
|
|
||||||
{ text: "Indeed, Allah is with the patient.", source: "Surah Al-Baqarah 2:153" },
|
|
||||||
{ text: "Call upon Me; I will respond to you.", source: "Surah Ghafir 40:60" },
|
|
||||||
{ text: "And He found you lost and guided [you].", source: "Surah Ad-Duhaa 93:7" },
|
|
||||||
{ text: "Unquestionably, by the remembrance of Allah hearts are assured.", source: "Surah Ar-Ra'd 13:28" },
|
|
||||||
{ text: "And put your trust in Allah, and sufficient is Allah as a Disposer of affairs.", source: "Surah Al-Ahzab 33:3" },
|
|
||||||
{ text: "Verily, in the remembrance of Allah do hearts find rest.", source: "Surah Ar-Ra'd 13:28" },
|
|
||||||
{ text: "Our Lord, pour upon us patience and plant firmly our feet.", source: "Surah Al-Baqarah 2:250" },
|
|
||||||
{ text: "If Allah should aid you, no one can overcome you.", source: "Surah Ali 'Imran 3:160" },
|
|
||||||
{ text: "Indeed, my Lord is near and responsive.", source: "Surah Hud 11:61" },
|
|
||||||
{ text: "And seek help through patience and prayer, and indeed, it is difficult except for the humbly submissive.", source: "Surah Al-Baqarah 2:45" },
|
|
||||||
{ text: "So surely with hardship comes ease.", source: "Surah Ash-Sharh 94:5" },
|
|
||||||
{ text: "Do not despair of the mercy of Allah.", source: "Surah Az-Zumar 39:53" },
|
|
||||||
{ text: "He knows what is in every heart.", source: "Surah Al-Mulk 67:13" },
|
|
||||||
{ text: "Allah is the best of planners.", source: "Surah Al-Anfal 8:30" },
|
|
||||||
{ text: "My Lord, indeed I am, for whatever good You would send down to me, in need.", source: "Surah Al-Qasas 28:24" },
|
|
||||||
{ text: "Indeed, good deeds do away with misdeeds.", source: "Surah Hud 11:114" },
|
|
||||||
],
|
|
||||||
jewish: [
|
|
||||||
{ text: "Be strong and of good courage.", source: "Joshua 1:9 (Tanakh)" },
|
|
||||||
{ text: "I have set the LORD always before me; because He is at my right hand, I shall not be moved.", source: "Psalm 16:8 (Tanakh)" },
|
|
||||||
{ text: "The LORD is my light and my salvation; whom shall I fear?", source: "Psalm 27:1 (Tanakh)" },
|
|
||||||
{ text: "Trust in the LORD with all your heart, and do not rely on your own understanding.", source: "Proverbs 3:5 (Tanakh)" },
|
|
||||||
{ text: "Create in me a clean heart, O God, and put a new and right spirit within me.", source: "Psalm 51:10 (Tanakh)" },
|
|
||||||
{ text: "Where there is no vision, the people perish.", source: "Proverbs 29:18 (Tanakh)" },
|
|
||||||
{ text: "A righteous man falls seven times, and rises up again.", source: "Proverbs 24:16 (Tanakh)" },
|
|
||||||
{ text: "Do not fear, for I am with you; do not be dismayed, for I am your God.", source: "Isaiah 41:10 (Tanakh)" },
|
|
||||||
{ text: "As water reflects the face, so one’s life reflects the heart.", source: "Proverbs 27:19 (Tanakh)" },
|
|
||||||
{ text: "If I am not for myself, who will be for me? And if I am only for myself, what am I?", source: "Pirkei Avot 1:14" },
|
|
||||||
{ text: "The world stands on three things: Torah, service, and acts of loving kindness.", source: "Pirkei Avot 1:2" },
|
|
||||||
{ text: "According to the effort is the reward.", source: "Pirkei Avot 5:23" },
|
|
||||||
{ text: "It is not your duty to finish the work, but neither are you at liberty to neglect it.", source: "Pirkei Avot 2:16" },
|
|
||||||
{ text: "Who is wise? One who learns from every man. Who is strong? One who overpowers his inclinations.", source: "Pirkei Avot 4:1" },
|
|
||||||
{ text: "Even in laughter the heart may ache, and the end of joy may be grief.", source: "Proverbs 14:13 (Tanakh)" },
|
|
||||||
{ text: "A soft answer turns away wrath, but a harsh word stirs up anger.", source: "Proverbs 15:1 (Tanakh)" },
|
|
||||||
{ text: "He who walks with integrity walks securely.", source: "Proverbs 10:9 (Tanakh)" },
|
|
||||||
{ text: "The candle of God is the soul of man.", source: "Proverbs 20:27 (Tanakh)" },
|
|
||||||
{ text: "There is a time for everything, and a season for every activity under the heavens.", source: "Ecclesiastes 3:1 (Tanakh)" },
|
|
||||||
{ text: "What you hate, do not do to your neighbor.", source: "Hillel the Elder (Shabbat 31a)" },
|
|
||||||
],
|
|
||||||
secular: [
|
secular: [
|
||||||
{ text: "The only way to do great work is to love what you do.", source: "Steve Jobs" },
|
{ text: "The only way to do great work is to love what you do.", source: "Steve Jobs" },
|
||||||
{ text: "It is during our darkest moments that we must focus to see the light.", source: "Aristotle" },
|
{ text: "It is during our darkest moments that we must focus to see the light.", source: "Aristotle" },
|
||||||
@ -148,44 +104,30 @@ export function DailyInspirationCard({ initialReligion, onReligionChange }: Dail
|
|||||||
|
|
||||||
const getLabel = (r: Religion) => {
|
const getLabel = (r: Religion) => {
|
||||||
switch (r) {
|
switch (r) {
|
||||||
case 'christian': return 'Christian (Bible)';
|
case 'christian': return 'Christian (KJV)';
|
||||||
case 'muslim': return 'Muslim (Quran)';
|
|
||||||
case 'jewish': return 'Jewish (Torah)';
|
|
||||||
case 'secular': return 'Secular (Quotes)';
|
case 'secular': return 'Secular (Quotes)';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = () => {
|
const getIcon = () => {
|
||||||
switch (currentReligion) {
|
if (currentReligion === 'christian') {
|
||||||
case 'christian':
|
return <BookOpen className="h-4 w-4 text-yellow-300 animate-pulse-subtle" />;
|
||||||
case 'muslim':
|
|
||||||
case 'jewish':
|
|
||||||
return <BookOpen className="h-4 w-4 text-yellow-300 animate-pulse-subtle" />;
|
|
||||||
default:
|
|
||||||
return <Sparkles className="h-4 w-4 text-yellow-300 animate-pulse-subtle" />;
|
|
||||||
}
|
}
|
||||||
|
return <Sparkles className="h-4 w-4 text-yellow-300 animate-pulse-subtle" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBackground = () => {
|
const getBackground = () => {
|
||||||
switch (currentReligion) {
|
if (currentReligion === 'christian') {
|
||||||
case 'christian':
|
return 'linear-gradient(135deg, rgba(59, 130, 246, 0.35) 0%, rgba(37, 99, 235, 0.3) 50%, rgba(30, 64, 175, 0.4) 100%)'; // Blue
|
||||||
return 'linear-gradient(135deg, rgba(59, 130, 246, 0.35) 0%, rgba(37, 99, 235, 0.3) 50%, rgba(30, 64, 175, 0.4) 100%)'; // Blue
|
|
||||||
case 'muslim':
|
|
||||||
return 'linear-gradient(135deg, rgba(16, 185, 129, 0.35) 0%, rgba(5, 150, 105, 0.3) 50%, rgba(4, 120, 87, 0.4) 100%)'; // Emerald
|
|
||||||
case 'jewish':
|
|
||||||
return 'linear-gradient(135deg, rgba(147, 51, 234, 0.35) 0%, rgba(126, 34, 206, 0.3) 50%, rgba(107, 33, 168, 0.4) 100%)'; // Purple
|
|
||||||
default:
|
|
||||||
return 'linear-gradient(135deg, rgba(67, 56, 202, 0.35) 0%, rgba(109, 40, 217, 0.3) 50%, rgba(76, 29, 149, 0.4) 100%)'; // Indigo/Original
|
|
||||||
}
|
}
|
||||||
|
return 'linear-gradient(135deg, rgba(67, 56, 202, 0.35) 0%, rgba(109, 40, 217, 0.3) 50%, rgba(76, 29, 149, 0.4) 100%)'; // Indigo/Original
|
||||||
};
|
};
|
||||||
|
|
||||||
const getShadowColor = () => {
|
const getShadowColor = () => {
|
||||||
switch (currentReligion) {
|
if (currentReligion === 'christian') {
|
||||||
case 'christian': return 'rgba(59, 130, 246, 0.15)';
|
return 'rgba(59, 130, 246, 0.15)';
|
||||||
case 'muslim': return 'rgba(16, 185, 129, 0.15)';
|
|
||||||
case 'jewish': return 'rgba(147, 51, 234, 0.15)';
|
|
||||||
default: return 'rgba(99, 102, 241, 0.15)';
|
|
||||||
}
|
}
|
||||||
|
return 'rgba(99, 102, 241, 0.15)';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -218,7 +160,7 @@ export function DailyInspirationCard({ initialReligion, onReligionChange }: Dail
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
{(['christian', 'muslim', 'jewish', 'secular'] as Religion[]).map((r) => (
|
{(['christian', 'secular'] as Religion[]).map((r) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
key={r}
|
key={r}
|
||||||
onClick={() => handleReligionChange(r)}
|
onClick={() => handleReligionChange(r)}
|
||||||
|
|||||||
@ -118,7 +118,7 @@ export function Dashboard({ user }: DashboardProps) {
|
|||||||
init();
|
init();
|
||||||
}, [loadData, checkAndUnlockAchievements]);
|
}, [loadData, checkAndUnlockAchievements]);
|
||||||
|
|
||||||
const handleSetupComplete = async (data: { substance: 'nicotine' | 'weed'; name: string; age: number; religion: 'christian' | 'muslim' | 'jewish' | 'secular' }) => {
|
const handleSetupComplete = async (data: { substance: 'nicotine' | 'weed'; name: string; age: number; religion: 'christian' | 'secular' }) => {
|
||||||
const today = getTodayString();
|
const today = getTodayString();
|
||||||
const newPrefs: UserPreferences = {
|
const newPrefs: UserPreferences = {
|
||||||
substance: data.substance,
|
substance: data.substance,
|
||||||
|
|||||||
180
src/components/InstallAppButton.tsx
Normal file
180
src/components/InstallAppButton.tsx
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { Download, Share, Plus, MoreVertical, Smartphone } from 'lucide-react';
|
||||||
|
|
||||||
|
interface BeforeInstallPromptEvent extends Event {
|
||||||
|
prompt: () => Promise<void>;
|
||||||
|
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InstallAppButton() {
|
||||||
|
const [showInstructions, setShowInstructions] = useState(false);
|
||||||
|
const [deferredPrompt, setDeferredPrompt] = useState<BeforeInstallPromptEvent | null>(null);
|
||||||
|
const [isIOS, setIsIOS] = useState(false);
|
||||||
|
const [isAndroid, setIsAndroid] = useState(false);
|
||||||
|
const [isStandalone, setIsStandalone] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Check if already installed as PWA
|
||||||
|
const isAppInstalled = window.matchMedia('(display-mode: standalone)').matches ||
|
||||||
|
(window.navigator as Navigator & { standalone?: boolean }).standalone === true;
|
||||||
|
setIsStandalone(isAppInstalled);
|
||||||
|
|
||||||
|
// Detect platform
|
||||||
|
const userAgent = window.navigator.userAgent.toLowerCase();
|
||||||
|
const isIOSDevice = /iphone|ipad|ipod/.test(userAgent);
|
||||||
|
const isAndroidDevice = /android/.test(userAgent);
|
||||||
|
setIsIOS(isIOSDevice);
|
||||||
|
setIsAndroid(isAndroidDevice);
|
||||||
|
|
||||||
|
// Listen for beforeinstallprompt (Android/Chrome)
|
||||||
|
const handleBeforeInstallPrompt = (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setDeferredPrompt(e as BeforeInstallPromptEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleInstallClick = async () => {
|
||||||
|
if (deferredPrompt) {
|
||||||
|
// Use the native install prompt (Android/Chrome)
|
||||||
|
await deferredPrompt.prompt();
|
||||||
|
const { outcome } = await deferredPrompt.userChoice;
|
||||||
|
if (outcome === 'accepted') {
|
||||||
|
setDeferredPrompt(null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show manual instructions
|
||||||
|
setShowInstructions(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't show if already installed as PWA
|
||||||
|
if (isStandalone) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleInstallClick}
|
||||||
|
className="gap-2 border-purple-500/30 hover:bg-purple-500/10 hover:border-purple-500/50"
|
||||||
|
>
|
||||||
|
<Smartphone className="h-4 w-4 text-purple-400" />
|
||||||
|
<span className="hidden sm:inline">Add to Home Screen</span>
|
||||||
|
<span className="sm:hidden">Install</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Dialog open={showInstructions} onOpenChange={setShowInstructions}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
<Download className="h-5 w-5 text-purple-400" />
|
||||||
|
Add QuitTraq to Home Screen
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
Get quick access to track your progress right from your phone's home screen.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="space-y-6 py-4">
|
||||||
|
{isIOS ? (
|
||||||
|
// iOS Instructions
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className="text-sm font-medium text-purple-400">For iPhone / iPad (Safari):</p>
|
||||||
|
<ol className="space-y-4 text-sm">
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">1</span>
|
||||||
|
<div>
|
||||||
|
<p>Tap the <strong>Share</strong> button</p>
|
||||||
|
<div className="mt-1 inline-flex items-center gap-1 rounded bg-muted px-2 py-1">
|
||||||
|
<Share className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">2</span>
|
||||||
|
<div>
|
||||||
|
<p>Scroll down and tap <strong>"Add to Home Screen"</strong></p>
|
||||||
|
<div className="mt-1 inline-flex items-center gap-1 rounded bg-muted px-2 py-1">
|
||||||
|
<Plus className="h-4 w-4" /> Add to Home Screen
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">3</span>
|
||||||
|
<p>Tap <strong>"Add"</strong> to confirm</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
) : isAndroid ? (
|
||||||
|
// Android Instructions
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className="text-sm font-medium text-purple-400">For Android (Chrome):</p>
|
||||||
|
<ol className="space-y-4 text-sm">
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">1</span>
|
||||||
|
<div>
|
||||||
|
<p>Tap the <strong>menu</strong> button (3 dots)</p>
|
||||||
|
<div className="mt-1 inline-flex items-center gap-1 rounded bg-muted px-2 py-1">
|
||||||
|
<MoreVertical className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">2</span>
|
||||||
|
<div>
|
||||||
|
<p>Tap <strong>"Add to Home screen"</strong> or <strong>"Install app"</strong></p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-3">
|
||||||
|
<span className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-purple-500/20 text-purple-400 text-xs font-bold">3</span>
|
||||||
|
<p>Tap <strong>"Add"</strong> or <strong>"Install"</strong> to confirm</p>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// Desktop / Unknown
|
||||||
|
<div className="space-y-4">
|
||||||
|
<p className="text-sm font-medium text-purple-400">On mobile device:</p>
|
||||||
|
<ul className="space-y-2 text-sm text-muted-foreground">
|
||||||
|
<li>• <strong>iPhone/iPad:</strong> Use Safari, tap Share → Add to Home Screen</li>
|
||||||
|
<li>• <strong>Android:</strong> Use Chrome, tap Menu → Add to Home screen</li>
|
||||||
|
</ul>
|
||||||
|
<p className="text-xs text-muted-foreground mt-4">
|
||||||
|
Open this page on your phone to add QuitTraq to your home screen for quick access!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="pt-2 border-t">
|
||||||
|
<p className="text-xs text-muted-foreground text-center">
|
||||||
|
📱 Once added, tap the QuitTraq icon to quickly log your usage!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button onClick={() => setShowInstructions(false)} className="w-full">
|
||||||
|
Got it!
|
||||||
|
</Button>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ import {
|
|||||||
|
|
||||||
interface SetupWizardProps {
|
interface SetupWizardProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onComplete: (data: { substance: 'nicotine' | 'weed'; name: string; age: number; religion: 'christian' | 'muslim' | 'jewish' | 'secular' }) => void;
|
onComplete: (data: { substance: 'nicotine' | 'weed'; name: string; age: number; religion: 'christian' | 'secular' }) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SetupWizard({ open, onComplete }: SetupWizardProps) {
|
export function SetupWizard({ open, onComplete }: SetupWizardProps) {
|
||||||
@ -29,7 +29,7 @@ export function SetupWizard({ open, onComplete }: SetupWizardProps) {
|
|||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [age, setAge] = useState('25');
|
const [age, setAge] = useState('25');
|
||||||
const [substance, setSubstance] = useState<'nicotine' | 'weed' | ''>('');
|
const [substance, setSubstance] = useState<'nicotine' | 'weed' | ''>('');
|
||||||
const [religion, setReligion] = useState<'christian' | 'muslim' | 'jewish' | 'secular' | ''>('');
|
const [religion, setReligion] = useState<'christian' | 'secular' | ''>('');
|
||||||
|
|
||||||
const ages = Array.from({ length: 83 }, (_, i) => (i + 18).toString());
|
const ages = Array.from({ length: 83 }, (_, i) => (i + 18).toString());
|
||||||
|
|
||||||
@ -159,14 +159,12 @@ export function SetupWizard({ open, onComplete }: SetupWizardProps) {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label htmlFor="religion">Choose your daily inspiration</Label>
|
<Label htmlFor="religion">Choose your daily inspiration</Label>
|
||||||
<Select value={religion} onValueChange={(v) => setReligion(v as 'christian' | 'muslim' | 'jewish' | 'secular')}>
|
<Select value={religion} onValueChange={(v) => setReligion(v as 'christian' | 'secular')}>
|
||||||
<SelectTrigger id="religion">
|
<SelectTrigger id="religion">
|
||||||
<SelectValue placeholder="Select source" />
|
<SelectValue placeholder="Select source" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="christian">Christian (Bible Verses)</SelectItem>
|
<SelectItem value="christian">Christian (KJV Bible Verses)</SelectItem>
|
||||||
<SelectItem value="muslim">Muslim (Quran Verses)</SelectItem>
|
|
||||||
<SelectItem value="jewish">Jewish (Torah/Tanakh)</SelectItem>
|
|
||||||
<SelectItem value="secular">Secular (Motivational Quotes)</SelectItem>
|
<SelectItem value="secular">Secular (Motivational Quotes)</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
@ -24,8 +24,8 @@ interface UsageCalendarProps {
|
|||||||
usageData: UsageEntry[];
|
usageData: UsageEntry[];
|
||||||
onDataUpdate: () => void;
|
onDataUpdate: () => void;
|
||||||
userId: string;
|
userId: string;
|
||||||
religion?: 'christian' | 'muslim' | 'jewish' | 'secular' | null;
|
religion?: 'christian' | 'secular' | null;
|
||||||
onReligionUpdate?: (religion: 'christian' | 'muslim' | 'jewish' | 'secular') => void;
|
onReligionUpdate?: (religion: 'christian' | 'secular') => void;
|
||||||
preferences?: UserPreferences | null;
|
preferences?: UserPreferences | null;
|
||||||
onPreferencesUpdate?: (prefs: UserPreferences) => Promise<void>;
|
onPreferencesUpdate?: (prefs: UserPreferences) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Cigarette, Leaf, LogOut, Home, ChevronDown, Sun, Moon, Bell, BellOff, BellRing, Menu } from 'lucide-react';
|
import { Cigarette, Leaf, LogOut, Home, ChevronDown, Sun, Moon, Bell, BellOff, BellRing, Menu } from 'lucide-react';
|
||||||
import { useTheme } from '@/lib/theme-context';
|
import { useTheme } from '@/lib/theme-context';
|
||||||
|
import { InstallAppButton } from './InstallAppButton';
|
||||||
|
|
||||||
interface UserHeaderProps {
|
interface UserHeaderProps {
|
||||||
user: User;
|
user: User;
|
||||||
@ -132,6 +133,7 @@ export function UserHeader({ user, preferences }: UserHeaderProps) {
|
|||||||
<Bell className="h-5 w-5 text-muted-foreground transition-transform duration-300" />
|
<Bell className="h-5 w-5 text-muted-foreground transition-transform duration-300" />
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
<InstallAppButton />
|
||||||
<button
|
<button
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
className="p-2.5 sm:p-2 rounded-full bg-muted hover:bg-muted/80 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-primary/30 hover:scale-110 active:scale-95"
|
className="p-2.5 sm:p-2 rounded-full bg-muted hover:bg-muted/80 transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-primary/30 hover:scale-110 active:scale-95"
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export interface UserPreferences {
|
|||||||
quitPlan: QuitPlan | null;
|
quitPlan: QuitPlan | null;
|
||||||
userName: string | null;
|
userName: string | null;
|
||||||
userAge: number | null;
|
userAge: number | null;
|
||||||
religion: 'christian' | 'muslim' | 'jewish' | 'secular' | null;
|
religion: 'christian' | 'secular' | null;
|
||||||
lastNicotineUsageTime?: string | null; // ISO timestamp of last usage
|
lastNicotineUsageTime?: string | null; // ISO timestamp of last usage
|
||||||
lastWeedUsageTime?: string | null; // ISO timestamp of last usage
|
lastWeedUsageTime?: string | null; // ISO timestamp of last usage
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user