feat: implement basic analytics ui

This commit is contained in:
Eunwoo Choi 2023-07-28 01:50:03 +09:00
parent 95879bb7b4
commit 0fc2cf0c5b
No known key found for this signature in database
GPG Key ID: ADB4D90A2C8C2E13
5 changed files with 151 additions and 1 deletions

View File

@ -0,0 +1,23 @@
import DatePicker, { registerLocale } from 'react-datepicker'
import { ko } from 'date-fns/locale'
import 'react-datepicker/dist/react-datepicker.css'
registerLocale('ko', ko)
const DateSelect: React.FC<DateSelectProps> = ({ ...props }) => {
return <DatePicker
locale='ko'
className='border-grey-light relative px-3 w-full py-1.5 text-black dark:text-white dark:bg-very-black border dark:border-transparent rounded outline-none'
dateFormat='yyyy.MM.dd'
{...props}
/>
}
interface DateSelectProps {
[key: string]: unknown
}
export default DateSelect

View File

@ -0,0 +1,30 @@
const StatisticsCard: React.FC<StatisticsCardProps> = ({name, currentValue, diff, icon, range}) => {
return <div className='text-black dark:text-white dark:bg-discord-black bg-little-white rounded-2xl shadow-xl p-5 hover:-translate-y-1 transition duration-100 ease-in cursor-pointer'>
<p className='text-xl text-gray-400 font-semibold'>
{name}
</p>
<div className='flex flex-row'>
{icon}
<p className='text-2xl text-center font-bold'>
{currentValue}
</p>
<div className='w-1.5' />
<p className={'text-base self-end text-semibold ' + (diff > 0 ? 'text-green-600' : diff == 0 ? 'text-gray-500' : 'text-red-600')}>
{diff > 0 ? '+' : diff == 0 ? '-' : ''}{diff}
</p>
</div>
<p className='text-lg text-gray-500 font-light'>
{range === 'day' ? '어제 대비' : range === 'week' ? '저번주 대비' : range === 'month' ? '저번 달 대비' : range}
</p>
</div>
}
interface StatisticsCardProps {
name: string
currentValue: number | string
diff: number
icon: JSX.Element
range: 'day' | 'week' | 'month' | string
}
export default StatisticsCard

View File

@ -24,6 +24,7 @@
"cookie": "0.5.0",
"csrf": "3.1.0",
"dataloader": "2.2.2",
"date-fns": "^2.30.0",
"dayjs": "^1.10.6",
"dd-trace": "^4.3.0",
"difflib": "0.2.4",
@ -49,6 +50,7 @@
"rc-tooltip": "5.1.1",
"react": "17.0.2",
"react-adsense": "0.1.0",
"react-datepicker": "^4.16.0",
"react-dom": "17.0.2",
"react-ga": "^3.3.1",
"react-hotkeys": "2.0.0",

View File

