mirror of
https://github.com/nagaoo0/HabbitGrid.git
synced 2026-01-11 23:44:55 +00:00
Add random congrats msg
This commit is contained in:
102
public/encouragements.json
Normal file
102
public/encouragements.json
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
[
|
||||||
|
"Great job! Keep going!",
|
||||||
|
"You're on fire! 🔥",
|
||||||
|
"Consistency is key!",
|
||||||
|
"Amazing streak!",
|
||||||
|
"You crushed it today!",
|
||||||
|
"Small steps, big results!",
|
||||||
|
"Habit hero!",
|
||||||
|
"Progress, not perfection!",
|
||||||
|
"Every dot counts!",
|
||||||
|
"Keep up the momentum!",
|
||||||
|
"You’re building something awesome!",
|
||||||
|
"One step closer to your goal!",
|
||||||
|
"You’re unstoppable!",
|
||||||
|
"Keep the streak alive!",
|
||||||
|
"You’re making it happen!",
|
||||||
|
"Your effort is inspiring!",
|
||||||
|
"You’re a streak superstar!",
|
||||||
|
"Every day matters!",
|
||||||
|
"You’re a habit legend!",
|
||||||
|
"You’re doing fantastic!",
|
||||||
|
"Keep shining!",
|
||||||
|
"You’re a role model!",
|
||||||
|
"You’re a champion!",
|
||||||
|
"You’re making progress!",
|
||||||
|
"You’re a winner!",
|
||||||
|
"You’re a streak master!",
|
||||||
|
"You’re a habit machine!",
|
||||||
|
"You’re a streak builder!",
|
||||||
|
"You’re a streak star!",
|
||||||
|
"You’re a streak hero!",
|
||||||
|
"You’re a streak ninja!",
|
||||||
|
"You’re a streak wizard!",
|
||||||
|
"You’re a streak warrior!",
|
||||||
|
"You’re a streak explorer!",
|
||||||
|
"You’re a streak adventurer!",
|
||||||
|
"You’re a streak conqueror!",
|
||||||
|
"You’re a streak champion!",
|
||||||
|
"You’re a streak genius!",
|
||||||
|
"You’re a streak guru!",
|
||||||
|
"You’re a streak expert!",
|
||||||
|
"You’re a streak pro!",
|
||||||
|
"You’re a streak veteran!",
|
||||||
|
"You’re a streak rookie!",
|
||||||
|
"You’re a streak all-star!",
|
||||||
|
"You’re a streak MVP!",
|
||||||
|
"You’re a streak superstar!",
|
||||||
|
"You’re a streak rockstar!",
|
||||||
|
"You’re a streak dynamo!",
|
||||||
|
"You’re a streak powerhouse!",
|
||||||
|
"You’re a streak inspiration!",
|
||||||
|
"You’re a streak motivator!",
|
||||||
|
"You’re a streak leader!",
|
||||||
|
"You’re a streak innovator!",
|
||||||
|
"You’re a streak creator!",
|
||||||
|
"You’re a streak builder!",
|
||||||
|
"You’re a streak achiever!",
|
||||||
|
"You’re a streak doer!",
|
||||||
|
"You’re a streak finisher!",
|
||||||
|
"You’re a streak starter!",
|
||||||
|
"You’re a streak closer!",
|
||||||
|
"You’re a streak winner!",
|
||||||
|
"You’re a streak believer!",
|
||||||
|
"You’re a streak dreamer!",
|
||||||
|
"You’re a streak thinker!",
|
||||||
|
"You’re a streak planner!",
|
||||||
|
"You’re a streak organizer!",
|
||||||
|
"You’re a streak strategist!",
|
||||||
|
"You’re a streak tactician!",
|
||||||
|
"You’re a streak visionary!",
|
||||||
|
"You’re a streak optimist!",
|
||||||
|
"You’re a streak realist!",
|
||||||
|
"You’re a streak enthusiast!",
|
||||||
|
"You’re a streak supporter!",
|
||||||
|
"You’re a streak encourager!",
|
||||||
|
"You’re a streak helper!",
|
||||||
|
"You’re a streak friend!",
|
||||||
|
"You’re a streak teammate!",
|
||||||
|
"You’re a streak partner!",
|
||||||
|
"You’re a streak ally!",
|
||||||
|
"You’re a streak companion!",
|
||||||
|
"You’re a streak buddy!",
|
||||||
|
"You’re a streak pal!",
|
||||||
|
"You’re a streak mate!",
|
||||||
|
"You’re a streak peer!",
|
||||||
|
"You’re a streak colleague!",
|
||||||
|
"You’re a streak associate!",
|
||||||
|
"You’re a streak collaborator!",
|
||||||
|
"You’re a streak contributor!",
|
||||||
|
"You’re a streak participant!",
|
||||||
|
"You’re a streak member!",
|
||||||
|
"You’re a streak player!",
|
||||||
|
"You’re a streak contender!",
|
||||||
|
"You’re a streak competitor!",
|
||||||
|
"You’re a streak challenger!",
|
||||||
|
"You’re a streak rival!",
|
||||||
|
"You’re a streak victor!",
|
||||||
|
"You’re a streak survivor!",
|
||||||
|
"You’re a streak thriver!",
|
||||||
|
"You’re a streak overcomer!",
|
||||||
|
"You’re a streak achiever!"
|
||||||
|
]
|
||||||
@@ -17,6 +17,7 @@ import { motion } from 'framer-motion';
|
|||||||
import { getColorIntensity, isToday, formatDate } from '../lib/utils-habit';
|
import { getColorIntensity, isToday, formatDate } from '../lib/utils-habit';
|
||||||
import { getFrozenDays } from '../lib/utils-habit';
|
import { getFrozenDays } from '../lib/utils-habit';
|
||||||
import { toggleCompletion } from '../lib/storage';
|
import { toggleCompletion } from '../lib/storage';
|
||||||
|
import { toast } from './ui/use-toast';
|
||||||
|
|
||||||
const MiniGrid = ({ habit, onUpdate }) => {
|
const MiniGrid = ({ habit, onUpdate }) => {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@@ -39,10 +40,33 @@ const MiniGrid = ({ habit, onUpdate }) => {
|
|||||||
days.push(date);
|
days.push(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCellClick = (e, date) => {
|
const handleCellClick = async (e, date) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleCompletion(habit.id, formatDate(date));
|
const dateStr = formatDate(date);
|
||||||
|
const isTodayCell = isToday(date);
|
||||||
|
const wasCompleted = habit.completions.includes(dateStr);
|
||||||
|
toggleCompletion(habit.id, dateStr);
|
||||||
onUpdate();
|
onUpdate();
|
||||||
|
// Only show encouragement toast if validating (adding) today's dot
|
||||||
|
if (isTodayCell && !wasCompleted) {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/encouragements.json');
|
||||||
|
const messages = await res.json();
|
||||||
|
const msg = messages[Math.floor(Math.random() * messages.length)];
|
||||||
|
toast({
|
||||||
|
title: '🎉 Keep Going!',
|
||||||
|
description: msg,
|
||||||
|
duration: 2500,
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// fallback message
|
||||||
|
toast({
|
||||||
|
title: '🎉 Keep Going!',
|
||||||
|
description: 'Great job! Keep up the streak!',
|
||||||
|
duration: 2500,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -7,19 +7,21 @@ import React from 'react';
|
|||||||
const ToastProvider = ToastPrimitives.Provider;
|
const ToastProvider = ToastPrimitives.Provider;
|
||||||
|
|
||||||
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (
|
const ToastViewport = React.forwardRef(({ className, ...props }, ref) => (
|
||||||
<ToastPrimitives.Viewport
|
<ToastPrimitives.Viewport
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
|
'fixed z-[100] flex max-h-screen w-full flex-col-reverse p-4',
|
||||||
className,
|
'sm:bottom-4 sm:right-4 sm:top-auto sm:left-auto sm:flex-col md:max-w-[420px]',
|
||||||
)}
|
'bottom-4 left-1/2 transform -translate-x-1/2 sm:transform-none',
|
||||||
{...props}
|
className,
|
||||||
/>
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
));
|
));
|
||||||
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
||||||
|
|
||||||
const toastVariants = cva(
|
const toastVariants = cva(
|
||||||
'data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full',
|
'data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-2xl border-0 p-6 pr-8 shadow-2xl transition-all bg-white/80 backdrop-blur-lg ring-2 ring-green-300/40 drop-shadow-xl scale-95 animate-toast-in data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
@@ -73,20 +75,24 @@ const ToastClose = React.forwardRef(({ className, ...props }, ref) => (
|
|||||||
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
||||||
|
|
||||||
const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (
|
const ToastTitle = React.forwardRef(({ className, ...props }, ref) => (
|
||||||
<ToastPrimitives.Title
|
<ToastPrimitives.Title
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('text-sm font-semibold', className)}
|
className={cn('text-lg font-bold flex items-center gap-2', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
|
<span className="animate-float inline-block">🎊</span> {props.children}
|
||||||
|
</ToastPrimitives.Title>
|
||||||
));
|
));
|
||||||
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
||||||
|
|
||||||
const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (
|
const ToastDescription = React.forwardRef(({ className, ...props }, ref) => (
|
||||||
<ToastPrimitives.Description
|
<ToastPrimitives.Description
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('text-sm opacity-90', className)}
|
className={cn('text-base opacity-95 font-medium flex items-center gap-2', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
|
<span className="animate-float inline-block">✨</span> {props.children}
|
||||||
|
</ToastPrimitives.Description>
|
||||||
));
|
));
|
||||||
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
||||||
|
|
||||||
|
|||||||
@@ -94,4 +94,21 @@
|
|||||||
|
|
||||||
.dark .grid-scroll::-webkit-scrollbar-thumb {
|
.dark .grid-scroll::-webkit-scrollbar-thumb {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Toast custom animations */
|
||||||
|
@keyframes toast-in {
|
||||||
|
0% { transform: scale(0.7) translateY(40px); opacity: 0; }
|
||||||
|
100% { transform: scale(1) translateY(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
.animate-toast-in {
|
||||||
|
animation: toast-in 0.5s cubic-bezier(.68,-0.55,.27,1.55);
|
||||||
|
}
|
||||||
|
@keyframes float {
|
||||||
|
0% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-8px); }
|
||||||
|
100% { transform: translateY(0); }
|
||||||
|
}
|
||||||
|
.animate-float {
|
||||||
|
animation: float 2s infinite ease-in-out;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user