-
Notifications
You must be signed in to change notification settings - Fork 2
add basic stats for us to keep tabs on whats up #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { Router, Status } from 'oak' | ||
| import { defineRoute } from './_common.ts' | ||
| import {withDBConnection, festivalStats, accountReferralStatus} from '../../utils/db.ts' | ||
|
|
||
| export default function register(router: Router) { | ||
| defineRoute(router, { | ||
| endpoint: '/stats', | ||
| method: 'get', | ||
| requireAuth: true, | ||
| handler: async ({ jwt: { account_id }}) => { | ||
| // only these accounts are allowed to see stats | ||
| // if ([ | ||
| // 'allowed-account-id-1', | ||
| // ].indexOf(account_id) === -1) { | ||
| // return [null, Status.Forbidden] | ||
| // } | ||
|
|
||
| const stats = await withDBConnection(db => { | ||
| return festivalStats(db) | ||
| }) | ||
|
|
||
| return [stats, Status.OK] | ||
| } | ||
| }) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /* eslint-disable indent */ | ||
| import React, { } from 'react' | ||
| import { observer } from 'mobx-react-lite' | ||
| import { useRequest } from '../mobx/hooks' | ||
| import Col from './core/Col' | ||
| import Store from "../Store.ts"; | ||
| import {vibefetch} from "../vibefetch.ts"; | ||
| import LoadingDots from "./core/LoadingDots.tsx"; | ||
| import { | ||
| PURCHASE_TYPES_BY_TYPE, | ||
| PurchaseCountMap, | ||
| PurchaseType | ||
| } from "../../../back-end/types/misc.ts"; | ||
| import {objectKeys} from "../../../back-end/utils/misc.ts"; | ||
|
|
||
| export default observer(() => { | ||
| const stats = useRequest(async () => { | ||
| if (!Store.loggedIn) { | ||
| return undefined | ||
| } | ||
|
|
||
| const { response } = await vibefetch( | ||
| Store.jwt, | ||
| '/stats', | ||
| 'get', | ||
| undefined | ||
| ) | ||
|
|
||
| return response | ||
| }) | ||
|
|
||
| const purchases = stats.state.result?.purchases ?? ({} as PurchaseCountMap) | ||
| const purchaseTable = Object.keys(purchases).length > 0 ? ( | ||
| <table> | ||
| {objectKeys<PurchaseCountMap>(purchases).map((key) => { | ||
| return ( | ||
| <tr key={key}> | ||
| <th style={{textAlign: 'left', fontWeight: "300"}}>{PURCHASE_TYPES_BY_TYPE[key].description}</th> | ||
| <td style={{fontWeight: "400"}}>{purchases[key]}</td> | ||
| </tr> | ||
| ); | ||
| })} | ||
| </table> | ||
| ) : <div></div> | ||
|
|
||
| return ( | ||
| <Col padding={20} pageLevel> | ||
| <h1 style={{ fontSize: 24, alignSelf: 'flex-start' }}> | ||
| Stats | ||
| </h1> | ||
|
|
||
| {stats.state.kind === 'loading' ? <LoadingDots size={80} color={"blue"}/> : ( | ||
| <> | ||
| <div style={{marginTop: '20px'}}> | ||
| Accounts Created: <span style={{fontWeight: "400"}}>{stats.state.result?.accounts}</span> | ||
| </div> | ||
| <div style={{marginTop: '20px'}}> | ||
| <h3>Purchases</h3> | ||
| {purchaseTable} | ||
| </div> | ||
| </> | ||
| )} | ||
| </Col> | ||
| ) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { objectEntries } from '../../back-end/utils/misc' | ||
| import Account from './components/Account' | ||
| import Tickets from './components/Tickets' | ||
| import Stats from './components/Stats' | ||
|
|
||
| export const VIEWS = { | ||
| Tickets: { | ||
|
|
@@ -22,14 +23,18 @@ export const VIEWS = { | |
| Account: { | ||
| icon: 'person', | ||
| component: Account | ||
| } | ||
| }, | ||
| Stats: { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll need to give some thought to how to prevent this from showing up for regular users. I'm thinking for now maybe But there is a bigger discussion to be had around, if budibase won't serve all of our admin needs, should we a) look for an alternate service or b) modify our core systems and schemas to properly handle different access levels for admin users?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea this is my core question. there's a also a third option to use something that's meant for dashboards/reports such as https://github.com/metabase/metabase (why is everything called ____base???)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. partially i did this to get into the codebase a bit. ty for the comments, will fix what you pointed out |
||
| icon: 'info', | ||
| component: Stats | ||
| }, | ||
| } as const | ||
|
|
||
| export const VIEWS_ARRAY = objectEntries(VIEWS) | ||
| .map(([name, {icon, component}]) => ({ name, icon, component } as const)) | ||
| .map(([name, { icon, component }]) => ({ name, icon, component } as const)) | ||
|
|
||
| export function isViewName(str: string): str is ViewName { | ||
| return Object.keys(VIEWS).includes(str) | ||
| } | ||
|
|
||
| export type ViewName = keyof typeof VIEWS | ||
| export type ViewName = keyof typeof VIEWS | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like to avoid using numbers in boolean contexts (where 0 is falsy and >0 is truthy), because it can be a foot-gun
In this case I'd just do
accounts: Number(accountRes[0].accounts ?? 0)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i agree with you on not doing truey integers, but i don't think im doing that here. if
!accountRes.length, then there are 0 accountsalso im not sure i understand the difference in your two examples