@ -26,15 +26,35 @@ const DeveloperLayout = dynamic(() => import('@components/DeveloperLayout'))
const DiscordAvatar = dynamic(() => import('@components/DiscordAvatar'))
const Message = dynamic(() => import('@components/Message'))
const Modal = dynamic(() => import('@components/Modal'))
const StatisticsCard = dynamic(() => import('@components/StatisticsCard'))
const Select = dynamic(() => import('@components/Form/Select'))
const DateSelect = dynamic(() => import('@components/Form/DateSelect'))
const BotApplication: NextPage<BotApplicationProps> = ({ user, spec, bot, theme, csrfToken }) => {
const router = useRouter()
const [ data, setData ] = useState<ResponseProps<unknown>>(null)
const [ modalOpened, setModalOpen ] = useState(false)
const [ showToken, setShowToken ] = useState(false)
const [ range, setRange ] = useState('day')
const [ startDate, setStartDate ] = useState(new Date())
const [ endDate, setEndDate ] = useState(new Date())
const [ tokenCopied, setTokenCopied ] = useClipboard(spec?.token, {
successDuration: 1000
})
const availableRanges = [
{
label: '일간',
value: 'day'
},
{
label: '주간',
value: 'week'
},
{
label: '월간',
value: 'month'
},
]
async function updateApplication(d: DeveloperBot) {
const res = await Fetch(`/applications/bots/${bot.id}`, {
method: 'PATCH',
@ -140,6 +160,25 @@ const BotApplication: NextPage<BotApplicationProps> = ({ user, spec, bot, theme,
</div>
<Divider />
<h1 className='text-3xl font-bold pt-6'> </h1>
<div className='flex flex-row pt-2 items-center'>
<div className='w-20 mr-2'>
<Select options={availableRanges} handleChange={(option) => setRange(option.value)} value={availableRanges[0]} handleTouch={() => null} />
</div>
<DateSelect selected={startDate} onChange={(date) => setStartDate(date)} startDate={startDate} endDate={endDate} />
<p className='px-2 text-gray-400 text-xl font-semibold'>
</p>
<DateSelect selected={endDate} onChange={(date) => setEndDate(date)} startDate={startDate} endDate={endDate} minDate={startDate} />
<p className='px-2 text-gray-400 text-xl font-semibold'>
</p>
</div>
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 pt-3 gap-5'>
<StatisticsCard name='하트 수' currentValue={50} diff={10} icon={<i className='fas fa-heart text-red-600 self-center text-xl mr-1' />} range={range} />
<StatisticsCard name='서버 수' currentValue={1000} diff={-10} icon={<i className='fas fa-users text-discord-blurple self-center text-xl mr-2' />} range={range} />
<StatisticsCard name='조회수' currentValue={102} diff={0} icon={<i className='fas fa-mouse-pointer text-neutral-200 self-center text-lg -mr-1' />} range={range} />
<StatisticsCard name='초대 클릭 수' currentValue={5} diff={3} icon={<i className='fas fa-user-plus text-discord-old-blurple self-center text-xl mr-2.5' />} range={range} />
</div>
</DeveloperLayout>
}

View File

@ -1705,6 +1705,13 @@
dependencies:
regenerator-runtime "^0.13.11"
"@babel/runtime@^7.21.0":
version "7.22.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
dependencies:
regenerator-runtime "^0.13.11"
"@babel/template@^7.22.5", "@babel/template@^7.3.3":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@ -2427,6 +2434,11 @@
picocolors "^1.0.0"
tslib "^2.5.0"
"@popperjs/core@^2.11.8":
version "2.11.8"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
@ -4206,6 +4218,13 @@ dataloader@2.2.2:
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0"
integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==
date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
dependencies:
"@babel/runtime" "^7.21.0"
dayjs@^1.10.6:
version "1.11.8"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea"
@ -8159,7 +8178,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.8.1:
prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -8284,6 +8303,18 @@ react-adsense@0.1.0:
resolved "https://registry.yarnpkg.com/react-adsense/-/react-adsense-0.1.0.tgz#f170007a9ab05ee2cd1c915cdc9788624658e3c1"
integrity sha512-VzGMH9qA7bBKAveK1joFWphhB7e2Y1ToSGzUa8b7eit3+VSkg/2MSvKxe5d/r4mcmkOHu6CHNJW0nn6RQC3IZA==
react-datepicker@^4.16.0:
version "4.16.0"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-4.16.0.tgz#b9dd389bb5611a1acc514bba1dd944be21dd877f"
integrity sha512-hNQ0PAg/LQoVbDUO/RWAdm/RYmPhN3cz7LuQ3hqbs24OSp69QCiKOJRrQ4jk1gv1jNR5oYu8SjjgfDh8q6Q1yw==
dependencies:
"@popperjs/core" "^2.11.8"
classnames "^2.2.6"
date-fns "^2.30.0"
prop-types "^15.7.2"
react-onclickoutside "^6.12.2"
react-popper "^2.3.0"
react-dom@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
@ -8298,6 +8329,11 @@ react-fast-compare@^2.0.1:
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9"
integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==
react-fast-compare@^3.0.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49"
integrity sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==
react-ga@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-3.3.1.tgz#d8e1f4e05ec55ed6ff944dcb14b99011dfaf9504"
@ -8327,6 +8363,19 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-onclickoutside@^6.12.2:
version "6.13.0"
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz#e165ea4e5157f3da94f4376a3ab3e22a565f4ffc"
integrity sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==
react-popper@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"
integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==
dependencies:
react-fast-compare "^3.0.1"
warning "^4.0.2"
react-responsive-modal@6.4.2:
version "6.4.2"
resolved "https://registry.yarnpkg.com/react-responsive-modal/-/react-responsive-modal-6.4.2.tgz#666b5c35b232cec617981006c9fe59414531a5a0"
@ -9769,6 +9818,13 @@ walker@^1.0.8:
dependencies:
makeerror "1.0.12"
warning@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
dependencies:
loose-envify "^1.0.0"
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"