fix: refactor codebase

feat/streak
Arik Chakma 4 months ago
parent 95a369849a
commit 5266c8cffc
  1. 134
      src/components/AccountStreak/AccountStreak.tsx
  2. 56
      src/components/AccountStreak/StreakDay.tsx

@ -2,9 +2,9 @@ import { useEffect, useRef, useState } from 'react';
import { isLoggedIn } from '../../lib/jwt'; import { isLoggedIn } from '../../lib/jwt';
import { httpGet } from '../../lib/http'; import { httpGet } from '../../lib/http';
import { useToast } from '../../hooks/use-toast'; import { useToast } from '../../hooks/use-toast';
import { ChevronDown, Flame, X } from 'lucide-react'; import { Flame, X } from 'lucide-react';
import { useOutsideClick } from '../../hooks/use-outside-click'; import { useOutsideClick } from '../../hooks/use-outside-click';
import { cn } from '../../lib/classname'; import { StreakDay } from './StreakDay';
type StreakResponse = { type StreakResponse = {
count: number; count: number;
@ -69,7 +69,9 @@ export function AccountStreak(props: AccountStreakProps) {
const currentCircleCount = Math.min(currentCount, 5) + 1; const currentCircleCount = Math.min(currentCount, 5) + 1;
// Adding one day to show the streak they broke // Adding one day to show the streak they broke
const leftCircleCount = Math.min(5 - currentCircleCount, previousCount) + 1; const leftCircleCount = Math.min(5 - currentCircleCount, previousCount) + 1;
// In the maximum case, we will show 10 circles
const remainingCount = Math.max(0, 10 - leftCircleCount - currentCircleCount); const remainingCount = Math.max(0, 10 - leftCircleCount - currentCircleCount);
const totalCircles = leftCircleCount + currentCircleCount + remainingCount;
return ( return (
<div className="relative z-[90] animate-fade-in"> <div className="relative z-[90] animate-fade-in">
@ -86,98 +88,70 @@ export function AccountStreak(props: AccountStreakProps) {
{showDropdown && ( {showDropdown && (
<div <div
ref={dropdownRef} ref={dropdownRef}
className="absolute right-0 top-full z-50 w-[280px] translate-y-1 rounded-lg border border-gray-200 bg-white text-black shadow-lg" className="absolute right-0 top-full z-50 w-[320px] translate-y-1 rounded-lg bg-slate-800 shadow-xl"
> >
<div className="p-2"> <div className="px-3 py-2">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-slate-400">
Current Streak Current Streak
<span className="ml-2 font-medium text-black"> <span className="ml-2 font-medium text-white">
{accountStreak?.count || 0} {accountStreak?.count || 0}
</span> </span>
</p> </p>
<p className="text-sm text-gray-500"> <p className="text-sm text-slate-400">
Longest Streak Longest Streak
<span className="ml-2 font-medium text-black"> <span className="ml-2 font-medium text-white">
{accountStreak?.longestCount || 0} {accountStreak?.longestCount || 0}
</span> </span>
</p> </p>
</div> </div>
<div className="mt-6"> <div className="mt-8">
<div className="grid grid-cols-10 gap-2"> <div className="grid grid-cols-10 gap-1">
{[...Array(leftCircleCount)].map((_, index) => { {Array.from({ length: totalCircles }).map((_, index) => {
const isLast = index === leftCircleCount - 1; let dayCount,
const dayCount = icon,
previousCount - leftCircleCount + index + 1 + 1; isPreviousStreakDay,
isCurrentStreakDay,
isRemainingStreakDay,
isToday;
if (index < leftCircleCount) {
// Previous streak days
dayCount = previousCount - leftCircleCount + index + 1 + 1;
isPreviousStreakDay = true;
icon =
index === leftCircleCount - 1 ? (
<X className="size-3.5 text-white" />
) : (
<Flame className="size-3.5 text-white" />
);
} else if (index < leftCircleCount + currentCircleCount) {
// Current streak days
const currentIndex = index - leftCircleCount;
dayCount =
currentCount - currentCircleCount + currentIndex + 1 + 1;
isCurrentStreakDay = true;
isToday = currentIndex === currentCircleCount - 1;
icon = <Flame className="size-3.5 text-white" />;
} else {
// Remaining streak days
const remainingIndex =
index - leftCircleCount - currentCircleCount;
dayCount = currentCount + remainingIndex + 1 + 1;
isRemainingStreakDay = true;
}
return ( return (
<div <StreakDay
className="flex flex-col items-center justify-center gap-1.5" key={`streak-${index}`}
key={`left-${index}`} dayCount={dayCount}
> icon={icon}
<div isPreviousStreakDay={isPreviousStreakDay}
key={index} isCurrentStreakDay={isCurrentStreakDay}
className="flex size-5 items-center justify-center rounded-full bg-red-200" isRemainingStreakDay={isRemainingStreakDay}
> isToday={isToday}
{isLast ? ( />
<X className="size-3 stroke-[2.5px] text-red-600" />
) : (
<Flame className="size-3 text-red-600" />
)}
</div>
<span className="text-xs text-red-600">{dayCount}</span>
</div>
);
})}
{[...Array(currentCircleCount)].map((_, index) => {
const dayCount =
currentCount - currentCircleCount + index + 1 + 1;
const isLast = index === currentCircleCount - 1;
return (
<div
className="relative flex flex-col items-center justify-center gap-1.5"
key={`current-${index}`}
>
<div
key={index}
className={cn(
'flex size-5 items-center justify-center rounded-full',
isLast
? 'border-2 border-dashed border-gray-400'
: 'bg-purple-200',
)}
>
{!isLast && (
<Flame className="size-3 text-purple-500" />
)}
</div>
<span className="text-xs text-gray-600">{dayCount}</span>
{isLast && (
<ChevronDown className="absolute bottom-full left-1/2 h-4 w-4 -translate-x-1/2 transform stroke-[2.5px] text-gray-400" />
)}
</div>
);
})}
{[...Array(remainingCount)].map((_, index) => {
const dayCount = currentCount + index + 1 + 1;
return (
<div
className="flex flex-col items-center justify-center gap-1.5"
key={`remaining-${index}`}
>
<div
key={index}
className={cn(
'flex size-5 items-center justify-center rounded-full',
'bg-gray-200',
)}
></div>
<span className="text-xs text-gray-400">{dayCount}</span>
</div>
); );
})} })}
</div> </div>

@ -0,0 +1,56 @@
import type { ReactNode } from 'react';
import { cn } from '../../lib/classname';
import { ChevronDown } from 'lucide-react';
type StreakDayProps = {
isToday?: boolean;
isCurrentStreakDay?: boolean;
isPreviousStreakDay?: boolean;
isRemainingStreakDay?: boolean;
dayCount: number;
icon?: ReactNode;
};
export function StreakDay(props: StreakDayProps) {
const {
isCurrentStreakDay,
isPreviousStreakDay,
isRemainingStreakDay,
dayCount,
icon,
isToday = false,
} = props;
return (
<div
className={cn(
'flex flex-col items-center justify-center gap-1.5',
isCurrentStreakDay && 'relative',
)}
>
<div
className={cn(
'flex size-6 items-center justify-center rounded-full',
isPreviousStreakDay && 'bg-red-500',
isCurrentStreakDay && 'bg-purple-500',
isRemainingStreakDay && 'bg-slate-700',
isToday && 'border-2 border-dashed border-slate-500 bg-transparent',
)}
>
{isToday ? null : icon}
</div>
<span
className={cn(
'text-sm',
isPreviousStreakDay && 'text-slate-400',
(isCurrentStreakDay || isRemainingStreakDay) && 'text-slate-100',
)}
>
{dayCount}
</span>
{isToday && (
<ChevronDown className="absolute bottom-full left-1/2 h-4 w-4 -translate-x-1/2 transform stroke-[2.5px] text-slate-400" />
)}
</div>
);
}
Loading…
Cancel
Save