This commit is contained in:
2026-05-21 09:50:31 +01:00
parent 81c0b534b7
commit 40bfbf7c11

View File

@@ -57,6 +57,9 @@ export default function App() {
// Estado para Definições
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
const [dailyOutfitTime, setDailyOutfitTime] = useState('08:00');
const [dailyOutfitNotifEnabled, setDailyOutfitNotifEnabled] = useState(false);
const [lastNotifiedDate, setLastNotifiedDate] = useState('');
const [weatherAlerts, setWeatherAlerts] = useState(true);
const [language, setLanguage] = useState('PT');
const [showLangModal, setShowLangModal] = useState(false);
@@ -172,6 +175,23 @@ export default function App() {
saveUserSetting('notificationsEnabled', newVal);
};
const handleDailyOutfitNotifToggle = async (newVal) => {
if (newVal) {
if ('Notification' in window) {
const permission = await Notification.requestPermission();
if (permission !== 'granted') {
alert('Permissão recusada. Ative as notificações no browser para usar esta funcionalidade.');
return;
}
} else {
alert('O seu browser não suporta notificações.');
return;
}
}
setDailyOutfitNotifEnabled(newVal);
saveUserSetting('dailyOutfitNotifEnabled', newVal);
};
const handleWeatherAlertsToggle = (newVal) => {
setWeatherAlerts(newVal);
saveUserSetting('weatherAlerts', newVal);
@@ -370,6 +390,9 @@ export default function App() {
if (data.settings.darkMode !== undefined) setDarkMode(data.settings.darkMode);
if (data.settings.theme !== undefined) setTheme(data.settings.theme);
if (data.settings.notificationsEnabled !== undefined) setNotificationsEnabled(data.settings.notificationsEnabled);
if (data.settings.dailyOutfitTime !== undefined) setDailyOutfitTime(data.settings.dailyOutfitTime);
if (data.settings.dailyOutfitNotifEnabled !== undefined) setDailyOutfitNotifEnabled(data.settings.dailyOutfitNotifEnabled);
if (data.settings.lastNotifiedDate !== undefined) setLastNotifiedDate(data.settings.lastNotifiedDate);
if (data.settings.weatherAlerts !== undefined) setWeatherAlerts(data.settings.weatherAlerts);
if (data.settings.cardSize !== undefined) setCardSize(data.settings.cardSize);
if (data.settings.defaultPage !== undefined) {
@@ -630,6 +653,45 @@ export default function App() {
};
const dailyLooks = getLooksForDayGlobal(todayStrGlobal);
// Efeito para notificação diária
useEffect(() => {
if (!user || !dailyOutfitNotifEnabled || !dailyOutfitTime) return;
const intervalId = setInterval(() => {
const now = new Date();
const currentHour = String(now.getHours()).padStart(2, '0');
const currentMinute = String(now.getMinutes()).padStart(2, '0');
const currentTime = `${currentHour}:${currentMinute}`;
const todayStr = `${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}`;
if (currentTime === dailyOutfitTime && lastNotifiedDate !== todayStr) {
const todaysLooks = getLooksForDayGlobal(todayStr);
if (todaysLooks.length > 0) {
const mainLook = todaysLooks[0];
if ('Notification' in window && Notification.permission === 'granted') {
navigator.serviceWorker?.getRegistration().then(reg => {
const title = 'MyCloset - Outfit Diário';
const options = {
body: `O seu outfit planeado "${mainLook.name}" está pronto para hoje!`,
icon: '/favicon.ico'
};
if (reg) {
reg.showNotification(title, options);
} else {
new Notification(title, options);
}
});
setLastNotifiedDate(todayStr);
saveUserSetting('lastNotifiedDate', todayStr);
}
}
}
}, 30000); // Verifica a cada 30 segundos
return () => clearInterval(intervalId);
}, [user, dailyOutfitNotifEnabled, dailyOutfitTime, lastNotifiedDate, outfitPlans, looks]);
const baseClothes = view === 'wishlist' ? wishlistClothes : activeClothes;
const availableColors = useMemo(() => {
@@ -2566,6 +2628,28 @@ export default function App() {
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${notificationsEnabled ? 'left-7' : 'left-1'}`}></div>
</button>
</div>
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4">
<div>
<p className="font-bold text-inherit flex items-center gap-2">Notificação do Outfit Diário</p>
<p className="text-[10px] uppercase tracking-widest opacity-50 text-inherit">Receber notificação com o outfit planeado à hora marcada</p>
</div>
<div className="flex items-center gap-4 w-full sm:w-auto justify-between sm:justify-end">
{dailyOutfitNotifEnabled && (
<input
type="time"
value={dailyOutfitTime}
onChange={(e) => {
setDailyOutfitTime(e.target.value);
saveUserSetting('dailyOutfitTime', e.target.value);
}}
className={`px-3 py-1.5 rounded-xl text-sm outline-none border font-bold ${darkMode ? 'bg-gray-800 border-gray-700 text-white' : 'bg-white border-gray-200 text-gray-900'}`}
/>
)}
<button onClick={() => handleDailyOutfitNotifToggle(!dailyOutfitNotifEnabled)} className={`w-14 h-8 rounded-full transition-colors relative shrink-0 ${dailyOutfitNotifEnabled ? 'bg-primary-600' : 'bg-gray-200'}`}>
<div className={`w-6 h-6 rounded-full bg-white absolute top-1 transition-all ${dailyOutfitNotifEnabled ? 'left-7' : 'left-1'}`}></div>
</button>
</div>
</div>
<div className="flex items-center justify-between">
<div>
<p className="font-bold text-inherit flex items-center gap-2">{t('weatherAlerts')}</p>