From 4e66790acecab9a9d3a6a6909045a4a74556b528 Mon Sep 17 00:00:00 2001 From: Sawa <34698182+sawaYch@users.noreply.github.com> Date: Sat, 2 Mar 2024 07:03:56 +0800 Subject: [PATCH] refactor(ui): poll cardgroup --- components/new-poll-confirm-dialog.tsx | 50 +++++ components/poll-app.tsx | 4 +- .../{poll-section.tsx => poll-cardgroup.tsx} | 188 ++++-------------- components/poll-summary-subcard.tsx | 31 +++ hooks/use-chart-config.ts | 80 ++++++++ 5 files changed, 200 insertions(+), 153 deletions(-) create mode 100644 components/new-poll-confirm-dialog.tsx rename components/{poll-section.tsx => poll-cardgroup.tsx} (71%) create mode 100644 components/poll-summary-subcard.tsx create mode 100644 hooks/use-chart-config.ts diff --git a/components/new-poll-confirm-dialog.tsx b/components/new-poll-confirm-dialog.tsx new file mode 100644 index 0000000..57db88f --- /dev/null +++ b/components/new-poll-confirm-dialog.tsx @@ -0,0 +1,50 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { ArrowBigRightDashIcon } from 'lucide-react'; + +interface NewPollConfirmDialogProps { + handleProceedNewPoll: () => void; +} + +const NewPollConfirmDialog = ({ + handleProceedNewPoll, +}: NewPollConfirmDialogProps) => { + return ( + + + + + + + Confirmation + + Creating next new poll will discard current poll records. This + webapp will not keep the poll records and this action cannot be + undone. + + + + Cancel + + Continue + + + + + ); +}; + +export default NewPollConfirmDialog; diff --git a/components/poll-app.tsx b/components/poll-app.tsx index 609f379..f2362bc 100644 --- a/components/poll-app.tsx +++ b/components/poll-app.tsx @@ -3,7 +3,7 @@ import { useCallback, useRef, useState } from 'react'; import { useToast } from '@/components/ui/use-toast'; import { AuthForm } from './auth-form'; -import PollSection from './poll-section'; +import PollCardGroup from './poll-cardgroup'; const PollApp = () => { const [isAuth, setIsAuth] = useState(false); @@ -32,7 +32,7 @@ const PollApp = () => { {!isAuth ? ( ) : ( - + )} ); diff --git a/components/poll-section.tsx b/components/poll-cardgroup.tsx similarity index 71% rename from components/poll-section.tsx rename to components/poll-cardgroup.tsx index bb813e7..d5cfafe 100644 --- a/components/poll-section.tsx +++ b/components/poll-cardgroup.tsx @@ -1,25 +1,13 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import UrlInput from './url-input'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { vidParser } from '@/lib/vid-parser'; import { LiveMetadata, MessageData, PollUserData } from '@/types/liveChat'; import { useLiveChat } from '@/hooks/use-livechat'; -import { useToast } from './ui/use-toast'; -import { Label } from './ui/label'; +import { useToast } from '@/components/ui/use-toast'; +import { Label } from '@/components/ui/label'; import { Card, CardContent, CardHeader, CardTitle } from './ui/card'; -import { Input } from './ui/input'; -import { Button } from './ui/button'; -import { ArrowBigRightDashIcon, PlayIcon, StopCircleIcon } from 'lucide-react'; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, - AlertDialogTrigger, -} from '@/components/ui/alert-dialog'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { PlayIcon, StopCircleIcon } from 'lucide-react'; import { Bar } from 'react-chartjs-2'; import { Chart as ChartJS, @@ -30,14 +18,17 @@ import { Tooltip, Legend, } from 'chart.js'; -import { randomRGBAColor } from '@/lib/random-rgba-color'; -import LiveStreamMetadataCard from './livestream-metadata-card'; -import Spinner from './spinner'; +import LiveStreamMetadataCard from '@/components/livestream-metadata-card'; +import Spinner from '@/components/spinner'; import { defaultBaseInterval, isNumeric } from '@/lib/utils'; import dayjs from 'dayjs'; -import { Checkbox } from './ui/checkbox'; +import { Checkbox } from '@/components/ui/checkbox'; +import { useChartConfig } from '@/hooks/use-chart-config'; +import NewPollConfirmDialog from '@/components/new-poll-confirm-dialog'; +import PollSummarySubCard from '@/components/poll-summary-subcard'; +import UrlInput from '@/components/url-input'; -interface UrlInputSectionProps { +interface PollCardGroupProps { currentPassphrase: string; } @@ -52,7 +43,7 @@ ChartJS.register( Legend ); -const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { +const PollCardGroup = ({ currentPassphrase }: PollCardGroupProps) => { const { toast } = useToast(); const [isLoading, setIsLoading] = useState(false); const [isReady, setIsReady] = useState(false); @@ -83,7 +74,6 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { const [pollData, setPollData] = useState({}); const [pollStartDate, setPollStartDate] = useState(); const [pollSummary, setPollSummary] = useState([]); - const [pollSummaryTop, setPollSummaryTop] = useState(0); const [allowUpdatePollOptions, setAllowUpdatePollOptions] = useState(true); const { fetchLiveChatMessage, fetchLiveStreamingDetails, extractMessage } = @@ -230,7 +220,6 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { const sortChartResult = useCallback(() => { const data = new Array(numOfOptions).fill(0); - // MOCKData: {'Sawa': 5, 'userA': 5, 'Sam': 1, 'userB': 5, 'userC': 2} Object.values(pollData).forEach((v) => { if (v > 0 && v <= numOfOptions) { data[v - 1]++; @@ -249,9 +238,7 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { }); const pollSummary = arrayOfObj.map((it) => it.data); - const topIndex = pollSummary.indexOf(Math.max(...pollSummary)); setPollSummary(pollSummary); - setPollSummaryTop(topIndex); const sortedArrayOfObj = arrayOfObj.sort((a, b) => a.data === b.data ? 0 : a.data > b.data ? -1 : 1 ); @@ -268,7 +255,7 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { newArrayLabel.push(`${d.label}`); } newArrayData.push(d.data); - newBarColor.push(d.backgroundColor); // seems buggy here + newBarColor.push(d.backgroundColor); newBorderColor.push(d.borderColor); }); @@ -281,76 +268,22 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { } }, [barColor?.bar, barColor?.border, numOfOptions, pollData]); - const chartInitData = useMemo(() => { - const labels = Array.from(Array(numOfOptions).keys()).map( - (i) => `${i + 1}` - ); - const color = randomRGBAColor(numOfOptions); - setBarColor(color); - - return { - labels, - datasets: [ - { - label: 'Poll', - data: [], - backgroundColor: color.bar, - borderColor: color.border, - maxBarThickness: 24, - }, - ], - }; - }, [numOfOptions]); - - const chartOptions = useMemo(() => { - return { - indexAxis: 'y' as const, - elements: { - bar: { - borderWidth: 2, - }, - }, - responsive: true, - plugins: { - legend: { - display: false, - }, - title: { - display: false, - }, - }, - scales: { - y: { - title: { - display: true, - text: 'Option๐ŸŽซ', - color: '#dddddd', - }, - ticks: { - color: '#dddddd', - }, - grid: { - color: '#81112a', - lineWidth: 0.5, - }, - }, - x: { - title: { - display: true, - color: '#dddddd', - text: 'Count', - }, - ticks: { - color: '#dddddd', - stepSize: 1, - }, - grid: { - color: '#cd1b42', - lineWidth: 1, - }, - }, - }, - }; + const { chartOptions, chartInitData, chartColor } = + useChartConfig(numOfOptions); + + useEffect(() => { + // memories chartColor scheme + setBarColor(chartColor); + }, [chartColor]); + + const handleProceedNewPoll = useCallback(() => { + setPollStatus('prepare'); + setPollData({}); + setNumOfOptions(0); + setPollSummary([]); + if (inputRef.current) { + inputRef.current.value = ''; + } }, []); return ( @@ -471,23 +404,8 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { redraw /> {pollStatus === 'stop' && ( -
- - ๐Ÿ“Š Poll Summary - -
- {pollSummary.map((value, index) => { - return ( -
- {index + 1}: {value ?? 0}{' '} - {pollSummaryTop === index && '๐Ÿ‘‘'} -
- ); - })} -
-
+ )} - {pollStatus === 'start' && ( )} {pollStatus === 'stop' && ( - - - - - - - Confirmation - - Creating next new poll will discard current poll - records. This webapp will not keep the poll records - and this action cannot be undone. - - - - Cancel - { - setPollStatus('prepare'); - setPollData({}); - setNumOfOptions(0); - setPollSummary([]); - setPollSummaryTop(0); - if (inputRef.current) { - inputRef.current.value = ''; - } - }} - > - Continue - - - - + )} @@ -547,4 +433,4 @@ const PollSection = ({ currentPassphrase }: UrlInputSectionProps) => { ); }; -export default PollSection; +export default PollCardGroup; diff --git a/components/poll-summary-subcard.tsx b/components/poll-summary-subcard.tsx new file mode 100644 index 0000000..2b34675 --- /dev/null +++ b/components/poll-summary-subcard.tsx @@ -0,0 +1,31 @@ +import { CardTitle } from '@/components/ui/card'; +import { useMemo, useState } from 'react'; + +interface PollSummarySubCardProps { + pollSummary: number[]; +} + +const PollSummarySubCard = ({ pollSummary }: PollSummarySubCardProps) => { + const pollSummaryTop = useMemo(() => { + return pollSummary.indexOf(Math.max(...pollSummary)); + }, [pollSummary]); + + return ( +
+ + ๐Ÿ“Š Poll Summary + +
+ {pollSummary.map((value, index) => { + return ( +
+ {index + 1}: {value ?? 0} {pollSummaryTop === index && '๐Ÿ‘‘'} +
+ ); + })} +
+
+ ); +}; + +export default PollSummarySubCard; diff --git a/hooks/use-chart-config.ts b/hooks/use-chart-config.ts new file mode 100644 index 0000000..8b0c712 --- /dev/null +++ b/hooks/use-chart-config.ts @@ -0,0 +1,80 @@ +import { randomRGBAColor } from '@/lib/random-rgba-color'; +import { useMemo } from 'react'; + +export const useChartConfig = (numOfOptions: number) => { + const chartOptions = useMemo(() => { + return { + indexAxis: 'y' as const, + elements: { + bar: { + borderWidth: 2, + }, + }, + responsive: true, + plugins: { + legend: { + display: false, + }, + title: { + display: false, + }, + }, + scales: { + y: { + title: { + display: true, + text: 'Option๐ŸŽซ', + color: '#dddddd', + }, + ticks: { + color: '#dddddd', + }, + grid: { + color: '#81112a', + lineWidth: 0.5, + }, + }, + x: { + title: { + display: true, + color: '#dddddd', + text: 'Count', + }, + ticks: { + color: '#dddddd', + stepSize: 1, + }, + grid: { + color: '#cd1b42', + lineWidth: 1, + }, + }, + }, + }; + }, []); + + const chartColor = useMemo(() => { + return randomRGBAColor(numOfOptions); + }, [numOfOptions]); + + const chartInitData = useMemo(() => { + const labels = Array.from(Array(numOfOptions).keys()).map( + (i) => `${i + 1}` + ); + + return { + labels, + datasets: [ + { + label: 'Poll', + data: [], + backgroundColor: chartColor.bar, + borderColor: chartColor.border, + maxBarThickness: 24, + }, + ], + }; + }, [chartColor.bar, chartColor.border, numOfOptions]); + + return { chartOptions, chartInitData, chartColor }; +};